summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/tcpip
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/tdi/tcpip')
-rw-r--r--private/ntos/tdi/tcpip/dirs28
-rw-r--r--private/ntos/tdi/tcpip/h/oscfg.h72
-rw-r--r--private/ntos/tdi/tcpip/h/packoff.h37
-rw-r--r--private/ntos/tdi/tcpip/h/packon.h35
-rw-r--r--private/ntos/tdi/tcpip/h/queue.h87
-rw-r--r--private/ntos/tdi/tcpip/h/tdint.h41
-rw-r--r--private/ntos/tdi/tcpip/ip/arp.c4839
-rw-r--r--private/ntos/tdi/tcpip/ip/arp.h21
-rw-r--r--private/ntos/tdi/tcpip/ip/arpdef.h340
-rw-r--r--private/ntos/tdi/tcpip/ip/dirs22
-rw-r--r--private/ntos/tdi/tcpip/ip/icmp.c1698
-rw-r--r--private/ntos/tdi/tcpip/ip/icmp.h70
-rw-r--r--private/ntos/tdi/tcpip/ip/igmp.c799
-rw-r--r--private/ntos/tdi/tcpip/ip/igmp.h42
-rw-r--r--private/ntos/tdi/tcpip/ip/info.c609
-rw-r--r--private/ntos/tdi/tcpip/ip/info.h22
-rw-r--r--private/ntos/tdi/tcpip/ip/init.c3509
-rw-r--r--private/ntos/tdi/tcpip/ip/ipdef.h371
-rw-r--r--private/ntos/tdi/tcpip/ip/ipinit.h155
-rw-r--r--private/ntos/tdi/tcpip/ip/iploop.c665
-rw-r--r--private/ntos/tdi/tcpip/ip/iprcv.c1145
-rw-r--r--private/ntos/tdi/tcpip/ip/iproute.c4703
-rw-r--r--private/ntos/tdi/tcpip/ip/iproute.h107
-rw-r--r--private/ntos/tdi/tcpip/ip/iprtdef.h135
-rw-r--r--private/ntos/tdi/tcpip/ip/ipstatus.c205
-rw-r--r--private/ntos/tdi/tcpip/ip/ipxmit.c1949
-rw-r--r--private/ntos/tdi/tcpip/ip/ipxmit.h34
-rw-r--r--private/ntos/tdi/tcpip/ip/mp/makefile6
-rw-r--r--private/ntos/tdi/tcpip/ip/mp/sources27
-rw-r--r--private/ntos/tdi/tcpip/ip/ntip.c3361
-rw-r--r--private/ntos/tdi/tcpip/ip/ntirp.c1458
-rw-r--r--private/ntos/tdi/tcpip/ip/ntreg.c672
-rw-r--r--private/ntos/tdi/tcpip/ip/sources.inc59
-rw-r--r--private/ntos/tdi/tcpip/ip/up/makefile6
-rw-r--r--private/ntos/tdi/tcpip/ip/up/sources27
-rw-r--r--private/ntos/tdi/tcpip/tcp/addr.c2061
-rw-r--r--private/ntos/tdi/tcpip/tcp/addr.h211
-rw-r--r--private/ntos/tdi/tcpip/tcp/alpha/sources3
-rw-r--r--private/ntos/tdi/tcpip/tcp/alpha/xsum.s271
-rw-r--r--private/ntos/tdi/tcpip/tcp/dgram.c990
-rw-r--r--private/ntos/tdi/tcpip/tcp/dgram.h89
-rw-r--r--private/ntos/tdi/tcpip/tcp/dirs22
-rw-r--r--private/ntos/tdi/tcpip/tcp/i386/sources4
-rw-r--r--private/ntos/tdi/tcpip/tcp/i386/xsum.asm259
-rw-r--r--private/ntos/tdi/tcpip/tcp/info.c917
-rw-r--r--private/ntos/tdi/tcpip/tcp/info.h51
-rw-r--r--private/ntos/tdi/tcpip/tcp/init.c597
-rw-r--r--private/ntos/tdi/tcpip/tcp/mips/sources4
-rw-r--r--private/ntos/tdi/tcpip/tcp/mips/xsum.s243
-rw-r--r--private/ntos/tdi/tcpip/tcp/mp/makefile6
-rw-r--r--private/ntos/tdi/tcpip/tcp/mp/sources30
-rw-r--r--private/ntos/tdi/tcpip/tcp/mp/tcpip.prf297
-rw-r--r--private/ntos/tdi/tcpip/tcp/ntautodl.c258
-rw-r--r--private/ntos/tdi/tcpip/tcp/ntdisp.c4063
-rw-r--r--private/ntos/tdi/tcpip/tcp/ntinit.c1038
-rw-r--r--private/ntos/tdi/tcpip/tcp/ppc/sources4
-rw-r--r--private/ntos/tdi/tcpip/tcp/ppc/xsum.s257
-rw-r--r--private/ntos/tdi/tcpip/tcp/raw.c693
-rw-r--r--private/ntos/tdi/tcpip/tcp/raw.h34
-rw-r--r--private/ntos/tdi/tcpip/tcp/secfltr.c1425
-rw-r--r--private/ntos/tdi/tcpip/tcp/secfltr.h61
-rw-r--r--private/ntos/tdi/tcpip/tcp/sources.inc67
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcb.c1524
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcb.h67
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcp.h426
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcpcfg.h86
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcpconn.c2344
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcpconn.h124
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcpdeb.c169
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcpdeb.h78
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcpdeliv.c1971
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcpdeliv.h44
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcpip.def8
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcpip.rc12
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcprcv.c3397
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcprcv.h74
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcpsend.c2666
-rw-r--r--private/ntos/tdi/tcpip/tcp/tcpsend.h105
-rw-r--r--private/ntos/tdi/tcpip/tcp/tlcommon.c553
-rw-r--r--private/ntos/tdi/tcpip/tcp/tlcommon.h46
-rw-r--r--private/ntos/tdi/tcpip/tcp/udp.c673
-rw-r--r--private/ntos/tdi/tcpip/tcp/udp.h39
-rw-r--r--private/ntos/tdi/tcpip/tcp/up/makefile6
-rw-r--r--private/ntos/tdi/tcpip/tcp/up/sources30
-rw-r--r--private/ntos/tdi/tcpip/tcp/up/tcpip.prf297
85 files changed, 56050 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/dirs b/private/ntos/tdi/tcpip/dirs
new file mode 100644
index 000000000..1ada6a1e5
--- /dev/null
+++ b/private/ntos/tdi/tcpip/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= \
+ ip \
+ tcp
+
+OPTIONAL_DIRS=
+
diff --git a/private/ntos/tdi/tcpip/h/oscfg.h b/private/ntos/tdi/tcpip/h/oscfg.h
new file mode 100644
index 000000000..5544d5b40
--- /dev/null
+++ b/private/ntos/tdi/tcpip/h/oscfg.h
@@ -0,0 +1,72 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+#ifndef OSCFG_INCLUDED
+#define OSCFG_INCLUDED
+
+
+#define net_short(x) ((((x)&0xff) << 8) | (((x)&0xff00) >> 8))
+
+//#define net_long(x) (((net_short((x)&0xffff)) << 16) | net_short((((x)&0xffff0000L)>>16)))
+#define net_long(x) (((((ulong)(x))&0xffL)<<24) | \
+ ((((ulong)(x))&0xff00L)<<8) | \
+ ((((ulong)(x))&0xff0000L)>>8) | \
+ ((((ulong)(x))&0xff000000L)>>24))
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+
+#ifdef VXD
+/////////////////////////////////////////////////////////////////////////////
+//
+// VXD definitions
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include <stddef.h>
+
+#pragma code_seg("_LTEXT", "LCODE")
+#pragma data_seg("_LDATA", "LCODE")
+
+//* pragma bodies for bracketing of initialization code.
+
+#define BEGIN_INIT code_seg("_ITEXT", "ICODE")
+#define BEGIN_INIT_DATA data_seg("_IDATA", "ICODE")
+#define END_INIT code_seg()
+#define END_INIT_DATA data_seg()
+
+#else // VXD
+#ifdef NT
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// NT definitions
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include <ntos.h>
+#include <zwapi.h>
+
+#define BEGIN_INIT
+#define END_INIT
+
+#else // NT
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Definitions for additional environments go here
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#error Environment specific definitions missing
+
+#endif // NT
+
+#endif // VXD
+
+
+#endif // OSCFG_INCLUDED
diff --git a/private/ntos/tdi/tcpip/h/packoff.h b/private/ntos/tdi/tcpip/h/packoff.h
new file mode 100644
index 000000000..c47cd038f
--- /dev/null
+++ b/private/ntos/tdi/tcpip/h/packoff.h
@@ -0,0 +1,37 @@
+/*++
+
+Copyright (c) 1990,91 Microsoft Corporation
+
+Module Name:
+
+ packoff.h
+
+Abstract:
+
+ This file turns packing of structures off. (That is, it enables
+ automatic alignment of structure fields.) An include file is needed
+ because various compilers do this in different ways.
+
+ packoff.h is the complement to packon.h. An inclusion of packoff.h
+ MUST ALWAYS be preceded by an inclusion of packon.h, in one-to-one
+ correspondence.
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 4-Mar-1990
+
+Revision History:
+
+ 15-Apr-1991 JohnRo
+ Created lint-able variant.
+--*/
+
+#if ! (defined(lint) || defined(_lint))
+
+#ifndef VXD
+#if i386
+#pragma warning(disable:4103)
+#endif
+#endif
+#pragma pack() // x86, MS compiler; MIPS, MIPS compiler
+#endif // ! (defined(lint) || defined(_lint))
diff --git a/private/ntos/tdi/tcpip/h/packon.h b/private/ntos/tdi/tcpip/h/packon.h
new file mode 100644
index 000000000..f2c73cb85
--- /dev/null
+++ b/private/ntos/tdi/tcpip/h/packon.h
@@ -0,0 +1,35 @@
+/*++
+
+Copyright (c) 1990,91 Microsoft Corporation
+
+Module Name:
+
+ packon.h
+
+Abstract:
+
+ This file turns packing of structures on. (That is, it disables
+ automatic alignment of structure fields.) An include file is needed
+ because various compilers do this in different ways.
+
+ The file packoff.h is the complement to this file.
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 4-Mar-1990
+
+Revision History:
+
+ 15-Apr-1991 JohnRo
+ Created lint-able variant.
+--*/
+
+#if ! (defined(lint) || defined(_lint))
+
+#ifndef VXD
+#if i386
+#pragma warning(disable:4103)
+#endif
+#endif
+#pragma pack(1) // x86, MS compiler; MIPS, MIPS compiler
+#endif // ! (defined(lint) || defined(_lint))
diff --git a/private/ntos/tdi/tcpip/h/queue.h b/private/ntos/tdi/tcpip/h/queue.h
new file mode 100644
index 000000000..f1e23d682
--- /dev/null
+++ b/private/ntos/tdi/tcpip/h/queue.h
@@ -0,0 +1,87 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** QUEUE.H - TCP/UDP queuing definitons.
+//
+// This file contains the definitions for the queue functions used
+// by the TCP/UDP code.
+//
+
+//* Definition of a queue linkage field.
+struct Queue {
+ struct Queue *q_next;
+ struct Queue *q_prev;
+}; /* Queue */
+
+typedef struct Queue Queue;
+
+//* Initialize queue macro.
+
+#define INITQ(q) { (q)->q_next = (q);\
+ (q)->q_prev = (q); }
+
+//* Macro to check for queue empty.
+#define EMPTYQ(q) ((q)->q_next == (q))
+
+//* Place an element onto the end of the queue.
+
+#define ENQUEUE(q, e) { (q)->q_prev->q_next = (e);\
+ (e)->q_prev = (q)->q_prev;\
+ (q)->q_prev = (e);\
+ (e)->q_next = (q); }
+
+//* Remove an element from the head of the queue. This macro assumes the queue
+// is not empty. The element is returned as type t, queued through linkage
+// l.
+
+#define DEQUEUE(q, ptr, t, l) {\
+ Queue *__tmp__;\
+ \
+ __tmp__ = (q)->q_next;\
+ (q)->q_next = __tmp__->q_next;\
+ __tmp__->q_next->q_prev = (q);\
+ (ptr) = STRUCT_OF(t, __tmp__, l);\
+ }
+
+//* Peek at an element at the head of the queue. We return a pointer to it
+// without removing anything.
+
+#define PEEKQ(q, ptr, t, l) {\
+ Queue *__tmp__;\
+ \
+ __tmp__ = (q)->q_next;\
+ (ptr) = STRUCT_OF(t, __tmp__, l);\
+ }
+
+//* Macro to push an element onto the head of a queue.
+
+#define PUSHQ(q, e) { (e)->q_next = (q)->q_next;\
+ (q)->q_next->q_prev = (e);\
+ (e)->q_prev = (q);\
+ (q)->q_next = e; }
+
+//* Macro to remove an element from the middle of a queue.
+#define REMOVEQ(q) { (q)->q_next->q_prev = (q)->q_prev;\
+ (q)->q_prev->q_next = (q)->q_next; }
+
+//** The following macros define methods for working with queue without
+// dequeueing, mostly dealing with Queue structures directly.
+
+//* Macro to define the end of a Q, used in walking a queue sequentially.
+#define QEND(q) (q)
+
+//* Macro to get the first on a queue.
+#define QHEAD(q) (q)->q_next
+
+//* Macro to get a structure, given a queue.
+
+#define QSTRUCT(t, q, l) STRUCT_OF(t, (q), l)
+
+//* Macro to get the next thing on q queue.
+
+#define QNEXT(q) (q)->q_next
+
+
diff --git a/private/ntos/tdi/tcpip/h/tdint.h b/private/ntos/tdi/tcpip/h/tdint.h
new file mode 100644
index 000000000..852f29c2a
--- /dev/null
+++ b/private/ntos/tdi/tcpip/h/tdint.h
@@ -0,0 +1,41 @@
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ tdint.h
+
+Abstract:
+
+ This file defines TDI types specific to the NT environment.
+
+Author:
+
+ Mike Massa (mikemas) August 13, 1993
+
+Revision History:
+
+--*/
+
+#ifndef _TDINT_
+#define _TDINT_
+
+#include <tdikrnl.h>
+
+typedef PTDI_IND_CONNECT PConnectEvent;
+typedef PTDI_IND_DISCONNECT PDisconnectEvent;
+typedef PTDI_IND_ERROR PErrorEvent;
+typedef PTDI_IND_RECEIVE PRcvEvent;
+typedef PTDI_IND_RECEIVE_DATAGRAM PRcvDGEvent;
+typedef PTDI_IND_RECEIVE_EXPEDITED PRcvExpEvent;
+
+typedef IRP EventRcvBuffer;
+typedef IRP ConnectEventInfo;
+
+//
+// BUGBUG: What about SEND_POSSIBLE????
+//
+
+#endif // ifndef _TDINT_
+
diff --git a/private/ntos/tdi/tcpip/ip/arp.c b/private/ntos/tdi/tcpip/ip/arp.c
new file mode 100644
index 000000000..3c8253566
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/arp.c
@@ -0,0 +1,4839 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** arp.c - ARP VxD routines.
+//
+// This file containes all of the ARP related routines, including
+// table lookup, registration, etc.
+//
+// ARP is architected to support multiple protocols, but for now
+// it in only implemented to take one protocol (IP). This is done
+// for simplicity and ease of implementation. In the future we may
+// split ARP out into a seperate driver.
+
+#include "oscfg.h"
+#ifdef VXD
+#include <string.h>
+#endif
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "llipif.h"
+#include "arp.h"
+#include "arpdef.h"
+#include "tdiinfo.h"
+#include "ipinfo.h"
+#include "llinfo.h"
+#include "tdistat.h"
+#include "iproute.h"
+#include "iprtdef.h"
+#include "arpinfo.h"
+#include "ipinit.h"
+
+#ifndef CHICAGO
+#ifndef _PNP_POWER
+#define NDIS_MAJOR_VERSION 0x03
+#define NDIS_MINOR_VERSION 0
+#else
+#define NDIS_MAJOR_VERSION 0x04
+#define NDIS_MINOR_VERSION 0
+#endif
+#endif
+
+#ifndef NDIS_API
+#define NDIS_API
+#endif
+
+
+static ulong ARPLookahead = LOOKAHEAD_SIZE;
+
+static uchar ENetBcst[] = "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x06";
+static uchar TRBcst[] = "\x10\x40\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x82\x70";
+static uchar FDDIBcst[] = "\x57\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00";
+static uchar ARCBcst[] = "\x00\x00\xd5";
+
+static uchar ENetMcst[] = "\x01\x00\x5E\x00\x00\x00";
+static uchar FDDIMcst[] = "\x57\x01\x00\x5E\x00\x00\x00";
+static uchar ARPSNAP[] = "\xAA\xAA\x03\x00\x00\x00\x08\x06";
+
+#ifdef NT
+static WCHAR ARPName[] = TCP_NAME;
+#else // NT
+static uchar ARPName[] = TCP_NAME;
+#endif // NT
+
+NDIS_HANDLE ARPHandle; // Our NDIS protocol handle.
+
+uint ArpCacheLife;
+uint sArpAlwaysSourceRoute; // True if we always send ARP requests
+ // with source route info on token ring.
+uint sIPAlwaysSourceRoute;
+extern uchar TrRii;
+
+extern PDRIVER_OBJECT IPDriverObject;
+
+extern void IPRcv(void *, void *, uint, uint, NDIS_HANDLE, uint, uint);
+extern void IPTDComplete(void *, PNDIS_PACKET, NDIS_STATUS, uint);
+extern void IPSendComplete(void *, PNDIS_PACKET, NDIS_STATUS);
+extern void IPStatus(void *, NDIS_STATUS, void *, uint);
+extern void IPRcvComplete(void);
+extern PNDIS_BUFFER CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size,
+ uint *StartOffset);
+
+extern void NDIS_API ARPSendComplete(NDIS_HANDLE, PNDIS_PACKET, NDIS_STATUS);
+extern void IPULUnloadNotify(void);
+
+#ifdef _PNP_POWER
+extern IP_STATUS IPAddInterface(PNDIS_STRING ConfigName, void *PNP,
+ void *Context, LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo);
+extern void IPDelInterface(void *Context);
+
+extern void NotifyOfUnload(void);
+
+
+extern uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE *Handle);
+extern int IsLLInterfaceValueNull (NDIS_HANDLE Handle) ;
+extern void CloseIFConfig(NDIS_HANDLE Handle);
+
+#endif
+
+
+// Tables for bitswapping.
+
+uchar SwapTableLo[] = {
+ 0, // 0
+ 0x08, // 1
+ 0x04, // 2
+ 0x0c, // 3
+ 0x02, // 4
+ 0x0a, // 5,
+ 0x06, // 6,
+ 0x0e, // 7,
+ 0x01, // 8,
+ 0x09, // 9,
+ 0x05, // 10,
+ 0x0d, // 11,
+ 0x03, // 12,
+ 0x0b, // 13,
+ 0x07, // 14,
+ 0x0f // 15
+};
+
+uchar SwapTableHi[] = {
+ 0, // 0
+ 0x80, // 1
+ 0x40, // 2
+ 0xc0, // 3
+ 0x20, // 4
+ 0xa0, // 5,
+ 0x60, // 6,
+ 0xe0, // 7,
+ 0x10, // 8,
+ 0x90, // 9,
+ 0x50, // 10,
+ 0xd0, // 11,
+ 0x30, // 12,
+ 0xb0, // 13,
+ 0x70, // 14,
+ 0xf0 // 15
+};
+
+// Table of source route maximum I-field lengths for token ring.
+ushort IFieldSize[] = {
+ 516,
+ 1500,
+ 2052,
+ 4472,
+ 8191
+};
+
+#define LF_BIT_SHIFT 4
+#define MAX_LF_BITS 4
+
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+//
+// Disposable init code.
+//
+void FreeARPInterface(ARPInterface *Interface);
+void ARPOpen(void *Context);
+
+#pragma alloc_text(INIT, ARPInit)
+#ifndef _PNP_POWER
+#pragma alloc_text(INIT, FreeARPInterface)
+#pragma alloc_text(INIT, ARPOpen)
+#pragma alloc_text(INIT, ARPRegister)
+#else
+#pragma alloc_text(PAGE, ARPOpen)
+#pragma alloc_text(PAGE, ARPRegister)
+
+#endif
+
+//
+// Paged code
+//
+void NotifyConflictProc(CTEEvent *Event, void *Context);
+
+#pragma alloc_text(PAGE, NotifyConflictProc)
+
+#endif // ALLOC_PRAGMA
+#endif // NT
+
+#ifdef VXD
+extern void EnableInts(void);
+#endif
+
+//* DoNDISRequest - Submit a request to an NDIS driver.
+//
+// This is a utility routine to submit a general request to an NDIS
+// driver. The caller specifes the request code (OID), a buffer and
+// a length. This routine allocates a request structure,
+// fills it in, and submits the request.
+//
+// Entry:
+// Adapter - A pointer to the ARPInterface adapter structure.
+// Request - Type of request to be done (Set or Query)
+// OID - Value to be set/queried.
+// Info - A pointer to the buffer to be passed.
+// Length - Length of data in the buffer.
+// Needed - On return, filled in with bytes needed in buffer.
+//
+// Exit:
+//
+NDIS_STATUS
+DoNDISRequest(ARPInterface *Adapter, NDIS_REQUEST_TYPE RT, NDIS_OID OID,
+ void *Info, uint Length, uint *Needed)
+{
+ NDIS_REQUEST Request; // Request structure we'll use.
+ NDIS_STATUS Status;
+
+ // Now fill it in.
+ Request.RequestType = RT;
+ if (RT == NdisRequestSetInformation) {
+ Request.DATA.SET_INFORMATION.Oid = OID;
+ Request.DATA.SET_INFORMATION.InformationBuffer = Info;
+ Request.DATA.SET_INFORMATION.InformationBufferLength = Length;
+ } else {
+ Request.DATA.QUERY_INFORMATION.Oid = OID;
+ Request.DATA.QUERY_INFORMATION.InformationBuffer = Info;
+ Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length;
+ }
+
+ // Initialize the block structure.
+ CTEInitBlockStruc(&Adapter->ai_block);
+#ifdef VXD
+ EnableInts();
+#endif
+
+ // Submit the request.
+ NdisRequest(&Status, Adapter->ai_handle, &Request);
+
+ // Wait for it to finish
+ if (Status == NDIS_STATUS_PENDING)
+ Status = (NDIS_STATUS)CTEBlock(&Adapter->ai_block);
+
+ if (Needed != NULL)
+ *Needed = Request.DATA.QUERY_INFORMATION.BytesNeeded;
+
+ return Status;
+}
+//* FreeARPBuffer - Free a header and buffer descriptor pair.
+//
+// Called when we're done with a buffer. We'll free the buffer and the
+// buffer descriptor pack to the interface.
+//
+// Entry: Interface - Interface buffer/bd came frome.
+// Buffer - NDIS_BUFFER to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeARPBuffer(ARPInterface *Interface, PNDIS_BUFFER Buffer)
+{
+ CTELockHandle lhandle;
+ uchar **Header; // header buffer to be freed.
+ uint Size;
+
+ Size = NdisBufferLength(Buffer);
+
+ if (Size <= Interface->ai_sbsize) {
+#ifdef VXD
+ // A small buffer, put him on the list.
+ NDIS_BUFFER_LINKAGE(Buffer) = Interface->ai_sblist;
+ Interface->ai_sblist = Buffer;
+#else
+ ExInterlockedPushEntrySList(
+ &Interface->ai_sblist,
+ STRUCT_OF(SINGLE_LIST_ENTRY, &(Buffer->Next), Next),
+ &Interface->ai_lock
+ );
+
+#endif
+
+ return;
+ } else {
+ // A big buffer. Get the buffer pointer, link it on, and free the
+ // NDIS buffer.
+ Header = (uchar **)NdisBufferVirtualAddress(Buffer);
+
+ CTEGetLock(&Interface->ai_lock, &lhandle);
+ *Header = Interface->ai_bblist;
+ Interface->ai_bblist = (uchar *)Header;
+ CTEFreeLock(&Interface->ai_lock, lhandle);
+
+ NdisFreeBuffer(Buffer);
+ }
+}
+
+//* GrowARPHeaders - Grow the ARP header buffer list.
+//
+// Called when we need to grow the ARP header buffer list. Called with the
+// interface lock held.
+//
+// Input: Interface - Interface on which to grow.
+//
+// Returns: Pointer to newly allocated buffer, or NULL.
+//
+PNDIS_BUFFER
+GrowARPHeaders(ARPInterface *Interface)
+{
+ ARPBufferTracker *NewTracker;
+ PNDIS_BUFFER Buffer, ReturnBuffer;
+ uchar *Header;
+ uint i;
+ NDIS_STATUS Status;
+ CTELockHandle Handle;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+
+ // Make sure we're allowed to allocate.
+ if (Interface->ai_curhdrs >= Interface->ai_maxhdrs)
+ goto failure;
+
+ NewTracker = CTEAllocMem(sizeof(ARPBufferTracker));
+ if (NewTracker == NULL)
+ goto failure; // We're out of memory.
+
+ NdisAllocateBufferPool(&Status, &NewTracker->abt_handle,
+ ARP_HDRBUF_GROW_SIZE);
+
+ if (Status != NDIS_STATUS_SUCCESS) {
+ CTEFreeMem(NewTracker);
+ goto failure;
+ }
+
+ Header = CTEAllocMem((uint)Interface->ai_sbsize * ARP_HDRBUF_GROW_SIZE);
+ if (Header == NULL) {
+ NdisFreeBufferPool(NewTracker->abt_handle);
+ CTEFreeMem(NewTracker);
+ goto failure;
+ }
+
+ // Got the resources we need, allocate the buffers.
+ NewTracker->abt_buffer = Header;
+ NewTracker->abt_next = Interface->ai_buflist;
+ Interface->ai_buflist = NewTracker;
+ ReturnBuffer = NULL;
+ Interface->ai_curhdrs += ARP_HDRBUF_GROW_SIZE;
+ CTEFreeLock(&Interface->ai_lock, Handle);
+
+ for (i = 0; i < ARP_HDRBUF_GROW_SIZE; i++) {
+ NdisAllocateBuffer(&Status, &Buffer, NewTracker->abt_handle,
+ Header + (i * Interface->ai_sbsize), Interface->ai_sbsize);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ CTEAssert(FALSE);
+ break;
+ }
+ if (i != 0) {
+ FreeARPBuffer(Interface, Buffer);
+ } else
+ ReturnBuffer = Buffer;
+ }
+
+ // Update for what we didn't allocate, if any.
+ CTEInterlockedAddUlong(&Interface->ai_curhdrs, i - ARP_HDRBUF_GROW_SIZE,
+ &Interface->ai_lock);
+
+ return ReturnBuffer;
+
+failure:
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return NULL;
+}
+
+//* GetARPBuffer - Get a buffer and descriptor
+//
+// Returns a pointer to an NDIS_BUFFER and a pointer to a buffer
+// of the specified size.
+//
+// Entry: Interface - Pointer to ARPInterface structure to allocate buffer from.
+// BufPtr - Pointer to where to return buf address.
+// Size - Size in bytes of buffer needed.
+//
+// Returns: Pointer to NDIS_BUFFER if successfull, NULL if not
+//
+PNDIS_BUFFER
+GetARPBuffer(ARPInterface *Interface, uchar **BufPtr, uchar size)
+{
+ CTELockHandle lhandle; // Lock handle
+ NDIS_STATUS Status;
+ PNDIS_BUFFER Buffer; // NDIS buffer allocated.
+
+ if (size <= Interface->ai_sbsize) {
+#ifdef VXD
+ Buffer = Interface->ai_sblist;
+ if (Buffer != NULL) {
+ Interface->ai_sblist = NDIS_BUFFER_LINKAGE(Buffer);
+ NDIS_BUFFER_LINKAGE(Buffer) = NULL;
+ NdisBufferLength(Buffer) = size;
+ *BufPtr = NdisBufferVirtualAddress(Buffer);
+ return Buffer;
+#else
+ PSINGLE_LIST_ENTRY BufferLink;
+
+ BufferLink = ExInterlockedPopEntrySList(
+ &Interface->ai_sblist,
+ &Interface->ai_lock
+ );
+ if (BufferLink != NULL) {
+ Buffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next);
+ NDIS_BUFFER_LINKAGE(Buffer) = NULL;
+ NdisBufferLength(Buffer) = size;
+ *BufPtr = NdisBufferVirtualAddress(Buffer);
+ return Buffer;
+#endif
+
+ } else {
+ Buffer = GrowARPHeaders(Interface);
+ if (Buffer != NULL) {
+ NDIS_BUFFER_LINKAGE(Buffer) = NULL;
+ NdisBufferLength(Buffer) = size;
+ *BufPtr = NdisBufferVirtualAddress(Buffer);
+ }
+ return Buffer;
+ }
+ } else {
+ // Need a 'big' buffer.
+ CTEGetLock(&Interface->ai_lock, &lhandle);
+ if ((*BufPtr = Interface->ai_bblist) != (uchar *)NULL) {
+ Interface->ai_bblist = *(uchar **)*BufPtr;
+ CTEFreeLock(&Interface->ai_lock, lhandle); // Got a buffer.
+ NdisAllocateBuffer(&Status, &Buffer, Interface->ai_bpool, *BufPtr,
+ size);
+ if (Status == NDIS_STATUS_SUCCESS)
+ return Buffer;
+ else { // Couldn't get NDIS buffer, free our buffer.
+ CTEGetLock(&Interface->ai_lock, &lhandle);
+ *(uchar **)&**BufPtr = Interface->ai_bblist;
+ Interface->ai_bblist = *BufPtr;
+ CTEFreeLock(&Interface->ai_lock, lhandle);
+ return (PNDIS_BUFFER)NULL;
+ }
+ }
+
+ // Couldn't get a header buffer, free lock and return NULL.
+ CTEFreeLock(&Interface->ai_lock, lhandle);
+ return (PNDIS_BUFFER)NULL;
+ }
+}
+
+
+//* BitSwap - Bit swap two strings.
+//
+// A routine to bitswap two strings.
+//
+// Input: Dest - Destination of swap.
+// Src - Src string to be swapped.
+// Length - Length in bytes to swap.
+//
+// Returns: Nothing.
+//
+void
+BitSwap(uchar *Dest, uchar *Src, uint Length)
+{
+ uint i;
+ uchar Temp, TempSrc;
+
+ for (i = 0; i < Length; i++, Dest++, Src++) {
+ TempSrc = *Src;
+ Temp = SwapTableLo[TempSrc >> 4] | SwapTableHi[TempSrc & 0x0f];
+ *Dest = Temp;
+ }
+
+}
+
+
+//* SendARPPacket - Build a header, and send a packet.
+//
+// A utility routine to build and ARP header and send a packet. We assume
+// the media specific header has been built.
+//
+// Entry: Interface - Interface for NDIS drive.
+// Packet - Pointer to packet to be sent
+// Header - Pointer to header to fill in.
+// Opcode - Opcode for packet.
+// Address - Source HW address.
+// SrcAddr - Address to use as our source h/w address.
+// Destination - Destination IP address.
+// Src - Source IP address.
+// HWType - Hardware type.
+// CheckIF - TRUE iff we are to check the I/F status before
+// sending.
+//
+// Returns: NDIS_STATUS of send.
+//
+NDIS_STATUS
+SendARPPacket(ARPInterface *Interface, PNDIS_PACKET Packet, ARPHeader *Header, ushort Opcode,
+ uchar *Address, uchar *SrcAddr, IPAddr Destination, IPAddr Src,
+ ushort HWType, uint CheckIF)
+{
+ NDIS_STATUS Status;
+ PNDIS_BUFFER Buffer;
+ uint PacketDone;
+ uchar *AddrPtr;
+
+ Header->ah_hw = HWType;
+ Header->ah_pro = net_short(ARP_ETYPE_IP);
+ Header->ah_hlen = Interface->ai_addrlen;
+ Header->ah_plen = sizeof(IPAddr);
+ Header->ah_opcode = Opcode;
+ AddrPtr = Header->ah_shaddr;
+
+ if (SrcAddr == NULL)
+ SrcAddr = Interface->ai_addr;
+
+ CTEMemCopy(AddrPtr, SrcAddr, Interface->ai_addrlen);
+
+ AddrPtr += Interface->ai_addrlen;
+ *(IPAddr UNALIGNED *)AddrPtr = Src;
+ AddrPtr += sizeof(IPAddr);
+
+ if (Address != (uchar *)NULL)
+ CTEMemCopy(AddrPtr, Address, Interface->ai_addrlen);
+ else
+ CTEMemSet(AddrPtr, 0, Interface->ai_addrlen);
+
+ AddrPtr += Interface->ai_addrlen;
+ *(IPAddr UNALIGNED *)AddrPtr = Destination;
+
+ PacketDone = FALSE;
+
+ if (!CheckIF || Interface->ai_state == INTERFACE_UP) {
+
+ Interface->ai_qlen++;
+ NdisSend(&Status, Interface->ai_handle, Packet);
+
+ if (Status != NDIS_STATUS_PENDING) {
+ PacketDone = TRUE;
+ Interface->ai_qlen--;
+#ifdef VXD
+ CTEAssert(*(int *)&Interface->ai_qlen >= 0);
+#endif
+ if (Status == NDIS_STATUS_SUCCESS)
+ Interface->ai_outoctets += Packet->Private.TotalLength;
+ else {
+ if (Status == NDIS_STATUS_RESOURCES)
+ Interface->ai_outdiscards++;
+ else
+ Interface->ai_outerrors++;
+ }
+ }
+ } else {
+ PacketDone = TRUE;
+ Status = NDIS_STATUS_ADAPTER_NOT_READY;
+ }
+
+ if (PacketDone) {
+ NdisUnchainBufferAtFront(Packet, &Buffer);
+ FreeARPBuffer(Interface, Buffer);
+ NdisFreePacket(Packet);
+ }
+ return Status;
+}
+
+//* SendARPRequest - Send an ARP packet
+//
+// Called when we need to ARP an IP address, or respond to a request. We'll send out
+// the packet, and the receiving routines will process the response.
+//
+// Entry: Interface - Interface to send the request on.
+// Destination - The IP address to be ARPed.
+// Type - Either RESOLVING_GLOBAL or RESOLVING_LOCAL
+// SrcAddr - NULL if we're sending from ourselves, the value
+// to use otherwise.
+// CheckIF - Flag passed through to SendARPPacket().
+//
+// Returns: Status of attempt to send ARP request.
+//
+NDIS_STATUS
+SendARPRequest(ARPInterface *Interface, IPAddr Destination, uchar Type,
+ uchar *SrcAddr, uint CheckIF)
+{
+ uchar *MHeader; // Pointer to media header.
+ PNDIS_BUFFER Buffer; // NDIS buffer descriptor.
+ uchar MHeaderSize; // Size of media header.
+ uchar *MAddr; // Pointer to media address structure.
+ uint SAddrOffset; // Offset into media address of source address.
+ uchar SRFlag = 0; // Source routing flag.
+ uchar SNAPLength = 0;
+ uchar *SNAPAddr; // Address of SNAP header.
+ PNDIS_PACKET Packet; // Packet for sending.
+ NDIS_STATUS Status;
+ ushort HWType;
+ IPAddr Src;
+ CTELockHandle Handle;
+ ARPIPAddr *Addr;
+
+ // First, get a source address we can use.
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ Addr = &Interface->ai_ipaddr;
+ Src = NULL_IP_ADDR;
+ do {
+ if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) {
+ //
+ // This is a valid address. See if it is the same as the
+ // target address - i.e. arp'ing for ourselves. If it is,
+ // we want to use that as our source address.
+ //
+ if (IP_ADDR_EQUAL(Addr->aia_addr, Destination)) {
+ Src = Addr->aia_addr;
+ break;
+ }
+
+ // See if the target is on this subnet.
+ if (IP_ADDR_EQUAL(
+ Addr->aia_addr & Addr->aia_mask,
+ Destination & Addr->aia_mask
+ ))
+ {
+ //
+ // See if we've already found a suitable candidate on the
+ // same subnet. If we haven't, we'll use this one.
+ //
+ if (!IP_ADDR_EQUAL(
+ Addr->aia_addr & Addr->aia_mask,
+ Src & Addr->aia_mask
+ ))
+ {
+ Src = Addr->aia_addr;
+ }
+ }
+ else {
+ // He's not on our subnet. If we haven't already found a valid
+ // address save this one in case we don't find a match for the
+ // subnet.
+ if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) {
+ Src = Addr->aia_addr;
+ }
+ }
+ }
+
+ Addr = Addr->aia_next;
+
+ } while (Addr != NULL);
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+
+ // If we didn't find a source address, give up.
+ if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR))
+ return NDIS_STATUS_SUCCESS;
+
+ NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ Interface->ai_outdiscards++;
+ return Status;
+ }
+
+ ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK;
+ (Interface->ai_outpcount[AI_NONUCAST_INDEX])++;
+
+ // Figure out what type of media this is, and do the appropriate thing.
+ switch (Interface->ai_media) {
+ case NdisMedium802_3:
+ MHeaderSize = ARP_MAX_MEDIA_ENET;
+ MAddr = ENetBcst;
+ if (Interface->ai_snapsize == 0) {
+ SNAPAddr = (uchar *)NULL;
+ HWType = net_short(ARP_HW_ENET);
+ } else {
+ SNAPLength = sizeof(SNAPHeader);
+ SNAPAddr = ARPSNAP;
+ HWType = net_short(ARP_HW_802);
+ }
+
+ SAddrOffset = offsetof(struct ENetHeader, eh_saddr);
+ break;
+ case NdisMedium802_5:
+ // Token ring. We have logic for dealing with the second transmit
+ // of an arp request.
+ MAddr = TRBcst;
+ SAddrOffset = offsetof(struct TRHeader, tr_saddr);
+ SNAPLength = sizeof(SNAPHeader);
+ SNAPAddr = ARPSNAP;
+ MHeaderSize = sizeof(TRHeader);
+ HWType = net_short(ARP_HW_802);
+ if (Type == ARP_RESOLVING_GLOBAL) {
+ MHeaderSize += sizeof(RC);
+ SRFlag = TR_RII;
+ }
+ break;
+ case NdisMediumFddi:
+ MHeaderSize = sizeof(FDDIHeader);
+ MAddr = FDDIBcst;
+ SNAPAddr = ARPSNAP;
+ SNAPLength = sizeof(SNAPHeader);
+ SAddrOffset = offsetof(struct FDDIHeader, fh_saddr);
+ HWType = net_short(ARP_HW_ENET);
+ break;
+ case NdisMediumArcnet878_2:
+ MHeaderSize = ARP_MAX_MEDIA_ARC;
+ MAddr = ARCBcst;
+ SNAPAddr = (uchar *)NULL;
+ SAddrOffset = offsetof(struct ARCNetHeader, ah_saddr);
+ HWType = net_short(ARP_HW_ARCNET);
+ break;
+ default:
+ DEBUGCHK;
+ Interface->ai_outerrors++;
+ return NDIS_STATUS_UNSUPPORTED_MEDIA;
+ }
+
+
+
+ if ((Buffer = GetARPBuffer(Interface, &MHeader,
+ (uchar)(sizeof(ARPHeader) + MHeaderSize + SNAPLength))) == (PNDIS_BUFFER)NULL) {
+ NdisFreePacket(Packet);
+ Interface->ai_outdiscards++;
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ if (Interface->ai_media == NdisMediumArcnet878_2)
+ NdisBufferLength(Buffer) -= ARCNET_ARPHEADER_ADJUSTMENT;
+
+ // Copy broadcast address into packet.
+ CTEMemCopy(MHeader, MAddr, MHeaderSize);
+ // Fill in source address.
+ if (SrcAddr == NULL) {
+ SrcAddr = Interface->ai_addr;
+ }
+
+ if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize != 0) {
+ ENetHeader *Hdr = (ENetHeader *)MHeader;
+
+ // Using SNAP on ethernet. Adjust the etype to a length.
+ Hdr->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader));
+ }
+
+ CTEMemCopy(&MHeader[SAddrOffset], SrcAddr, Interface->ai_addrlen);
+ if ((Interface->ai_media == NdisMedium802_5) && (Type == ARP_RESOLVING_GLOBAL)) {
+ // Turn on source routing.
+ MHeader[SAddrOffset] |= SRFlag;
+ MHeader[SAddrOffset + Interface->ai_addrlen] |= TrRii;
+ }
+ // Copy in SNAP header, if any.
+ CTEMemCopy(&MHeader[MHeaderSize], SNAPAddr, SNAPLength);
+
+ // Media header is filled in. Now do ARP packet itself.
+ NdisChainBufferAtFront(Packet, Buffer);
+ return SendARPPacket(Interface, Packet,(ARPHeader *)&MHeader[MHeaderSize + SNAPLength],
+ net_short(ARP_REQUEST), (uchar *)NULL, SrcAddr, Destination, Src,
+ HWType, CheckIF);
+}
+
+//* SendARPReply - Reply to an ARP request.
+//
+// Called by our receive packet handler when we need to reply. We build a packet
+// and buffer and call SendARPPacket to send it.
+//
+// Entry: Interface - Pointer to interface to reply on.
+// Destination - IPAddress to reply to.
+// Src - Source address to reply from.
+// HWAddress - Hardware address to reply to.
+// SourceRoute - Source Routing information, if any.
+// SourceRouteSize - Size in bytes of soure routing.
+// UseSNAP - Whether or not to use SNAP for this reply.
+//
+// Returns: Nothing.
+//
+void
+SendARPReply(ARPInterface *Interface, IPAddr Destination, IPAddr Src, uchar *HWAddress,
+ RC UNALIGNED *SourceRoute, uint SourceRouteSize, uint UseSNAP)
+{
+ PNDIS_PACKET Packet; // Buffer and packet to be used.
+ PNDIS_BUFFER Buffer;
+ uchar *Header; // Pointer to media header.
+ NDIS_STATUS Status;
+ uchar Size = 0; // Size of media header buffer.
+ ushort HWType;
+ ENetHeader *EH;
+ FDDIHeader *FH;
+ ARCNetHeader *AH;
+ TRHeader *TRH;
+
+ // Allocate a packet for this.
+ NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ Interface->ai_outdiscards++;
+ return;
+ }
+
+ ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK;
+ (Interface->ai_outpcount[AI_UCAST_INDEX])++;
+
+ Size = Interface->ai_hdrsize;
+
+ if (UseSNAP)
+ Size += Interface->ai_snapsize;
+
+ if (Interface->ai_media == NdisMedium802_5)
+ Size += SourceRouteSize;
+
+ if ((Buffer = GetARPBuffer(Interface, &Header, (uchar)(Size + sizeof(ARPHeader)))) ==
+ (PNDIS_BUFFER)NULL) {
+ Interface->ai_outdiscards++;
+ NdisFreePacket(Packet);
+ return;
+ }
+
+ // Decide how to build the header based on the media type.
+ switch (Interface->ai_media) {
+ case NdisMedium802_3:
+ EH = (ENetHeader *)Header;
+ CTEMemCopy(EH->eh_daddr, HWAddress, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(EH->eh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH);
+ if (!UseSNAP) {
+ EH->eh_type = net_short(ARP_ETYPE_ARP);
+ HWType = net_short(ARP_HW_ENET);
+ } else {
+ // Using SNAP on ethernet.
+ EH->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader));
+ HWType = net_short(ARP_HW_802);
+ CTEMemCopy(Header + sizeof(ENetHeader), ARPSNAP,
+ sizeof(SNAPHeader));
+ }
+ break;
+ case NdisMedium802_5:
+ TRH = (TRHeader *)Header;
+ TRH->tr_ac = ARP_AC;
+ TRH->tr_fc = ARP_FC;
+ CTEMemCopy(TRH->tr_daddr, HWAddress, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(TRH->tr_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH);
+ if (SourceRouteSize) {// If we have source route info, deal with
+ // it.
+ CTEMemCopy(Header + sizeof(TRHeader), SourceRoute,
+ SourceRouteSize);
+ // Convert to directed response.
+ ((RC *)&Header[sizeof(TRHeader)])->rc_blen &= RC_LENMASK;
+
+ ((RC *)&Header[sizeof(TRHeader)])->rc_dlf ^= RC_DIR;
+ TRH->tr_saddr[0] |= TR_RII;
+ }
+ CTEMemCopy(Header + sizeof(TRHeader) + SourceRouteSize, ARPSNAP,
+ sizeof(SNAPHeader));
+ HWType = net_short(ARP_HW_802);
+ break;
+ case NdisMediumFddi:
+ FH = (FDDIHeader *)Header;
+ FH->fh_pri = ARP_FDDI_PRI;
+ CTEMemCopy(FH->fh_daddr, HWAddress, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(FH->fh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(Header + sizeof(FDDIHeader), ARPSNAP, sizeof(SNAPHeader));
+ HWType = net_short(ARP_HW_ENET);
+ break;
+ case NdisMediumArcnet878_2:
+ AH = (ARCNetHeader *)Header;
+ AH->ah_saddr = Interface->ai_addr[0];
+ AH->ah_daddr = *HWAddress;
+ AH->ah_prot = ARP_ARCPROT_ARP;
+ NdisBufferLength(Buffer) -= ARCNET_ARPHEADER_ADJUSTMENT;
+ HWType = net_short(ARP_HW_ARCNET);
+ break;
+ default:
+ DEBUGCHK;
+ Interface->ai_outerrors++;
+ FreeARPBuffer(Interface, Buffer);
+ NdisFreePacket(Packet);
+ return;
+ }
+
+ NdisChainBufferAtFront(Packet, Buffer);
+ SendARPPacket(Interface, Packet,(ARPHeader *)(Header + Size), net_short(ARP_RESPONSE),
+ HWAddress, NULL, Destination, Src, HWType, TRUE);
+}
+
+
+//* ARPRemoveRCE - Remove an RCE from the ATE list.
+//
+// This funtion removes a specified RCE from a given ATE. It assumes the ate_lock
+// is held by the caller.
+//
+// Entry: ATE - ATE from which RCE is to be removed.
+// RCE - RCE to be removed.
+//
+// Returns: Nothing
+//
+void
+ARPRemoveRCE(ARPTableEntry *ATE, RouteCacheEntry *RCE)
+{
+ ARPContext *CurrentAC; // Current ARP Context being checked.
+#ifdef DEBUG
+ uint Found = FALSE;
+#endif
+
+ CurrentAC = (ARPContext *)(((char *)&ATE->ate_rce) -
+ offsetof(struct ARPContext, ac_next));
+
+ while (CurrentAC->ac_next != (RouteCacheEntry *)NULL)
+ if (CurrentAC->ac_next == RCE) {
+ ARPContext *DummyAC = (ARPContext *)RCE->rce_context;
+ CurrentAC->ac_next = DummyAC->ac_next;
+ DummyAC->ac_ate = (ARPTableEntry *)NULL;
+#ifdef DEBUG
+ Found = TRUE;
+#endif
+ break;
+ }
+ else
+ CurrentAC = (ARPContext *)CurrentAC->ac_next->rce_context;
+
+ CTEAssert(Found);
+}
+//* ARPLookup - Look up an entry in the ARP table.
+//
+// Called to look up an entry in an interface's ARP table. If we find it, we'll
+// lock the entry and return a pointer to it, otherwise we return NULL. We
+// assume that the caller has the ARP table locked when we are called.
+//
+// The ARP table entry is structured as a hash table of pointers to
+// ARPTableEntrys.After hashing on the IP address, a linear search is done to
+// lookup the entry.
+//
+// If we find the entry, we lock it for the caller. If we don't find
+// the entry, we leave the ARP table locked so that the caller may atomically
+// insert a new entry without worrying about a duplicate being inserted between
+// the time the table was checked and the time the caller went to insert the
+// entry.
+//
+// Entry: Interface - The interface to be searched upon.
+// Address - The IP address we're looking up.
+// Handle - Pointer to lock handle to be used to lock entry.
+//
+// Returns: Pointer to ARPTableEntry if found, or NULL if not.
+//
+ARPTableEntry *
+ARPLookup(ARPInterface *Interface, IPAddr Address, CTELockHandle *Handle)
+{
+ int i = ARP_HASH(Address); // Index into hash table.
+ ARPTableEntry *Current; // Current ARP Table entry being
+ // examined.
+
+ Current = (*Interface->ai_ARPTbl)[i];
+
+ while (Current != (ARPTableEntry *)NULL) {
+ CTEGetLock(&Current->ate_lock, Handle);
+ if (IP_ADDR_EQUAL(Current->ate_dest, Address)) { // Found a match.
+ return Current;
+ }
+ CTEFreeLock(&Current->ate_lock, *Handle);
+ Current = Current->ate_next;
+ }
+ // If we got here, we didn't find the entry. Leave the table locked and
+ // return the handle.
+ return (ARPTableEntry *)NULL;
+}
+
+//* IsBCastOnIF- See it an address is a broadcast address on an interface.
+//
+// Called to see if a particular address is a broadcast address on an
+// interface. We'll check the global, net, and subnet broadcasts. We assume
+// the caller holds the lock on the interface.
+//
+// Entry: Interface - Interface to check.
+// Addr - Address to check.
+//
+// Returns: TRUE if it it a broadcast, FALSE otherwise.
+//
+uint
+IsBCastOnIF(ARPInterface *Interface, IPAddr Addr)
+{
+ IPAddr BCast;
+ IPMask Mask;
+ ARPIPAddr *ARPAddr;
+ IPAddr LocalAddr;
+
+ // First get the interface broadcast address.
+ BCast = Interface->ai_bcast;
+
+ // First check for global broadcast.
+ if (IP_ADDR_EQUAL(BCast, Addr) || CLASSD_ADDR(Addr))
+ return TRUE;
+
+ // Now walk the local addresses, and check for net/subnet bcast on each
+ // one.
+ ARPAddr = &Interface->ai_ipaddr;
+ do {
+ // See if this one is valid.
+ LocalAddr = ARPAddr->aia_addr;
+ if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) {
+ // He's valid.
+ Mask = ARPAddr->aia_mask;
+
+ // First check for subnet bcast.
+ if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr))
+ return TRUE;
+
+ // Now check all nets broadcast.
+ Mask = IPNetMask(LocalAddr);
+ if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr))
+ return TRUE;
+ }
+
+ ARPAddr = ARPAddr->aia_next;
+
+ } while (ARPAddr != NULL);
+
+ // If we're here, it's not a broadcast.
+ return FALSE;
+
+}
+
+
+//* ARPSendBCast - See if this is a bcast or mcast frame, and send it.
+//
+// Called when we have a packet to send and we want to see if it's a broadcast
+// or multicast frame on this interface. We'll search the local addresses and
+// see if we can determine if it is. If it is, we'll send it here. Otherwise
+// we return FALSE, and the caller will try to resolve the address.
+//
+// Entry: Interface - A pointer to an AI structure.
+// Dest - Destination of datagram.
+// Packet - Packet to be sent.
+// Status - Place to return status of send attempt.
+//
+// Returns: TRUE if is was a bcast or mcast send, FALSE otherwise.
+//
+uint
+ARPSendBCast(ARPInterface *Interface, IPAddr Dest, PNDIS_PACKET Packet,
+ PNDIS_STATUS Status)
+{
+ uint IsBCast;
+ CTELockHandle Handle;
+ PNDIS_BUFFER ARPBuffer; // ARP Header buffer.
+ uchar *BufAddr; // Address of NDIS buffer
+ NDIS_STATUS MyStatus;
+ ENetHeader *Hdr;
+ FDDIHeader *FHdr;
+ TRHeader *TRHdr;
+ SNAPHeader UNALIGNED *SNAPPtr;
+ RC UNALIGNED *RCPtr;
+ ARCNetHeader *AHdr;
+ uint DataLength;
+
+ // Get the lock, and see if it's a broadcast.
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ IsBCast = IsBCastOnIF(Interface, Dest);
+ CTEFreeLock(&Interface->ai_lock, Handle);
+
+ if (IsBCast) {
+ if (Interface->ai_state == INTERFACE_UP) {
+ uchar Size;
+
+ Size = Interface->ai_hdrsize + Interface->ai_snapsize;
+
+ if (Interface->ai_media == NdisMedium802_5)
+ Size += sizeof(RC);
+
+ ARPBuffer = GetARPBuffer(Interface, &BufAddr, Size);
+ if (ARPBuffer != NULL) {
+ uint UNALIGNED *Temp;
+
+ // Got the buffer we need.
+ switch (Interface->ai_media) {
+
+ case NdisMedium802_3:
+
+ Hdr = (ENetHeader *)BufAddr;
+ if (!CLASSD_ADDR(Dest))
+ CTEMemCopy(Hdr, ENetBcst, ARP_802_ADDR_LENGTH);
+ else {
+ CTEMemCopy(Hdr, ENetMcst, ARP_802_ADDR_LENGTH);
+ Temp = (uint UNALIGNED *)&Hdr->eh_daddr[2];
+ *Temp |= (Dest & ARP_MCAST_MASK);
+ }
+
+ CTEMemCopy(Hdr->eh_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+
+ if (Interface->ai_snapsize == 0) {
+ // No snap on this interface, so just use ETypr.
+ Hdr->eh_type = net_short(ARP_ETYPE_IP);
+ } else {
+ ushort ShortDataLength;
+
+ // We're using SNAP. Find the size of the packet.
+ NdisQueryPacket(Packet, NULL, NULL, NULL,
+ &DataLength);
+ ShortDataLength = (ushort)(DataLength +
+ sizeof(SNAPHeader));
+ Hdr->eh_type = net_short(ShortDataLength);
+ SNAPPtr = (SNAPHeader UNALIGNED *)
+ (BufAddr + sizeof(ENetHeader));
+ CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader));
+ SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP);
+ }
+
+ break;
+
+ case NdisMedium802_5:
+
+ // This is token ring. We'll have to screw around with
+ // source routing.
+
+ // BUGBUG Need to support 'real' TR functional address
+ // for multicast - see RFC 1469.
+
+ TRHdr = (TRHeader *)BufAddr;
+
+ CTEMemCopy(TRHdr, TRBcst, offsetof(TRHeader, tr_saddr));
+ CTEMemCopy(TRHdr->tr_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+
+ if (sIPAlwaysSourceRoute)
+ {
+ TRHdr->tr_saddr[0] |= TR_RII;
+
+ RCPtr = (RC UNALIGNED *)((uchar *)TRHdr + sizeof(TRHeader));
+ RCPtr->rc_blen = TrRii | RC_LEN;
+ RCPtr->rc_dlf = RC_BCST_LEN;
+ SNAPPtr = (SNAPHeader UNALIGNED *)((uchar *)RCPtr + sizeof(RC));
+ }
+ else
+ {
+
+ //
+ // Adjust the size of the buffer to account for the
+ // fact that we don't have the RC field.
+ //
+ NdisAdjustBufferLength(ARPBuffer,(Size - sizeof(RC)));
+ SNAPPtr = (SNAPHeader UNALIGNED *)((uchar *)TRHdr + sizeof(TRHeader));
+ }
+ CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader));
+ SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP);
+
+ break;
+ case NdisMediumFddi:
+ FHdr = (FDDIHeader *)BufAddr;
+
+ if (!CLASSD_ADDR(Dest))
+ CTEMemCopy(FHdr, FDDIBcst,
+ offsetof(FDDIHeader, fh_saddr));
+ else {
+ CTEMemCopy(FHdr, FDDIMcst,
+ offsetof(FDDIHeader, fh_saddr));
+ Temp = (uint UNALIGNED *)&FHdr->fh_daddr[2];
+ *Temp |= (Dest & ARP_MCAST_MASK);
+ }
+
+ CTEMemCopy(FHdr->fh_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+
+ SNAPPtr = (SNAPHeader UNALIGNED *)(BufAddr + sizeof(FDDIHeader));
+ CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader));
+ SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP);
+
+ break;
+ case NdisMediumArcnet878_2:
+ AHdr = (ARCNetHeader *)BufAddr;
+ AHdr->ah_saddr = Interface->ai_addr[0];
+ AHdr->ah_daddr = 0;
+ AHdr->ah_prot = ARP_ARCPROT_IP;
+ break;
+ default:
+ DEBUGCHK;
+ *Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
+ FreeARPBuffer(Interface, ARPBuffer);
+ return FALSE;
+
+ }
+
+ (Interface->ai_outpcount[AI_NONUCAST_INDEX])++;
+ Interface->ai_qlen++;
+ NdisChainBufferAtFront(Packet, ARPBuffer);
+ NdisSend(&MyStatus, Interface->ai_handle, Packet);
+
+ *Status = MyStatus;
+
+ if (MyStatus != NDIS_STATUS_PENDING) { // Send finished
+ // immediately.
+ if (MyStatus == NDIS_STATUS_SUCCESS) {
+ Interface->ai_outoctets += Packet->Private.TotalLength;
+ } else {
+ if (MyStatus == NDIS_STATUS_RESOURCES)
+ Interface->ai_outdiscards++;
+ else
+ Interface->ai_outerrors++;
+ }
+
+ Interface->ai_qlen--;
+#ifdef VXD
+ CTEAssert(*(int *)&Interface->ai_qlen >= 0);
+#endif
+ NdisUnchainBufferAtFront(Packet, &ARPBuffer);
+ FreeARPBuffer(Interface, ARPBuffer);
+ }
+ } else
+ *Status = NDIS_STATUS_RESOURCES;
+ } else
+ *Status = NDIS_STATUS_ADAPTER_NOT_READY;
+
+ return TRUE;
+
+ } else
+ return FALSE;
+}
+
+//* ARPSendData - Send a frame to a specific destination address.
+//
+// Called when we need to send a frame to a particular address, after the
+// ATE has been looked up. We take in an ATE and a packet, validate the state of the
+// ATE, and either send or ARP for the address if it's not done resolving. We assume
+// the lock on the ATE is held where we're called, and we'll free it before returning.
+//
+// Entry: Interface - A pointer to the AI structure.
+// Packet - A pointer to the BufDesc chain to be sent.
+// entry - A pointer to the ATE for the send.
+// lhandle - Pointer to a lock handle for the ATE.
+//
+// Returns: Status of the transmit - success, an error, or pending.
+//
+NDIS_STATUS
+ARPSendData(ARPInterface *Interface, PNDIS_PACKET Packet, ARPTableEntry *entry,
+ CTELockHandle lhandle)
+{
+ PNDIS_BUFFER ARPBuffer; // ARP Header buffer.
+ uchar *BufAddr; // Address of NDIS buffer
+ NDIS_STATUS Status; // Status of send.
+
+ if (Interface->ai_state == INTERFACE_UP) {
+
+ if (entry->ate_state == ARP_GOOD) { // Entry is valid
+
+ entry->ate_useticks = ArpCacheLife;
+ if ((ARPBuffer = GetARPBuffer(Interface, &BufAddr,
+ entry->ate_addrlength)) != (PNDIS_BUFFER)NULL) {
+
+ // Everything's in good shape, copy header and send packet.
+
+ (Interface->ai_outpcount[AI_UCAST_INDEX])++;
+ Interface->ai_qlen++;
+ CTEMemCopy(BufAddr, entry->ate_addr, entry->ate_addrlength);
+
+ // If we're on Ethernet, see if we're using SNAP here.
+ if (Interface->ai_media == NdisMedium802_3 &&
+ entry->ate_addrlength != sizeof(ENetHeader)) {
+ ENetHeader *Header;
+ uint DataSize;
+ ushort ShortDataSize;
+
+ // We're apparently using SNAP on Ethernet. Query the
+ // packet for the size, and set the length properly.
+ NdisQueryPacket(Packet, NULL, NULL, NULL, &DataSize);
+ ShortDataSize = (ushort)(DataSize + sizeof(SNAPHeader));
+ Header = (ENetHeader *)BufAddr;
+ Header->eh_type = net_short(ShortDataSize);
+ }
+
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ NdisChainBufferAtFront(Packet, ARPBuffer);
+ NdisSend(&Status, Interface->ai_handle, Packet);
+ if (Status != NDIS_STATUS_PENDING) { // Send finished
+ // immediately.
+ if (Status == NDIS_STATUS_SUCCESS) {
+ Interface->ai_outoctets += Packet->Private.TotalLength;
+ } else {
+ if (Status == NDIS_STATUS_RESOURCES)
+ Interface->ai_outdiscards++;
+ else
+ Interface->ai_outerrors++;
+ }
+
+ Interface->ai_qlen--;
+#ifdef VXD
+ CTEAssert(*(int *)&Interface->ai_qlen >= 0);
+#endif
+ NdisUnchainBufferAtFront(Packet, &ARPBuffer);
+ FreeARPBuffer(Interface, ARPBuffer);
+ }
+ return Status;
+ } else { // No buffer, free lock and return.
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ Interface->ai_outdiscards++;
+ return NDIS_STATUS_RESOURCES;
+ }
+ }
+ // The IP addresses match, but the state of the ARP entry indicates
+ // it's not valid. If the address is marked as resolving, we'll replace
+ // the current cached packet with this one. If it's been more than
+ // ARP_FLOOD_RATE ms. since we last sent an ARP request, we'll send
+ // another one now.
+ if (entry->ate_state <= ARP_RESOLVING) {
+ PNDIS_PACKET OldPacket = entry->ate_packet;
+ ulong Now = CTESystemUpTime();
+ entry->ate_packet = Packet;
+ if ((Now - entry->ate_valid) > ARP_FLOOD_RATE) {
+ IPAddr Dest = entry->ate_dest;
+
+ entry->ate_valid = Now;
+ entry->ate_state = ARP_RESOLVING_GLOBAL; // We're done this
+ // at least once.
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL,
+ NULL, TRUE); // Send a request.
+ } else
+ CTEFreeLock(&entry->ate_lock, lhandle);
+
+ if (OldPacket)
+ IPSendComplete(Interface->ai_context, OldPacket,
+ NDIS_STATUS_SUCCESS);
+
+ return NDIS_STATUS_PENDING;
+ } else {
+ DEBUGCHK;
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ Interface->ai_outerrors++;
+ return NDIS_STATUS_INVALID_PACKET;
+ }
+ } else {
+ // Adapter is down. Just return the error.
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ return NDIS_STATUS_ADAPTER_NOT_READY;
+ }
+}
+
+//* CreateARPTableEntry - Create a new entry in the ARP table.
+//
+// A function to put an entry into the ARP table. We allocate memory if we
+// need to.
+//
+// The first thing to do is get the lock on the ARP table, and see if the
+// entry already exists. If it does, we're done. Otherwise we need to allocate
+// memory and create a new entry.
+//
+// Entry: Interface - Interface for ARP table.
+// Destination - Destination address to be mapped.
+// Handle - Pointer to lock handle for entry.
+//
+// Returns: Pointer to newly created entry.
+//
+ARPTableEntry *
+CreateARPTableEntry(ARPInterface *Interface, IPAddr Destination,
+ CTELockHandle *Handle)
+{
+ ARPTableEntry *NewEntry, *Entry;
+ CTELockHandle TableHandle;
+ int i = ARP_HASH(Destination);
+ int Size;
+
+ // First look for it, and if we don't find it return try to create one.
+ CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle);
+ if ((Entry = ARPLookup(Interface, Destination, Handle)) !=
+ (ARPTableEntry *)NULL) {
+ CTEFreeLock(&Interface->ai_ARPTblLock, *Handle);
+ *Handle = TableHandle;
+ return Entry;
+ }
+
+ // Allocate memory for the entry. If we can't, fail the request.
+ Size = sizeof(ARPTableEntry) - 1 +
+ (Interface->ai_media == NdisMedium802_5 ?
+ ARP_MAX_MEDIA_TR : (Interface->ai_hdrsize +
+ Interface->ai_snapsize));
+
+ if ((NewEntry = CTEAllocMem(Size)) == (ARPTableEntry *)NULL) {
+ CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle);
+ return (ARPTableEntry *)NULL;
+ }
+
+ CTEMemSet(NewEntry, 0, Size);
+ NewEntry->ate_dest = Destination;
+ if (Interface->ai_media != NdisMedium802_5 || sArpAlwaysSourceRoute)
+ NewEntry->ate_state = ARP_RESOLVING_GLOBAL;
+ else
+ NewEntry->ate_state = ARP_RESOLVING_LOCAL;
+
+ NewEntry->ate_rce = NULL;
+
+ NewEntry->ate_valid = CTESystemUpTime();
+ NewEntry->ate_useticks = ArpCacheLife;
+ CTEInitLock(&NewEntry->ate_lock);
+
+ // Entry does not exist. Insert the new entry into the table at the appropriate spot.
+ // ARPLookup returns with the table lock held if it fails.
+ NewEntry->ate_next = (*Interface->ai_ARPTbl)[i];
+ (*Interface->ai_ARPTbl)[i] = NewEntry;
+ Interface->ai_count++;
+ CTEGetLock(&NewEntry->ate_lock, Handle);
+ CTEFreeLock(&Interface->ai_ARPTblLock, *Handle);
+ *Handle = TableHandle;
+ return NewEntry;
+}
+
+
+//* ARPTransmit - Send a frame.
+//
+// The main ARP transmit routine, called by the upper layer. This routine
+// takes as input a buf desc chain, RCE, and size. We validate the cached
+// information in the RCE. If it is valid, we use it to send the frame. Otherwise
+// we do a table lookup. If we find it in the table, we'll update the RCE and continue.
+// Otherwise we'll queue the packet and start an ARP resolution.
+//
+// Entry: Context - A pointer to the AI structure.
+// Packet - A pointer to the BufDesc chain to be sent.
+// Destination - IP address of destination we're trying to reach,
+// RCE - A pointer to an RCE which may have cached information.
+//
+// Returns: Status of the transmit - success, an error, or pending.
+//
+NDIS_STATUS
+ARPTransmit(void *Context, PNDIS_PACKET Packet, IPAddr Destination,
+ RouteCacheEntry *RCE)
+{
+ ARPInterface *ai = (ARPInterface *)Context; // Set up as AI pointer.
+ ARPContext *ac; // ARP context pointer.
+ ARPTableEntry *entry; // Pointer to ARP tbl. entry
+ CTELockHandle lhandle; // Lock handle
+ CTELockHandle tlhandle; // Lock handle for ARP table.
+ NDIS_STATUS Status;
+
+ CTEGetLock(&ai->ai_ARPTblLock, &tlhandle);
+ if (RCE != (RouteCacheEntry *)NULL) { // Have a valid RCE.
+ ac = (ARPContext *)RCE->rce_context; // Get pointer to context
+ entry = ac->ac_ate;
+ if (entry != (ARPTableEntry *)NULL) { // Have a valid ATE.
+ CTEGetLockAtDPC(&entry->ate_lock, &lhandle); // Lock this structure
+ if (IP_ADDR_EQUAL(entry->ate_dest, Destination)) {
+ CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle);
+ return ARPSendData(ai, Packet, entry, tlhandle); // Send the data
+ }
+
+ // We have an RCE that identifies the wrong ATE. We'll free it from
+ // this list and try and find an ATE that is valid.
+ ARPRemoveRCE(entry, RCE);
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ // Fall through to 'no valid entry' code.
+ }
+ }
+
+ // Here we have no valid ATE, either because the RCE is NULL or the ATE
+ // specified by the RCE was invalid. We'll try and find one in the table. If
+ // we find one, we'll fill in this RCE and send the packet. Otherwise we'll
+ // try to create one. At this point we hold the lock on the ARP table.
+
+ if ((entry = ARPLookup(ai, Destination, &lhandle)) != (ARPTableEntry *)NULL) {
+ // Found a matching entry. ARPLookup returns with the ATE lock held.
+ if (RCE != (RouteCacheEntry *)NULL) {
+ ac->ac_next = entry->ate_rce; // Fill in context for next time.
+ entry->ate_rce = RCE;
+ ac->ac_ate = entry;
+ }
+ CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle);
+ return ARPSendData(ai, Packet, entry, tlhandle);
+ }
+
+ // No valid entry in the ARP table. First we'll see if we're sending to a
+ // broadcast address or multicast address. If not, we'll try to create
+ // an entry in the table and get an ARP resolution going. ARPLookup returns
+ // with the table lock held when it fails, we'll free it here.
+ CTEFreeLock(&ai->ai_ARPTblLock, tlhandle);
+
+ if (ARPSendBCast(ai, Destination, Packet, &Status))
+ return Status;
+
+ entry = CreateARPTableEntry(ai, Destination, &lhandle);
+ if (entry != NULL) {
+ if (entry->ate_state <= ARP_RESOLVING) { // Newly created entry.
+
+ // Someone else could have raced in and created the entry between
+ // the time we free the lock and the time we called
+ // CreateARPTableEntry(). We check this by looking at the packet
+ // on the entry. If there is no old packet we'll ARP. If there is,
+ // we'll call ARPSendData to figure out what to do.
+
+ if (entry->ate_packet == NULL) {
+ entry->ate_packet = Packet;
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ SendARPRequest(ai, Destination, entry->ate_state, NULL, TRUE);
+ // We don't know the state of the entry - we've freed the lock
+ // and yielded, and it could conceivably have timed out by now,
+ // or SendARPRequest could have failed, etc. We could take the
+ // lock, check the status from SendARPRequest, see if it's
+ // still the same packet, and then make a decision on the
+ // return value, but it's easiest just to return pending. If
+ // SendARPRequest failed, the entry will time out anyway.
+ return NDIS_STATUS_PENDING;
+ } else
+ return ARPSendData(ai, Packet, entry, lhandle);
+
+ } else {
+ if (entry->ate_state == ARP_GOOD) // Yow! A valid entry.
+ return ARPSendData(ai, Packet, entry, lhandle);
+ else { // An invalid entry!
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ return NDIS_STATUS_RESOURCES;
+ }
+ }
+ } else // Couldn't create an entry.
+ return NDIS_STATUS_RESOURCES;
+
+}
+
+//* RemoveARPTableEntry - Delete an entry from the ARP table.
+//
+// This is a simple utility function to delete an entry from the ATP table. We
+// assume locks are held on both the table and the entry.
+//
+// Entry: Previous - The entry immediately before the one to be deleted.
+// Entry - The entry to be deleted.
+//
+// Returns: Nothing.
+//
+void
+RemoveARPTableEntry(ARPTableEntry *Previous, ARPTableEntry *Entry)
+{
+ RouteCacheEntry *RCE; // Pointer to route cache entry
+ ARPContext *AC;
+
+ RCE = Entry->ate_rce;
+ // Loop through and invalidate all RCEs on this ATE.
+ while (RCE != (RouteCacheEntry *)NULL) {
+ AC = (ARPContext *)RCE->rce_context;
+ AC->ac_ate = (ARPTableEntry *)NULL;
+ RCE = AC->ac_next;
+ }
+
+ // Splice this guy out of the list.
+ Previous->ate_next = Entry->ate_next;
+}
+
+//* ARPXferData - Transfer data on behalf on an upper later protocol.
+//
+// This routine is called by the upper layer when it needs to transfer data
+// from an NDIS driver. We just map his call down.
+//
+// Entry: Context - Context value we gave to IP (really a pointer to an AI).
+// MACContext - Context value MAC gave us on a receive.
+// MyOffset - Packet offset we gave to the protocol earlier.
+// ByteOffset - Byte offset into packet protocol wants transferred.
+// BytesWanted - Number of bytes to transfer.
+// Packet - Pointer to packet to be used for transferring.
+// Transferred - Pointer to where to return bytes transferred.
+//
+// Returns: NDIS_STATUS of command.
+//
+NDIS_STATUS
+ARPXferData(void *Context, NDIS_HANDLE MACContext, uint MyOffset, uint ByteOffset,
+ uint BytesWanted, PNDIS_PACKET Packet, uint *Transferred)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ NDIS_STATUS Status;
+
+ NdisTransferData(&Status, Interface->ai_handle, MACContext, ByteOffset+MyOffset,
+ BytesWanted, Packet, Transferred);
+
+ return Status;
+}
+
+
+//* ARPClose - Close an adapter.
+//
+// Called by IP when it wants to close an adapter, presumably due to an error condition.
+// We'll close the adapter, but we won't free any memory.
+//
+// Entry: Context - Context value we gave him earlier.
+//
+// Returns: Nothing.
+//
+void
+ARPClose(void *Context)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ NDIS_STATUS Status;
+ CTELockHandle LockHandle;
+ NDIS_HANDLE Handle;
+
+ Interface->ai_operstate = IF_STATUS_DOWN;
+ Interface->ai_state = INTERFACE_DOWN;
+ CTEInitBlockStruc(&Interface->ai_block);
+
+ CTEGetLock(&Interface->ai_lock, &LockHandle);
+ if (Interface->ai_handle != (NDIS_HANDLE)NULL) {
+ Handle = Interface->ai_handle;
+ Interface->ai_handle = NULL;
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+
+ NdisCloseAdapter(&Status, Handle);
+
+ if (Status == NDIS_STATUS_PENDING)
+ Status = CTEBlock(&Interface->ai_block);
+
+ } else {
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+ }
+}
+
+//* ARPInvalidate - Notification that an RCE is invalid.
+//
+// Called by IP when an RCE is closed or otherwise invalidated. We look up the ATE for
+// the specified RCE, and then remove the RCE from the ATE list.
+//
+// Entry: Context - Context value we gave him earlier.
+// RCE - RCE to be invalidated
+//
+// Returns: Nothing.
+//
+void
+ARPInvalidate(void *Context, RouteCacheEntry *RCE)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ ARPTableEntry *ATE;
+ CTELockHandle Handle, ATEHandle;
+ ARPContext *AC = (ARPContext *)RCE->rce_context;
+
+ CTEGetLock(&Interface->ai_ARPTblLock, &Handle);
+ if ((ATE = AC->ac_ate) == (ARPTableEntry *)NULL) {
+ CTEFreeLock(&Interface->ai_ARPTblLock, Handle); // No matching ATE.
+ return;
+ }
+
+ CTEGetLock(&ATE->ate_lock, &ATEHandle);
+ ARPRemoveRCE(ATE, RCE);
+ CTEMemSet(RCE->rce_context, 0, RCE_CONTEXT_SIZE);
+ CTEFreeLock(&Interface->ai_ARPTblLock, ATEHandle);
+ CTEFreeLock(&ATE->ate_lock, Handle);
+
+}
+
+//* ARPSetMCastList - Set the multicast address list for the adapter.
+//
+// Called to try and set the multicast reception list for the adapter.
+// We allocate a buffer big enough to hold the new address list, and format
+// the address list into the buffer. Then we submit the NDIS request to set
+// the list. If we can't set the list because the multicast address list is
+// full we'll put the card into all multicast mode.
+//
+// Input: Interface - Interface on which to set list.
+//
+// Returns: NDIS_STATUS of attempt.
+//
+NDIS_STATUS
+ARPSetMCastList(ARPInterface *Interface)
+{
+ CTELockHandle Handle;
+ uchar *MCastBuffer, *CurrentPtr;
+ uint MCastSize;
+ NDIS_STATUS Status;
+ uint i;
+ ARPMCastAddr *AddrPtr;
+ IPAddr UNALIGNED *Temp;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ MCastSize = Interface->ai_mcastcnt * ARP_802_ADDR_LENGTH;
+ if (MCastSize != 0)
+ MCastBuffer = CTEAllocMem(MCastSize);
+ else
+ MCastBuffer = NULL;
+
+ if (MCastBuffer != NULL || MCastSize == 0) {
+ // Got the buffer. Loop through, building the list.
+ AddrPtr = Interface->ai_mcast;
+
+ CurrentPtr = MCastBuffer;
+
+ for (i = 0; i < Interface->ai_mcastcnt; i++) {
+ CTEAssert(AddrPtr != NULL);
+
+ if (Interface->ai_media == NdisMedium802_3) {
+
+ CTEMemCopy(CurrentPtr, ENetMcst, ARP_802_ADDR_LENGTH);
+ Temp = (IPAddr UNALIGNED *)(CurrentPtr + 2);
+ *Temp |= AddrPtr->ama_addr;
+ } else
+ if (Interface->ai_media == NdisMediumFddi) {
+ CTEMemCopy(CurrentPtr, ((FDDIHeader *)FDDIMcst)->fh_daddr,
+ ARP_802_ADDR_LENGTH);
+ Temp = (IPAddr UNALIGNED *)(CurrentPtr + 2);
+ *Temp |= AddrPtr->ama_addr;
+ } else
+ DEBUGCHK;
+
+ CurrentPtr += ARP_802_ADDR_LENGTH;
+ AddrPtr = AddrPtr->ama_next;
+ }
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+
+ // We're built the list. Now give it to the driver to handle.
+ if (Interface->ai_media == NdisMedium802_3) {
+ Status = DoNDISRequest(Interface, NdisRequestSetInformation,
+ OID_802_3_MULTICAST_LIST, MCastBuffer, MCastSize, NULL);
+ } else
+ if (Interface->ai_media == NdisMediumFddi) {
+ Status = DoNDISRequest(Interface, NdisRequestSetInformation,
+ OID_FDDI_LONG_MULTICAST_LIST, MCastBuffer, MCastSize, NULL);
+ } else
+ DEBUGCHK;
+
+ if (MCastBuffer != NULL) {
+ CTEFreeMem(MCastBuffer);
+ }
+
+ if (Status == NDIS_STATUS_MULTICAST_FULL) {
+ // Multicast list is full. Try to set the filter to all multicasts.
+ Interface->ai_pfilter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
+
+ Status = DoNDISRequest(Interface, NdisRequestSetInformation,
+ OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter,
+ sizeof(uint), NULL);
+ }
+
+ } else {
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+
+ return Status;
+
+}
+
+//* ARPFindMCast - Find a multicast address structure on our list.
+//
+// Called as a utility to find a multicast address structure. If we find
+// it, we return a pointer to it and it's predecessor. Otherwise we return
+// NULL. We assume the caller holds the lock on the interface already.
+//
+// Input: Interface - Interface to search.
+// Addr - Addr to find.
+// Prev - Where to return previous pointer.
+//
+// Returns: Pointer if we find one, NULL otherwise.
+//
+ARPMCastAddr *
+ARPFindMCast(ARPInterface *Interface, IPAddr Addr, ARPMCastAddr **Prev)
+{
+ ARPMCastAddr *AddrPtr, *PrevPtr;
+
+ PrevPtr = STRUCT_OF(ARPMCastAddr, &Interface->ai_mcast, ama_next);
+ AddrPtr = PrevPtr->ama_next;
+ while (AddrPtr != NULL) {
+ if (IP_ADDR_EQUAL(AddrPtr->ama_addr, Addr))
+ break;
+ else {
+ PrevPtr = AddrPtr;
+ AddrPtr = PrevPtr->ama_next;
+ }
+ }
+
+ *Prev = PrevPtr;
+ return AddrPtr;
+}
+
+//* ARPDelMCast - Delete a multicast address.
+//
+// Called when we want to delete a multicast address. We look for a matching
+// (masked) address. If we find one, we'll dec. the reference count and if
+// it goes to 0 we'll pull him from the list and reset the multicast list.
+//
+// Input: Interface - Interface on which to act.
+// Addr - Address to be deleted.
+//
+// Returns: TRUE if it worked, FALSE otherwise.
+//
+uint
+ARPDelMCast(ARPInterface *Interface, IPAddr Addr)
+{
+ ARPMCastAddr *AddrPtr, *PrevPtr;
+ CTELockHandle Handle;
+ uint Status = TRUE;
+
+ // When we support TR (RFC 1469) fully we'll need to change this.
+ if (Interface->ai_media == NdisMedium802_3 || Interface->ai_media ==
+ NdisMediumFddi) {
+ // This is an interface that supports mcast addresses.
+ Addr &= ARP_MCAST_MASK;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr);
+ if (AddrPtr != NULL) {
+ // We found one. Dec. his refcnt, and if it's 0 delete him.
+ (AddrPtr->ama_refcnt)--;
+ if (AddrPtr->ama_refcnt == 0) {
+ // He's done.
+ PrevPtr->ama_next = AddrPtr->ama_next;
+ (Interface->ai_mcastcnt)--;
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ CTEFreeMem(AddrPtr);
+ ARPSetMCastList(Interface);
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ }
+ } else
+ Status = FALSE;
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ }
+
+ return Status;
+}
+//* ARPAddMCast - Add a multicast address.
+//
+// Called when we want to start receiving a multicast address. We'll mask
+// the address and look it up in our address list. If we find it, we'll just
+// bump the reference count. Otherwise we'll try to create one and put him
+// on the list. In that case we'll need to set the multicast address list for
+// the adapter.
+//
+// Input: Interface - Interface to set on.
+// Addr - Address to set.
+//
+// Returns: TRUE if we succeed, FALSE if we fail.
+//
+uint
+ARPAddMCast(ARPInterface *Interface, IPAddr Addr)
+{
+ ARPMCastAddr *AddrPtr, *PrevPtr;
+ CTELockHandle Handle;
+ uint Status = TRUE;
+
+
+ if (Interface->ai_state != INTERFACE_UP)
+ return FALSE;
+
+ // BUGBUG Currently we don't do anything with token ring, since we send
+ // all mcasts as TR broadcasts. When we comply with RFC 1469 we'll need to
+ // fix this.
+ if (Interface->ai_media == NdisMedium802_3 || Interface->ai_media ==
+ NdisMediumFddi) {
+ // This is an interface that supports mcast addresses.
+ Addr &= ARP_MCAST_MASK;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr);
+ if (AddrPtr != NULL) {
+ // We found one, just bump refcnt.
+ (AddrPtr->ama_refcnt)++;
+ } else {
+ // Didn't find one. Allocate space for one, link him in, and
+ // try to set the list.
+ AddrPtr = CTEAllocMem(sizeof(ARPMCastAddr));
+ if (AddrPtr != NULL) {
+ // Got one. Link him in.
+ AddrPtr->ama_addr = Addr;
+ AddrPtr->ama_refcnt = 1;
+ AddrPtr->ama_next = Interface->ai_mcast;
+ Interface->ai_mcast = AddrPtr;
+ (Interface->ai_mcastcnt)++;
+ CTEFreeLock(&Interface->ai_lock, Handle);
+
+ // Now try to set the list.
+ if (ARPSetMCastList(Interface) != NDIS_STATUS_SUCCESS) {
+ // Couldn't set the list. Call the delete routine to delete
+ // the address we just tried to set.
+ Status = ARPDelMCast(Interface, Addr);
+ if (!Status)
+ DEBUGCHK;
+ Status = FALSE;
+ }
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ } else
+ Status = FALSE; // Couldn't get memory.
+ }
+
+ // We've done out best. Free the lock and return.
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ }
+
+ return Status;
+}
+
+//* ARPAddAddr - Add an address to the ARP table.
+//
+// This routine is called by IP to add an address as a local address, or
+// or specify the broadcast address for this interface.
+//
+// Entry: Context - Context we gave IP earlier (really an ARPInterface pointer)
+// Type - Type of address (local, p-arp, multicast, or
+// broadcast).
+// Address - Broadcast IP address to be added.
+// Mask - Mask for address.
+//
+// Returns: 0 if we failed, non-zero otherwise
+//
+uint
+ARPAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ CTELockHandle Handle;
+
+ if (Type != LLIP_ADDR_LOCAL && Type != LLIP_ADDR_PARP) {
+ // Not a local address, must be broadcast or multicast.
+
+ if (Type == LLIP_ADDR_BCAST) {
+ Interface->ai_bcast = Address;
+ return TRUE;
+ } else
+ if (Type == LLIP_ADDR_MCAST) {
+ return ARPAddMCast(Interface, Address);
+ } else
+ return FALSE;
+ } else { // This is a local address.
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ if (Type != LLIP_ADDR_PARP) {
+ uint RetStatus = FALSE;
+ uint ArpForSelf = FALSE;
+
+ if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, 0)) {
+ Interface->ai_ipaddr.aia_addr = Address;
+ Interface->ai_ipaddr.aia_mask = Mask;
+ Interface->ai_ipaddr.aia_age = ARPADDR_NEW_LOCAL;
+ if (Interface->ai_state == INTERFACE_UP) {
+ Interface->ai_ipaddr.aia_context = Context2;
+ ArpForSelf = TRUE;
+ } else {
+ Interface->ai_ipaddr.aia_context = NULL;
+ }
+ RetStatus = TRUE;
+ } else {
+ ARPIPAddr *NewAddr;
+
+ NewAddr = CTEAllocMem(sizeof(ARPIPAddr));
+ if (NewAddr != (ARPIPAddr *)NULL) {
+ NewAddr->aia_addr = Address;
+ NewAddr->aia_mask = Mask;
+ NewAddr->aia_age = ARPADDR_NEW_LOCAL;
+ NewAddr->aia_next = Interface->ai_ipaddr.aia_next;
+ if (Interface->ai_state == INTERFACE_UP) {
+ NewAddr->aia_context = Context2;
+ ArpForSelf = TRUE;
+ } else {
+ NewAddr->aia_context = NULL;
+ }
+
+ Interface->ai_ipaddr.aia_next = NewAddr;
+ RetStatus = TRUE;
+ }
+ }
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ // ARP for the address we've added, to see it it already exists.
+ if (RetStatus == TRUE && ArpForSelf == TRUE) {
+ SendARPRequest(Interface, Address, ARP_RESOLVING_GLOBAL,
+ NULL, TRUE);
+ return IP_PENDING;
+ }
+
+ return RetStatus;
+ } else if (Type == LLIP_ADDR_PARP) {
+ ARPPArpAddr *NewPArp;
+
+ // He's adding a proxy arp address.
+ NewPArp = CTEAllocMem(sizeof(ARPPArpAddr));
+ if (NewPArp != NULL) {
+ NewPArp->apa_addr = Address;
+ NewPArp->apa_mask = Mask;
+ NewPArp->apa_next = Interface->ai_parpaddr;
+ Interface->ai_parpaddr = NewPArp;
+ Interface->ai_parpcount++;
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return TRUE;
+ }
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return FALSE;
+ }
+ }
+
+}
+
+//* ARPDeleteAddr - Delete a local or proxy address.
+//
+// Called to delete a local or proxy address.
+//
+// Entry: Context - An ARPInterface pointer.
+// Type - Type of address (local or p-arp).
+// Address - IP address to be deleted.
+// Mask - Mask for address. Used only for deleting proxy-ARP
+// entries.
+//
+// Returns: 0 if we failed, non-zero otherwise
+//
+uint
+ARPDeleteAddr(void *Context, uint Type, IPAddr Address, IPMask Mask)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ CTELockHandle Handle;
+ ARPIPAddr *DelAddr, *PrevAddr;
+ ARPPArpAddr *DelPAddr, *PrevPAddr;
+
+ if (Type == LLIP_ADDR_LOCAL) {
+ CTEGetLock(&Interface->ai_lock, &Handle);
+
+ if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, Address)) {
+ Interface->ai_ipaddr.aia_addr = NULL_IP_ADDR;
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return TRUE;
+ } else {
+ PrevAddr = STRUCT_OF(ARPIPAddr, &Interface->ai_ipaddr, aia_next);
+ DelAddr = PrevAddr->aia_next;
+ while (DelAddr != NULL)
+ if (IP_ADDR_EQUAL(DelAddr->aia_addr, Address))
+ break;
+ else {
+ PrevAddr = DelAddr;
+ DelAddr = DelAddr->aia_next;
+ }
+
+ if (DelAddr != NULL) {
+ PrevAddr->aia_next = DelAddr->aia_next;
+ CTEFreeMem(DelAddr);
+ }
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return (DelAddr != NULL);
+ }
+ } else if (Type == LLIP_ADDR_PARP) {
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ PrevPAddr = STRUCT_OF(ARPPArpAddr, &Interface->ai_parpaddr, apa_next);
+ DelPAddr = PrevPAddr->apa_next;
+ while (DelPAddr != NULL)
+ if (IP_ADDR_EQUAL(DelPAddr->apa_addr, Address) &&
+ DelPAddr->apa_mask == Mask)
+ break;
+ else {
+ PrevPAddr = DelPAddr;
+ DelPAddr = DelPAddr->apa_next;
+ }
+
+ if (DelPAddr != NULL) {
+ PrevPAddr->apa_next = DelPAddr->apa_next;
+ Interface->ai_parpcount--;
+ CTEFreeMem(DelPAddr);
+ }
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return (DelPAddr != NULL);
+ } else
+ if (Type == LLIP_ADDR_MCAST)
+ return ARPDelMCast(Interface, Address);
+ else
+ return FALSE;
+}
+
+//* ARPTimeout - ARP timeout routine.
+//
+// This is the timeout routine that is called periodically. We scan the ARP table, looking
+// for invalid entries that can be removed.
+//
+// Entry: Timer - Pointer to the timer that just fired.
+// Context - Pointer to the interface to be timed out.
+//
+// Returns: Nothing.
+//
+void
+ARPTimeout(CTEEvent *Timer, void *Context)
+{
+ ARPInterface *Interface = (ARPInterface *)Context; // Our interface.
+ ARPTable *Table;
+ ARPTableEntry *Current, *Previous;
+ int i; // Index variable.
+ ulong Now = CTESystemUpTime(), ValidTime;
+ CTELockHandle tblhandle, entryhandle;
+ uchar Deleted;
+ PNDIS_PACKET PList = (PNDIS_PACKET)NULL;
+ ARPIPAddr *Addr;
+
+ // Walk down the list of addresses, decrementing the age.
+ CTEGetLock(&Interface->ai_lock, &tblhandle);
+
+ Addr = &Interface->ai_ipaddr;
+
+ do {
+ if (Addr->aia_age != ARPADDR_OLD_LOCAL) {
+ (Addr->aia_age)--;
+ if (Addr->aia_age == ARPADDR_OLD_LOCAL) {
+ if (Addr->aia_context != NULL) {
+ SetAddrControl *SAC;
+ SetAddrRtn Rtn;
+
+ SAC = (SetAddrControl *)Addr->aia_context;
+ Rtn = (SetAddrRtn)SAC->sac_rtn;
+ CTEFreeLock(&Interface->ai_lock, tblhandle);
+ (*Rtn)(SAC, IP_SUCCESS);
+ CTEGetLock(&Interface->ai_lock, &tblhandle);
+ Addr->aia_context = NULL;
+ }
+ } else {
+ CTEFreeLock(&Interface->ai_lock, tblhandle);
+ SendARPRequest(Interface, Addr->aia_addr, ARP_RESOLVING_GLOBAL,
+ NULL, TRUE);
+ CTEGetLock(&Interface->ai_lock, &tblhandle);
+ }
+ }
+
+ Addr = Addr->aia_next;
+ } while (Addr != NULL);
+
+ CTEFreeLock(&Interface->ai_lock, tblhandle);
+
+ // Loop through the ARP table for this interface, and delete stale entries.
+ CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle);
+ Table = Interface->ai_ARPTbl;
+ for (i = 0; i < ARP_TABLE_SIZE;i++) {
+ Previous = (ARPTableEntry *)((uchar *)&((*Table)[i]) - offsetof(struct ARPTableEntry, ate_next));
+ Current = (*Table)[i];
+ while (Current != (ARPTableEntry *)NULL) {
+ CTEGetLock(&Current->ate_lock, &entryhandle);
+ Deleted = 0;
+
+ if (Current->ate_state == ARP_GOOD) {
+ //
+ // The ARP entry is valid for ARP_VALID_TIMEOUT by default.
+ // If a cache life greater than ARP_VALID_TIMEOUT has been
+ // configured, we'll make the entry valid for that time.
+ //
+ ValidTime = ArpCacheLife * ARP_TIMER_TIME;
+
+ if (ValidTime < ARP_MIN_VALID_TIMEOUT) {
+ ValidTime = ARP_MIN_VALID_TIMEOUT;
+ }
+ }
+ else {
+ ValidTime = ARP_RESOLVE_TIMEOUT;
+ }
+
+ if (Current->ate_valid != ALWAYS_VALID &&
+ ( ((Now - Current->ate_valid) > ValidTime) ||
+ (Current->ate_state == ARP_GOOD &&
+ !(--(Current->ate_useticks))))) {
+
+ if (Current->ate_state != ARP_RESOLVING_LOCAL) {
+ // Really need to delete this guy.
+ PNDIS_PACKET Packet = Current->ate_packet;
+
+ if (Packet != (PNDIS_PACKET)NULL) {
+ ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_link
+ = PList;
+ PList = Packet;
+ }
+ RemoveARPTableEntry(Previous, Current);
+ Interface->ai_count--;
+ Deleted = 1;
+ } else {
+ IPAddr Dest = Current->ate_dest;
+ // This entry is only resoving locally, presumably this is
+ // token ring. We'll need to transmit a 'global' resolution
+ // now.
+ CTEAssert(Interface->ai_media == NdisMedium802_5);
+
+ Now = CTESystemUpTime();
+ Current->ate_valid = Now;
+ Current->ate_state = ARP_RESOLVING_GLOBAL;
+ CTEFreeLock(&Current->ate_lock, entryhandle);
+ CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle);
+ // Send a global request.
+ SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL,
+ NULL, TRUE);
+ CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle);
+
+ // Since we've freed the locks, we need to start over from
+ // the start of this chain.
+ Previous = STRUCT_OF(ARPTableEntry, &((*Table)[i]),
+ ate_next);
+ Current = (*Table)[i];
+ continue;
+
+ }
+ }
+
+ // If we deleted the entry, leave the previous pointer alone, advance the
+ // current pointer, and free the memory. Otherwise move both pointers forward.
+ // We can free the entry lock now because the next pointers are protected by
+ // the table lock, and we've removed it from the list so nobody else should
+ // find it anyway.
+ CTEFreeLock(&Current->ate_lock, entryhandle);
+ if (Deleted) {
+ ARPTableEntry *Temp = Current;
+ Current = Current->ate_next;
+ CTEFreeMem(Temp);
+ } else {
+ Previous = Current;
+ Current = Current->ate_next;
+ }
+ }
+ }
+
+ CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle);
+
+ while (PList != (PNDIS_PACKET)NULL) {
+ PNDIS_PACKET Packet = PList;
+
+ PList = ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_link;
+ IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS);
+ }
+
+ CTEStartTimer(&Interface->ai_timer, ARP_TIMER_TIME, ARPTimeout, Interface);
+}
+
+//* IsLocalAddr - Return info. about local status of address.
+//
+// Called when we need info. about whether or not a particular address is
+// local. We return info about whether or not it is, and if it is how old
+// it is.
+//
+// Entry: Interface - Pointer to interface structure to be searched.
+// Address - Address in question.
+//
+// Returns: ARPADDR_*, for how old it is.
+//
+//
+uint
+IsLocalAddr(ARPInterface *Interface, IPAddr Address)
+{
+ CTELockHandle Handle;
+ ARPIPAddr *CurrentAddr;
+ uint Age;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+
+ CurrentAddr = &Interface->ai_ipaddr;
+ Age = ARPADDR_NOT_LOCAL;
+
+ do {
+ if (CurrentAddr->aia_addr == Address) {
+ Age = CurrentAddr->aia_age;
+ break;
+ }
+ CurrentAddr = CurrentAddr->aia_next;
+ } while (CurrentAddr != NULL);
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return Age;
+}
+
+//* ARPLocalAddr - Determine whether or not a given address if local.
+//
+// This routine is called when we receive an incoming packet and need to determine whether
+// or not it's local. We look up the provided address on the specified interface.
+//
+// Entry: Interface - Pointer to interface structure to be searched.
+// Address - Address in question.
+//
+// Returns: TRUE if it is a local address, FALSE if it's not.
+//
+uchar
+ARPLocalAddr(ARPInterface *Interface, IPAddr Address)
+{
+ CTELockHandle Handle;
+ ARPPArpAddr *CurrentPArp;
+ IPMask Mask, NetMask;
+ IPAddr MatchAddress;
+
+ // First, see if he's a local (not-proxy) address.
+ if (IsLocalAddr(Interface, Address) != ARPADDR_NOT_LOCAL)
+ return TRUE;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+
+ // Didn't find him in out local address list. See if he exists on our
+ // proxy ARP list.
+ for (CurrentPArp = Interface->ai_parpaddr; CurrentPArp != NULL;
+ CurrentPArp = CurrentPArp->apa_next) {
+ // See if this guy matches.
+ Mask = CurrentPArp->apa_mask;
+ MatchAddress = Address & Mask;
+ if (IP_ADDR_EQUAL(CurrentPArp->apa_addr, MatchAddress)) {
+ // He matches. We need to make a few more checks to make sure
+ // we don't reply to a broadcast address.
+ if (Mask == HOST_MASK) {
+ // We're matching the whole address, so it's OK.
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return TRUE;
+ }
+ // See if the non-mask part it all-zeros. Since the mask presumably
+ // covers a subnet, this trick will prevent us from replying to
+ // a zero host part.
+ if (IP_ADDR_EQUAL(MatchAddress, Address))
+ continue;
+
+ // See if the host part is all ones.
+ if (IP_ADDR_EQUAL(Address, MatchAddress | (IP_LOCAL_BCST & ~Mask)))
+ continue;
+
+ // If the mask we were given is not the net mask for this address,
+ // we'll need to repeat the above checks.
+ NetMask = IPNetMask(Address);
+ if (NetMask != Mask) {
+
+ MatchAddress = Address & NetMask;
+ if (IP_ADDR_EQUAL(MatchAddress, Address))
+ continue;
+
+ if (IP_ADDR_EQUAL(Address, MatchAddress |
+ (IP_LOCAL_BCST & ~NetMask)))
+ continue;
+ }
+
+ // If we get to this point we've passed all the tests, so it's
+ // local.
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return TRUE;
+ }
+ }
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return FALSE;
+
+}
+
+
+#ifdef VXD
+
+
+#ifndef CHICAGO
+extern void DisplayPopup(uchar *Msg);
+uchar CMsg1[] = "The system has detected a conflict for IP address ";
+uchar CMsg2[] = " with the system having hardware address ";
+uchar CMsg3[] = ". The local interface has been disabled";
+
+uchar CMsg[sizeof(CMsg1) - 1 + sizeof(CMsg2) - 1 + sizeof(CMsg3) - 1 +
+ ((sizeof(IPAddr) * 4) - 1) + ((ARP_802_ADDR_LENGTH * 3) - 1)
+ + 1 + 1];
+#else
+extern void NotifyConflictProc(CTEEvent *Event, void *Context);
+extern void DisplayConflictPopup(uchar *IPAddr, uchar *HWAddr, uint Shutoff);
+#endif // CHICAGO
+
+#endif // NT
+
+//* NotifyConflictProc - Notify the user of an address conflict.
+//
+// Called when we need to notify the user of an address conflict. The
+// exact mechanism is system dependent, but generally involves a popup.
+//
+// Input: Event - Event that fired.
+// Context - Pointer to ARPNotifyStructure.
+//
+// Returns: Nothing.
+//
+void
+#ifndef CHICAGO
+NotifyConflictProc(CTEEvent *Event, void *Context)
+#else
+DisplayConflictProc(void *Context)
+#endif
+{
+#ifdef VXD
+ uchar IPAddrBuffer[(sizeof(IPAddr) * 4)];
+ uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
+ uint i;
+ uint IPAddrCharCount;
+ ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *)Context;
+
+#ifndef CHICAGO
+ uint TotalSize;
+
+ CTEMemCopy(CMsg, CMsg1, sizeof(CMsg1) - 1);
+ TotalSize = sizeof(CMsg1) - 1;
+#endif
+
+ // Convert the IP address into a string.
+ IPAddrCharCount = 0;
+
+ for (i = 0; i < sizeof(IPAddr); i++) {
+ uint CurrentByte;
+
+ CurrentByte = NotifyStruct->ans_addr & 0xff;
+ if (CurrentByte > 99) {
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0';
+ CurrentByte %= 100;
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
+ CurrentByte %= 10;
+ } else if (CurrentByte > 9) {
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
+ CurrentByte %= 10;
+ }
+
+ IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0';
+ if (i != (sizeof(IPAddr) - 1))
+ IPAddrBuffer[IPAddrCharCount++] = '.';
+
+ NotifyStruct->ans_addr >>= 8;
+ }
+
+#ifndef CHICAGO
+ CTEMemCopy(&CMsg[TotalSize], IPAddrBuffer, IPAddrCharCount);
+ TotalSize += IPAddrCharCount;
+
+ CTEMemCopy(&CMsg[TotalSize], CMsg2, sizeof(CMsg2) - 1);
+ TotalSize += sizeof(CMsg2) - 1;
+#else
+ IPAddrBuffer[IPAddrCharCount] = '\0';
+#endif
+
+ for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) {
+ uchar CurrentHalf;
+
+ CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4;
+ HWAddrBuffer[i*3] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' :
+ (CurrentHalf - 10) + 'A');
+ CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f;
+ HWAddrBuffer[(i*3)+1] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' :
+ (CurrentHalf - 10) + 'A');
+ if (i != (NotifyStruct->ans_hwaddrlen - 1))
+ HWAddrBuffer[(i*3)+2] = ':';
+ }
+
+#ifndef CHICAGO
+ CTEMemCopy(&CMsg[TotalSize], HWAddrBuffer,
+ (NotifyStruct->ans_hwaddrlen * 3) - 1);
+ TotalSize += (NotifyStruct->ans_hwaddrlen * 3) - 1;
+
+ if (NotifyStruct->ans_shutoff) {
+ CTEMemCopy(&CMsg[TotalSize], CMsg3, sizeof(CMsg3) - 1);
+ TotalSize += sizeof(CMsg3) - 1;
+ }
+
+ CMsg[TotalSize] = '.';
+ CMsg[TotalSize+1] = '\0';
+
+ DisplayPopup(CMsg);
+#else
+ HWAddrBuffer[((NotifyStruct->ans_hwaddrlen * 3) - 1)] = '\0';
+ DisplayConflictPopup(IPAddrBuffer, HWAddrBuffer, NotifyStruct->ans_shutoff);
+ CTEFreeMem(NotifyStruct);
+#endif
+
+#else // VXD
+
+ ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *)Context;
+ PWCHAR stringList[2];
+ uchar IPAddrBuffer[(sizeof(IPAddr) * 4)];
+ uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
+ WCHAR unicodeIPAddrBuffer[((sizeof(IPAddr) * 4) + 1)];
+ WCHAR unicodeHWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
+ uint i;
+ uint IPAddrCharCount;
+ UNICODE_STRING unicodeString;
+ ANSI_STRING ansiString;
+
+
+ PAGED_CODE();
+
+ //
+ // Convert the IP address into a string.
+ //
+ IPAddrCharCount = 0;
+
+ for (i = 0; i < sizeof(IPAddr); i++) {
+ uint CurrentByte;
+
+ CurrentByte = NotifyStruct->ans_addr & 0xff;
+ if (CurrentByte > 99) {
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0';
+ CurrentByte %= 100;
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
+ CurrentByte %= 10;
+ } else if (CurrentByte > 9) {
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
+ CurrentByte %= 10;
+ }
+
+ IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0';
+ if (i != (sizeof(IPAddr) - 1))
+ IPAddrBuffer[IPAddrCharCount++] = '.';
+
+ NotifyStruct->ans_addr >>= 8;
+ }
+
+ //
+ // Convert the hardware address into a string.
+ //
+ for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) {
+ uchar CurrentHalf;
+
+ CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4;
+ HWAddrBuffer[i*3] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' :
+ (CurrentHalf - 10) + 'A');
+ CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f;
+ HWAddrBuffer[(i*3)+1] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' :
+ (CurrentHalf - 10) + 'A');
+ if (i != (NotifyStruct->ans_hwaddrlen - 1))
+ HWAddrBuffer[(i*3)+2] = ':';
+ }
+
+ //
+ // Unicode the strings.
+ //
+ *unicodeIPAddrBuffer = *unicodeHWAddrBuffer = UNICODE_NULL;
+
+ unicodeString.Buffer = unicodeIPAddrBuffer;
+ unicodeString.Length = 0;
+ unicodeString.MaximumLength = sizeof(WCHAR) * ((sizeof(IPAddr) * 4) + 1);
+ ansiString.Buffer = IPAddrBuffer;
+ ansiString.Length = IPAddrCharCount;
+ ansiString.MaximumLength = IPAddrCharCount;
+
+ RtlAnsiStringToUnicodeString(
+ &unicodeString,
+ &ansiString,
+ FALSE
+ );
+
+ stringList[0] = unicodeIPAddrBuffer;
+
+ unicodeString.Buffer = unicodeHWAddrBuffer;
+ unicodeString.Length = 0;
+ unicodeString.MaximumLength = sizeof(WCHAR) * (ARP_802_ADDR_LENGTH * 3);
+ ansiString.Buffer = HWAddrBuffer;
+ ansiString.Length = (NotifyStruct->ans_hwaddrlen * 3) - 1;
+ ansiString.MaximumLength = NotifyStruct->ans_hwaddrlen * 3;
+
+ RtlAnsiStringToUnicodeString(
+ &unicodeString,
+ &ansiString,
+ FALSE
+ );
+
+ stringList[1] = unicodeHWAddrBuffer;
+
+ //
+ // Kick off a popup and log an event.
+ //
+ if (NotifyStruct->ans_shutoff) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_ADDRESS_CONFLICT1,
+ 0,
+ 2,
+ stringList,
+ 0,
+ NULL
+ );
+
+ IoRaiseInformationalHardError(
+ STATUS_IP_ADDRESS_CONFLICT1,
+ NULL,
+ NULL
+ );
+ }
+ else {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_ADDRESS_CONFLICT2,
+ 0,
+ 2,
+ stringList,
+ 0,
+ NULL
+ );
+
+ IoRaiseInformationalHardError(
+ STATUS_IP_ADDRESS_CONFLICT2,
+ NULL,
+ NULL
+ );
+ }
+
+ CTEFreeMem(NotifyStruct);
+
+#endif // VXD
+
+ return;
+}
+
+
+//* HandleARPPacket - Process an incoming ARP packet.
+//
+// This is the main routine to process an incoming ARP packet. We look at all ARP frames,
+// and update our cache entry for the source address if one exists. Else, if we are the
+// target we create an entry if one doesn't exist. Finally, we'll handle the opcode,
+// responding if this is a request or sending pending packets if this is a response.
+//
+// Entry: Interface - Pointer to interface structure for this adapter.
+// Header - Pointer to header buffer.
+// HeaderSize - Size of header buffer.
+// ARPHdr - ARP packet header.
+// ARPHdrSize - Size of ARP header.
+// ProtOffset - Offset into original data field of arp header.
+// Will be non-zero if we're using SNAP.
+//
+// Returns: An NDIS_STATUS value to be returned to the NDIS driver.
+//
+NDIS_STATUS
+HandleARPPacket(ARPInterface *Interface, void *Header, uint HeaderSize,
+ ARPHeader UNALIGNED *ARPHdr, uint ARPHdrSize, uint ProtOffset)
+{
+ ARPTableEntry *Entry; // Entry in ARP table
+ CTELockHandle LHandle, TableHandle;
+ RC UNALIGNED *SourceRoute = (RC UNALIGNED *)NULL; // Pointer to Source Route info, if any.
+ uint SourceRouteSize = 0;
+ ulong Now = CTESystemUpTime();
+ uchar LocalAddr;
+ uint LocalAddrAge;
+ uchar *SHAddr, *DHAddr;
+ IPAddr UNALIGNED *SPAddr, *DPAddr;
+ ENetHeader *ENetHdr;
+ TRHeader *TRHdr;
+ FDDIHeader *FHdr;
+ ARCNetHeader *AHdr;
+ ushort MaxMTU;
+ uint UseSNAP;
+ SetAddrControl *SAC;
+ SetAddrRtn Rtn = NULL;
+
+ // We examine all ARP frames. If we find the source address in the ARP table, we'll
+ // update the hardware address and set the state to valid. If we're the
+ // target and he's not in the table, we'll add him. Otherwise if we're the
+ // target and this is a response we'll send any pending packets to him.
+ if (Interface->ai_media != NdisMediumArcnet878_2) {
+ if (ARPHdrSize < sizeof(ARPHeader))
+ return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small.
+
+ if (ARPHdr->ah_hw != net_short(ARP_HW_ENET) &&
+ ARPHdr->ah_hw != net_short(ARP_HW_802))
+ return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type
+
+ if (ARPHdr->ah_hlen != ARP_802_ADDR_LENGTH)
+ return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length.
+
+ if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize == 0)
+ UseSNAP = FALSE;
+ else
+ UseSNAP = (ProtOffset != 0);
+
+ // Figure out SR size on TR.
+ if (Interface->ai_media == NdisMedium802_5) {
+ // Check for source route information. SR is present if the header
+ // size is greater than the standard TR header size. If the SR is
+ // only an RC field, we ignore it because it came from the same
+ // ring which is the same as no SR.
+
+ if ((HeaderSize - sizeof(TRHeader)) > sizeof(RC)) {
+ SourceRouteSize = HeaderSize - sizeof(TRHeader);
+ SourceRoute = (RC UNALIGNED *)((uchar *)Header +
+ sizeof(TRHeader));
+ }
+ }
+
+ SHAddr = ARPHdr->ah_shaddr;
+ SPAddr = (IPAddr UNALIGNED *) &ARPHdr->ah_spaddr;
+ DHAddr = ARPHdr->ah_dhaddr;
+ DPAddr = (IPAddr UNALIGNED *) &ARPHdr->ah_dpaddr;
+
+ } else {
+ if (ARPHdrSize < (sizeof(ARPHeader) - ARCNET_ARPHEADER_ADJUSTMENT))
+ return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small.
+
+ if (ARPHdr->ah_hw != net_short(ARP_HW_ARCNET))
+ return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type
+
+ if (ARPHdr->ah_hlen != 1)
+ return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length.
+
+ UseSNAP = FALSE;
+ SHAddr = ARPHdr->ah_shaddr;
+ SPAddr = (IPAddr UNALIGNED *)(SHAddr + 1);
+ DHAddr = (uchar *)SPAddr + sizeof(IPAddr);
+ DPAddr = (IPAddr UNALIGNED *)(DHAddr + 1);
+ }
+
+ if (ARPHdr->ah_pro != net_short(ARP_ETYPE_IP))
+ return NDIS_STATUS_NOT_RECOGNIZED; // Unsupported protocol type.
+
+ if (ARPHdr->ah_plen != sizeof(IPAddr))
+ return NDIS_STATUS_NOT_RECOGNIZED;
+
+ if (IP_ADDR_EQUAL(*SPAddr, NULL_IP_ADDR))
+ return NDIS_STATUS_NOT_RECOGNIZED;
+
+ // First, let's see if we have an address conflict.
+ LocalAddrAge = IsLocalAddr(Interface, *SPAddr);
+ if (LocalAddrAge != ARPADDR_NOT_LOCAL) {
+ // The source IP address is one of ours. See if the source h/w address
+ // is ours also.
+ if (ARPHdr->ah_hlen != Interface->ai_addrlen ||
+ CTEMemCmp(SHAddr, Interface->ai_addr, Interface->ai_addrlen) != 0) {
+
+ uint Shutoff;
+ ARPNotifyStruct *NotifyStruct;
+
+ // This isn't from us; we must have an address conflict somewhere.
+ // We always log an error about this. If what triggered this is a
+ // response and the address in conflict is young, we'll turn off
+ // the interface.
+ if (LocalAddrAge != ARPADDR_OLD_LOCAL &&
+ ARPHdr->ah_opcode == net_short(ARP_RESPONSE)) {
+ // Send an arp request with the owner's address to reset the
+ // caches.
+
+ CTEGetLock(&Interface->ai_lock, &LHandle);
+ Interface->ai_state = INTERFACE_DOWN;
+ Interface->ai_adminstate = IF_STATUS_DOWN;
+ if (Interface->ai_ipaddr.aia_context != NULL) {
+ SAC = (SetAddrControl *)Interface->ai_ipaddr.aia_context;
+ Rtn = (SetAddrRtn)SAC->sac_rtn;
+ Interface->ai_ipaddr.aia_context = NULL;
+ }
+ CTEFreeLock(&Interface->ai_lock, LHandle);
+
+ SendARPRequest(Interface, *SPAddr, ARP_RESOLVING_GLOBAL,
+ SHAddr, FALSE); // Send a request.
+
+ Shutoff = TRUE;
+
+ if (Rtn != NULL) {
+ //
+ // this is a dhcp adapter. report the conflict to
+ // CompleteIPSetNTEAddrRequest so IOCTL_IP_SET_ADDRESS will
+ // be completed
+ //
+
+ (*Rtn)(SAC, IP_GENERAL_FAILURE);
+
+ //
+ // don't display a warning dialog in this case - DHCP will
+ // alert the user
+ //
+
+ goto no_dialog;
+ }
+ } else {
+ if (ARPHdr->ah_opcode == net_short(ARP_REQUEST) &&
+ (IsLocalAddr(Interface, *DPAddr) != ARPADDR_NOT_LOCAL)) {
+ // Send a response.
+ SendARPReply(Interface, *SPAddr, *DPAddr, SHAddr,
+ SourceRoute, SourceRouteSize, UseSNAP);
+ }
+ Shutoff = FALSE;
+ }
+
+ // Now allocate a structure, and schedule an event to notify
+ // the user.
+ NotifyStruct = CTEAllocMem(offsetof(ARPNotifyStruct, ans_hwaddr) +
+ ARPHdr->ah_hlen);
+ if (NotifyStruct != NULL) {
+ NotifyStruct->ans_addr = *SPAddr;
+ NotifyStruct->ans_shutoff = Shutoff;
+ NotifyStruct->ans_hwaddrlen = (uint)ARPHdr->ah_hlen;
+ CTEMemCopy(NotifyStruct->ans_hwaddr, SHAddr,
+ ARPHdr->ah_hlen);
+ CTEInitEvent(&NotifyStruct->ans_event, NotifyConflictProc);
+ CTEScheduleEvent(&NotifyStruct->ans_event, NotifyStruct);
+ }
+
+
+ no_dialog:
+ ;
+
+ }
+ return NDIS_STATUS_NOT_RECOGNIZED;
+ }
+
+ CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle);
+ MaxMTU = Interface->ai_mtu;
+
+ LocalAddr = ARPLocalAddr(Interface, *DPAddr);
+ Entry = ARPLookup(Interface, *SPAddr, &LHandle);
+ if (Entry == (ARPTableEntry *)NULL) {
+
+ // Didn't find him, create one if it's for us. The call to ARPLookup
+ // returned with the ARPTblLock held, so we need to free it.
+
+ CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle);
+ if (LocalAddr)
+ Entry = CreateARPTableEntry(Interface, *SPAddr, &LHandle);
+ else
+ return NDIS_STATUS_NOT_RECOGNIZED; // Not in our table, and not for us.
+ } else {
+ CTEFreeLock(&Interface->ai_ARPTblLock, LHandle);
+ LHandle = TableHandle;
+ }
+
+ // At this point, entry should be valid, and we hold the lock on the entry
+ // in LHandle.
+
+ if (Entry != (ARPTableEntry *)NULL) {
+ PNDIS_PACKET Packet; // Packet to be sent.
+
+ // If the entry is already static, we'll want to leave it as static.
+ if (Entry->ate_valid == ALWAYS_VALID)
+ Now = ALWAYS_VALID;
+
+ // OK, we have an entry to use, and hold the lock on it. Fill in the
+ // required fields.
+ switch (Interface->ai_media) {
+
+ case NdisMedium802_3:
+
+ // This is an Ethernet.
+ ENetHdr = (ENetHeader *)Entry->ate_addr;
+
+ CTEMemCopy(ENetHdr->eh_daddr, SHAddr, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(ENetHdr->eh_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+ ENetHdr->eh_type = net_short(ARP_ETYPE_IP);
+
+ // If we're using SNAP on this entry, copy in the SNAP header.
+ if (UseSNAP) {
+ CTEMemCopy(&Entry->ate_addr[sizeof(ENetHeader)], ARPSNAP,
+ sizeof(SNAPHeader));
+ Entry->ate_addrlength = (uchar)(sizeof(ENetHeader) +
+ sizeof(SNAPHeader));
+ *(ushort UNALIGNED *)&Entry->ate_addr[Entry->ate_addrlength-2] =
+ net_short(ARP_ETYPE_IP);
+ } else
+ Entry->ate_addrlength = sizeof(ENetHeader);
+
+ Entry->ate_state = ARP_GOOD;
+ Entry->ate_valid = Now; // Mark last time he was
+ // valid.
+ break;
+
+ case NdisMedium802_5:
+
+ // This is TR.
+ // For token ring we have to deal with source routing. There's
+ // a special case to handle multiple responses for an all-routes
+ // request - if the entry is currently good and we knew it was
+ // valid recently, we won't update the entry.
+
+
+ if (Entry->ate_state != ARP_GOOD ||
+ (Now - Entry->ate_valid) > ARP_RESOLVE_TIMEOUT) {
+
+ TRHdr = (TRHeader *)Entry->ate_addr;
+
+ // We need to update a TR entry.
+ TRHdr->tr_ac = ARP_AC;
+ TRHdr->tr_fc = ARP_FC;
+ CTEMemCopy(TRHdr->tr_daddr, SHAddr, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(TRHdr->tr_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+ if (SourceRoute != (RC UNALIGNED *)NULL) {
+ uchar MaxIFieldBits;
+
+ // We have source routing information.
+ CTEMemCopy(&Entry->ate_addr[sizeof(TRHeader)],
+ SourceRoute, SourceRouteSize);
+ MaxIFieldBits = (SourceRoute->rc_dlf & RC_LF_MASK) >>
+ LF_BIT_SHIFT;
+ MaxIFieldBits = MIN(MaxIFieldBits, MAX_LF_BITS);
+ MaxMTU = IFieldSize[MaxIFieldBits];
+
+ // The new MTU we've computed is the max I-field size,
+ // which doesn't include source routing info but
+ // does include SNAP info. Subtract off the SNAP size.
+ MaxMTU -= sizeof(SNAPHeader);
+
+ TRHdr->tr_saddr[0] |= TR_RII;
+ (*(RC UNALIGNED *)&Entry->ate_addr[sizeof(TRHeader)]).rc_dlf ^=
+ RC_DIR;
+ // Make sure it's non-broadcast.
+ (*(RC UNALIGNED *)&Entry->ate_addr[sizeof(TRHeader)]).rc_blen &=
+ RC_LENMASK;
+
+ }
+ CTEMemCopy(&Entry->ate_addr[sizeof(TRHeader)+SourceRouteSize],
+ ARPSNAP, sizeof(SNAPHeader));
+ Entry->ate_state = ARP_GOOD;
+ Entry->ate_valid = Now;
+ Entry->ate_addrlength = (uchar)(sizeof(TRHeader) +
+ SourceRouteSize + sizeof(SNAPHeader));
+ *(ushort *)&Entry->ate_addr[Entry->ate_addrlength-2] =
+ net_short(ARP_ETYPE_IP);
+ }
+ break;
+ case NdisMediumFddi:
+ FHdr = (FDDIHeader *)Entry->ate_addr;
+
+ FHdr->fh_pri = ARP_FDDI_PRI;
+ CTEMemCopy(FHdr->fh_daddr, SHAddr, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(FHdr->fh_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+ CTEMemCopy(&Entry->ate_addr[sizeof(FDDIHeader)], ARPSNAP,
+ sizeof(SNAPHeader));
+ Entry->ate_addrlength = (uchar)(sizeof(FDDIHeader) +
+ sizeof(SNAPHeader));
+ *(ushort UNALIGNED *)&Entry->ate_addr[Entry->ate_addrlength-2] =
+ net_short(ARP_ETYPE_IP);
+ Entry->ate_state = ARP_GOOD;
+ Entry->ate_valid = Now; // Mark last time he was
+ // valid.
+ break;
+ case NdisMediumArcnet878_2:
+ AHdr = (ARCNetHeader *)Entry->ate_addr;
+ AHdr->ah_saddr = Interface->ai_addr[0];
+ AHdr->ah_daddr = *SHAddr;
+ AHdr->ah_prot = ARP_ARCPROT_IP;
+ Entry->ate_addrlength = sizeof(ARCNetHeader);
+ Entry->ate_state = ARP_GOOD;
+ Entry->ate_valid = Now; // Mark last time he was
+ // valid.
+ break;
+ default:
+ DEBUGCHK;
+ break;
+ }
+
+ // At this point we've updated the entry, and we still hold the lock
+ // on it. If we have a packet that was pending to be sent, send it now.
+ // Otherwise just free the lock.
+
+ Packet = Entry->ate_packet;
+
+ if (Packet != NULL) {
+ // We have a packet to send.
+ CTEAssert(Entry->ate_state == ARP_GOOD);
+
+ Entry->ate_packet = NULL;
+
+ if (ARPSendData(Interface, Packet, Entry, LHandle) != NDIS_STATUS_PENDING)
+ IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS);
+ } else
+ CTEFreeLock(&Entry->ate_lock, LHandle);
+ }
+
+ // See if the MTU is less than our local one. This should only happen
+ // in the case of token ring source routing.
+ if (MaxMTU < Interface->ai_mtu) {
+ LLIPAddrMTUChange LAM;
+
+ LAM.lam_mtu = MaxMTU;
+ LAM.lam_addr = *SPAddr;
+
+ // It is less. Notify IP.
+ CTEAssert(Interface->ai_media == NdisMedium802_5);
+ IPStatus(Interface->ai_context, LLIP_STATUS_ADDR_MTU_CHANGE,
+ &LAM, sizeof(LLIPAddrMTUChange));
+
+ }
+
+ // At this point we've updated the entry (if we had one), and we've free
+ // all locks. If it's for a local address and it's a request, reply to
+ // it.
+ if (LocalAddr) { // It's for us.
+ if (ARPHdr->ah_opcode == net_short(ARP_REQUEST)) {
+ // It's a request, and we need to respond.
+ SendARPReply(Interface, *SPAddr, *DPAddr,
+ SHAddr, SourceRoute, SourceRouteSize, UseSNAP);
+ }
+ }
+
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+
+//* InitAdapter - Initialize an adapter.
+//
+// Called when an adapter is open to finish initialization. We set
+// up our lookahead size and packet filter, and we're ready to go.
+//
+// Entry:
+// adapter - Pointer to an adapter structure for the adapter to be
+// initialized.
+//
+// Exit: Nothing
+//
+void
+InitAdapter(ARPInterface *Adapter)
+{
+ NDIS_STATUS Status;
+ CTELockHandle Handle;
+ ARPIPAddr *Addr, *OldAddr;
+
+ if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation,
+ OID_GEN_CURRENT_LOOKAHEAD, &ARPLookahead, sizeof(ARPLookahead), NULL))
+ != NDIS_STATUS_SUCCESS) {
+ Adapter->ai_state = INTERFACE_DOWN;
+ return;
+ }
+
+ if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation,
+ OID_GEN_CURRENT_PACKET_FILTER, &Adapter->ai_pfilter, sizeof(uint),
+ NULL)) == NDIS_STATUS_SUCCESS) {
+ Adapter->ai_adminstate = IF_STATUS_UP;
+ Adapter->ai_operstate = IF_STATUS_UP;
+ Adapter->ai_state = INTERFACE_UP;
+ // Now walk through any addresses we have, and ARP for them.
+ CTEGetLock(&Adapter->ai_lock, &Handle);
+ OldAddr = NULL;
+ Addr = &Adapter->ai_ipaddr;
+ do {
+ if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) {
+ IPAddr Address = Addr->aia_addr;
+
+ Addr->aia_age = ARPADDR_NEW_LOCAL;
+ CTEFreeLock(&Adapter->ai_lock, Handle);
+ OldAddr = Addr;
+ SendARPRequest(Adapter, Address, ARP_RESOLVING_GLOBAL,
+ NULL, TRUE);
+ CTEGetLock(&Adapter->ai_lock, &Handle);
+ }
+ Addr = &Adapter->ai_ipaddr;
+ while (Addr != OldAddr && Addr != NULL)
+ Addr = Addr->aia_next;
+ if (Addr != NULL)
+ Addr = Addr->aia_next;
+ } while (Addr != NULL);
+
+ CTEFreeLock(&Adapter->ai_lock, Handle);
+
+ } else
+ Adapter->ai_state = INTERFACE_DOWN;
+
+}
+
+//** ARPOAComplete - ARP Open adapter complete handler.
+//
+// This routine is called by the NDIS driver when an open adapter
+// call completes. Presumably somebody is blocked waiting for this, so
+// we'll wake him up now.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Status - Final status of command.
+// ErrorStatus - Final error status.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPOAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status, NDIS_STATUS ErrorStatus)
+{
+ ARPInterface *ai = (ARPInterface *)Handle; // For compiler.
+
+ CTESignal(&ai->ai_block, (uint)Status); // Wake him up, and return status.
+
+}
+
+//** ARPCAComplete - ARP close adapter complete handler.
+//
+// This routine is called by the NDIS driver when a close adapter
+// call completes.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Status - Final status of command.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPCAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status)
+{
+ ARPInterface *ai = (ARPInterface *)Handle; // For compiler.
+
+ CTESignal(&ai->ai_block, (uint)Status); // Wake him up, and return status.
+
+}
+
+//** ARPSendComplete - ARP send complete handler.
+//
+// This routine is called by the NDIS driver when a send completes.
+// This is a pretty time critical operation, we need to get through here
+// quickly. We'll strip our buffer off and put it back, and call the upper
+// later send complete handler.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Packet - A pointer to the packet that was sent.
+// Status - Final status of command.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPSendComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status)
+{
+ ARPInterface *Interface = (ARPInterface *)Handle;
+ PacketContext *PC = (PacketContext *)Packet->ProtocolReserved;
+ PNDIS_BUFFER Buffer;
+
+ Interface->ai_qlen--;
+#ifdef VXD
+ CTEAssert(*(int *)&Interface->ai_qlen >= 0);
+#endif
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+ Interface->ai_outoctets += Packet->Private.TotalLength;
+ } else {
+ if (Status == NDIS_STATUS_RESOURCES)
+ Interface->ai_outdiscards++;
+ else
+ Interface->ai_outerrors++;
+ }
+
+ // Get first buffer on packet.
+ NdisUnchainBufferAtFront(Packet, &Buffer);
+#ifdef DEBUG
+ if (Buffer == (PNDIS_BUFFER)NULL) // No buffer!
+ DEBUGCHK;
+#endif
+
+ FreeARPBuffer(Interface, Buffer); // Free it up.
+ if (PC->pc_common.pc_owner != PACKET_OWNER_LINK) { // We don't own this one.
+ IPSendComplete(Interface->ai_context, Packet, Status);
+ return;
+ }
+
+ // This packet belongs to us, so free it.
+ NdisFreePacket(Packet);
+
+}
+
+//** ARPTDComplete - ARP transfer data complete handler.
+//
+// This routine is called by the NDIS driver when a transfer data
+// call completes. Since we never transfer data ourselves, this must be
+// from the upper layer. We'll just call his routine and let him deal
+// with it.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Packet - A pointer to the packet used for the TD.
+// Status - Final status of command.
+// BytesCopied - Count of bytes copied.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPTDComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status,
+ uint BytesCopied)
+{
+ ARPInterface *ai = (ARPInterface *)Handle;
+
+ IPTDComplete(ai->ai_context, Packet, Status, BytesCopied);
+
+}
+
+//** ARPResetComplete - ARP reset complete handler.
+//
+// This routine is called by the NDIS driver when a reset completes.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Status - Final status of command.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPResetComplete(NDIS_HANDLE Handle, NDIS_STATUS Status)
+{
+}
+
+//** ARPRequestComplete - ARP request complete handler.
+//
+// This routine is called by the NDIS driver when a general request
+// completes. ARP blocks on all requests, so we'll just wake up
+// whoever's blocked on this request.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Request - A pointer to the request that completed.
+// Status - Final status of command.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPRequestComplete(NDIS_HANDLE Handle, PNDIS_REQUEST Request,
+ NDIS_STATUS Status)
+{
+ ARPInterface *ai = (ARPInterface *)Handle;
+
+ CTESignal(&ai->ai_block, (uint)Status);
+}
+
+//** ARPRcv - ARP receive data handler.
+//
+// This routine is called when data arrives from the NDIS driver.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Context - NDIS context to be used for TD.
+// Header - Pointer to header
+// HeaderSize - Size of header
+// Data - Pointer to buffer of received data
+// Size - Byte count of data in buffer.
+// TotalSize - Byte count of total packet size.
+//
+// Exit: Status indicating whether or not we took the packet.
+//
+NDIS_STATUS NDIS_API
+ARPRcv(NDIS_HANDLE Handle, NDIS_HANDLE Context, void *Header, uint HeaderSize,
+ void *Data, uint Size, uint TotalSize)
+{
+ ARPInterface *Interface = Handle; // Interface for this driver.
+ ENetHeader UNALIGNED *EHdr = (ENetHeader UNALIGNED *)Header;
+ SNAPHeader UNALIGNED *SNAPHdr;
+ ushort type; // Protocol type
+ uint ProtOffset; // Offset in Data to non-media info.
+ uint NUCast; // TRUE if the frame is not
+ // a unicast frame.
+
+ if (Interface->ai_state == INTERFACE_UP &&
+ HeaderSize >= (uint)Interface->ai_hdrsize) {
+
+ Interface->ai_inoctets += TotalSize;
+
+ if (Interface->ai_media != NdisMediumArcnet878_2) {
+ if (Interface->ai_media == NdisMedium802_3 &&
+ (type = net_short(EHdr->eh_type)) >= MIN_ETYPE)
+ ProtOffset = 0;
+ else {
+ SNAPHdr = (SNAPHeader UNALIGNED *)Data;
+
+ if (Size >= sizeof(SNAPHeader) &&
+ SNAPHdr->sh_dsap == SNAP_SAP &&
+ SNAPHdr->sh_ssap == SNAP_SAP &&
+ SNAPHdr->sh_ctl == SNAP_UI) {
+ type = net_short(SNAPHdr->sh_etype);
+ ProtOffset = sizeof(SNAPHeader);
+ } else {
+ // BUGBUG handle XID/TEST here.
+ Interface->ai_uknprotos++;
+ return NDIS_STATUS_NOT_RECOGNIZED;
+ }
+ }
+ } else {
+ ARCNetHeader UNALIGNED *AH = (ARCNetHeader UNALIGNED *)Header;
+
+ ProtOffset = 0;
+ if (AH->ah_prot == ARP_ARCPROT_IP)
+ type = ARP_ETYPE_IP;
+ else
+ if (AH->ah_prot == ARP_ARCPROT_ARP)
+ type = ARP_ETYPE_ARP;
+ else
+ type = 0;
+ }
+
+ NUCast = ((*((uchar UNALIGNED *)EHdr + Interface->ai_bcastoff) &
+ Interface->ai_bcastmask) == Interface->ai_bcastval) ?
+ AI_NONUCAST_INDEX : AI_UCAST_INDEX;
+
+ if (type == ARP_ETYPE_IP) {
+
+ (Interface->ai_inpcount[NUCast])++;
+ IPRcv(Interface->ai_context, (uchar *)Data+ProtOffset,
+ Size-ProtOffset, TotalSize-ProtOffset, Context, ProtOffset,
+ NUCast);
+ return NDIS_STATUS_SUCCESS;
+ }
+ else {
+ if (type == ARP_ETYPE_ARP) {
+ (Interface->ai_inpcount[NUCast])++;
+ return HandleARPPacket(Interface, Header, HeaderSize,
+ (ARPHeader *)((uchar *)Data+ProtOffset), Size-ProtOffset,
+ ProtOffset);
+ } else {
+ Interface->ai_uknprotos++;
+ return NDIS_STATUS_NOT_RECOGNIZED;
+ }
+ }
+ } else {
+ // Interface is marked as down.
+ return NDIS_STATUS_NOT_RECOGNIZED;
+ }
+}
+
+//** ARPRcvComplete - ARP receive complete handler.
+//
+// This routine is called by the NDIS driver after some number of
+// receives. In some sense, it indicates 'idle time'.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPRcvComplete(NDIS_HANDLE Handle)
+{
+ IPRcvComplete();
+
+}
+
+
+//** ARPStatus - ARP status handler.
+//
+// Called by the NDIS driver when some sort of status change occurs.
+// We take action depending on the type of status.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// GStatus - General type of status that caused the call.
+// Status - Pointer to a buffer of status specific information.
+// StatusSize - Size of the status buffer.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPStatus(NDIS_HANDLE Handle, NDIS_STATUS GStatus, void *Status, uint
+ StatusSize)
+{
+ ARPInterface *ai = (ARPInterface *)Handle;
+
+ IPStatus(ai->ai_context, GStatus, Status, StatusSize);
+
+}
+
+//** ARPStatusComplete - ARP status complete handler.
+//
+// A routine called by the NDIS driver so that we can do postprocessing
+// after a status event.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPStatusComplete(NDIS_HANDLE Handle)
+{
+
+}
+
+extern void NDIS_API ARPBindAdapter(PNDIS_STATUS RetStatus,
+ NDIS_HANDLE BindContext, PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2);
+
+extern void NDIS_API ARPUnbindAdapter(PNDIS_STATUS RetStatus,
+ NDIS_HANDLE ProtBindContext, NDIS_HANDLE UnbindContext);
+extern void NDIS_API ARPUnloadProtocol(void);
+
+#ifndef NT
+NDIS_PROTOCOL_CHARACTERISTICS ARPCharacteristics = {
+ NDIS_MAJOR_VERSION,
+ NDIS_MINOR_VERSION,
+ 0,
+ ARPOAComplete,
+ ARPCAComplete,
+ ARPSendComplete,
+ ARPTDComplete,
+ ARPResetComplete,
+ ARPRequestComplete,
+ ARPRcv,
+ ARPRcvComplete,
+ ARPStatus,
+ ARPStatusComplete,
+#ifdef CHICAGO
+ ARPBindAdapter,
+ ARPUnbindAdapter,
+ ARPUnloadProtocol,
+#endif
+ { sizeof(TCP_NAME),
+ sizeof(TCP_NAME),
+ 0
+ }
+};
+#else // NT
+NDIS_PROTOCOL_CHARACTERISTICS ARPCharacteristics = {
+ NDIS_MAJOR_VERSION,
+ NDIS_MINOR_VERSION,
+ 0,
+ ARPOAComplete,
+ ARPCAComplete,
+ ARPSendComplete,
+ ARPTDComplete,
+ ARPResetComplete,
+ ARPRequestComplete,
+ ARPRcv,
+ ARPRcvComplete,
+ ARPStatus,
+ ARPStatusComplete,
+ { sizeof(TCP_NAME),
+ sizeof(TCP_NAME),
+ 0
+#ifdef _PNP_POWER
+ },
+ NULL,
+ ARPBindAdapter,
+ ARPUnbindAdapter,
+ NULL
+#else
+ }
+#endif
+};
+#endif
+
+//* ARPReadNext - Read the next entry in the ARP table.
+//
+// Called by the GetInfo code to read the next ATE in the table. We assume
+// the context passed in is valid, and the caller has the ARP TableLock.
+//
+// Input: Context - Pointer to a IPNMEContext.
+// Interface - Pointer to interface for table to read on.
+// Buffer - Pointer to an IPNetToMediaEntry structure.
+//
+// Returns: TRUE if more data is available to be read, FALSE is not.
+//
+uint
+ARPReadNext(void *Context, ARPInterface *Interface, void *Buffer)
+{
+ IPNMEContext *NMContext = (IPNMEContext *)Context;
+ IPNetToMediaEntry *IPNMEntry = (IPNetToMediaEntry *)Buffer;
+ CTELockHandle Handle;
+ ARPTableEntry *CurrentATE;
+ uint i;
+ ARPTable *Table = Interface->ai_ARPTbl;
+ uint AddrOffset;
+
+ CurrentATE = NMContext->inc_entry;
+
+ // Fill in the buffer.
+ CTEGetLock(&CurrentATE->ate_lock, &Handle);
+ IPNMEntry->inme_index = Interface->ai_index;
+ IPNMEntry->inme_physaddrlen = Interface->ai_addrlen;
+
+
+ switch (Interface->ai_media) {
+ case NdisMedium802_3:
+ AddrOffset = 0;
+ break;
+ case NdisMedium802_5:
+ AddrOffset = offsetof(struct TRHeader, tr_daddr);
+ break;
+ case NdisMediumFddi:
+ AddrOffset = offsetof(struct FDDIHeader, fh_daddr);
+ break;
+ case NdisMediumArcnet878_2:
+ AddrOffset = offsetof(struct ARCNetHeader, ah_daddr);
+ break;
+ default:
+ AddrOffset = 0;
+ break;
+ }
+
+ CTEMemCopy(IPNMEntry->inme_physaddr, &CurrentATE->ate_addr[AddrOffset],
+ Interface->ai_addrlen);
+ IPNMEntry->inme_addr = CurrentATE->ate_dest;
+
+ if (CurrentATE->ate_state == ARP_GOOD)
+ IPNMEntry->inme_type = (CurrentATE->ate_valid == ALWAYS_VALID ?
+ INME_TYPE_STATIC : INME_TYPE_DYNAMIC);
+ else
+ IPNMEntry->inme_type = INME_TYPE_INVALID;
+ CTEFreeLock(&CurrentATE->ate_lock, Handle);
+
+ // We've filled it in. Now update the context.
+ if (CurrentATE->ate_next != NULL) {
+ NMContext->inc_entry = CurrentATE->ate_next;
+ return TRUE;
+ } else {
+ // The next ATE is NULL. Loop through the ARP Table looking for a new
+ // one.
+ i = NMContext->inc_index + 1;
+ while (i < ARP_TABLE_SIZE) {
+ if ((*Table)[i] != NULL) {
+ NMContext->inc_entry = (*Table)[i];
+ NMContext->inc_index = i;
+ return TRUE;
+ break;
+ } else
+ i++;
+ }
+
+ NMContext->inc_index = 0;
+ NMContext->inc_entry = NULL;
+ return FALSE;
+ }
+
+}
+
+//* ARPValidateContext - Validate the context for reading an ARP table.
+//
+// Called to start reading an ARP table sequentially. We take in
+// a context, and if the values are 0 we return information about the
+// first route in the table. Otherwise we make sure that the context value
+// is valid, and if it is we return TRUE.
+// We assume the caller holds the ARPInterface lock.
+//
+// Input: Context - Pointer to a RouteEntryContext.
+// Interface - Pointer to an interface
+// Valid - Where to return information about context being
+// valid.
+//
+// Returns: TRUE if more data to be read in table, FALSE if not. *Valid set
+// to TRUE if input context is valid
+//
+uint
+ARPValidateContext(void *Context, ARPInterface *Interface, uint *Valid)
+{
+ IPNMEContext *NMContext = (IPNMEContext *)Context;
+ uint i;
+ ARPTableEntry *TargetATE;
+ ARPTableEntry *CurrentATE;
+ ARPTable *Table = Interface->ai_ARPTbl;
+
+ i = NMContext->inc_index;
+ TargetATE = NMContext->inc_entry;
+
+ // If the context values are 0 and NULL, we're starting from the beginning.
+ if (i == 0 && TargetATE == NULL) {
+ *Valid = TRUE;
+ do {
+ if ((CurrentATE = (*Table)[i]) != NULL) {
+ break;
+ }
+ i++;
+ } while (i < ARP_TABLE_SIZE);
+
+ if (CurrentATE != NULL) {
+ NMContext->inc_index = i;
+ NMContext->inc_entry = CurrentATE;
+ return TRUE;
+ } else
+ return FALSE;
+
+ } else {
+
+ // We've been given a context. We just need to make sure that it's
+ // valid.
+
+ if (i < ARP_TABLE_SIZE) {
+ CurrentATE = (*Table)[i];
+ while (CurrentATE != NULL) {
+ if (CurrentATE == TargetATE) {
+ *Valid = TRUE;
+ return TRUE;
+ break;
+ } else {
+ CurrentATE = CurrentATE->ate_next;
+ }
+ }
+
+ }
+
+ // If we get here, we didn't find the matching ATE.
+ *Valid = FALSE;
+ return FALSE;
+
+ }
+
+}
+
+#define IFE_FIXED_SIZE offsetof(struct IFEntry, if_descr)
+
+//* ARPQueryInfo - ARP query information handler.
+//
+// Called to query information about the ARP table or statistics about the
+// actual interface.
+//
+// Input: IFContext - Interface context (pointer to an ARPInterface).
+// ID - TDIObjectID for object.
+// Buffer - Buffer to put data into.
+// Size - Pointer to size of buffer. On return, filled with
+// bytes copied.
+// Context - Pointer to context block.
+//
+// Returns: Status of attempt to query information.
+//
+int
+ARPQueryInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size,
+ void *Context)
+{
+ ARPInterface *AI = (ARPInterface *)IFContext;
+ uint Offset = 0;
+ uint BufferSize = *Size;
+ CTELockHandle Handle;
+ uint ContextValid, DataLeft;
+ uint BytesCopied = 0;
+ uchar InfoBuff[sizeof(IFEntry)];
+ uint Entity;
+ uint Instance;
+
+
+ Entity = ID->toi_entity.tei_entity;
+ Instance = ID->toi_entity.tei_instance;
+
+ // First, make sure it's possibly an ID we can handle.
+ if ((Entity != AT_ENTITY || Instance != AI->ai_atinst) &&
+ (Entity != IF_ENTITY || Instance != AI->ai_ifinst)) {
+ return TDI_INVALID_REQUEST;
+ }
+
+ *Size = 0; // In case of an error.
+
+ if (ID->toi_type != INFO_TYPE_PROVIDER)
+ return TDI_INVALID_PARAMETER;
+
+ if (ID->toi_class == INFO_CLASS_GENERIC) {
+ if (ID->toi_id == ENTITY_TYPE_ID) {
+ // He's trying to see what type we are.
+ if (BufferSize >= sizeof(uint)) {
+ *(uint *)&InfoBuff[0] = (Entity == AT_ENTITY) ? AT_ARP :
+ IF_MIB;
+ (void)CopyToNdis(Buffer, InfoBuff, sizeof(uint), &Offset);
+ return TDI_SUCCESS;
+ } else
+ return TDI_BUFFER_TOO_SMALL;
+ }
+ return TDI_INVALID_PARAMETER;
+ }
+
+ // Might be able to handle this.
+ if (Entity == AT_ENTITY) {
+ // It's an address translation object. It could be a MIB object or
+ // an implementation specific object (the generic objects were handled
+ // above).
+
+ if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) {
+ ARPPArpAddr *PArpAddr;
+
+ // It's an implementation specific ID. The only ones we handle
+ // are the PARP_COUNT_ID and the PARP_ENTRY ID.
+
+ if (ID->toi_id == AT_ARP_PARP_COUNT_ID) {
+ // He wants to know the count. Just return that to him.
+ if (BufferSize >= sizeof(uint)) {
+
+ CTEGetLock(&AI->ai_lock, &Handle);
+
+ (void)CopyToNdis(Buffer, (uchar *)&AI->ai_parpcount,
+ sizeof(uint), &Offset);
+
+ CTEFreeLock(&AI->ai_lock, Handle);
+ return TDI_SUCCESS;
+ } else
+ return TDI_BUFFER_TOO_SMALL;
+ }
+
+ if (ID->toi_id != AT_ARP_PARP_ENTRY_ID)
+ return TDI_INVALID_PARAMETER;
+
+ // It's for Proxy ARP entries. The context should be either NULL
+ // or a pointer to the next one to be read.
+ CTEGetLock(&AI->ai_lock, &Handle);
+
+ PArpAddr = *(ARPPArpAddr **)Context;
+
+ if (PArpAddr != NULL) {
+ ARPPArpAddr *CurrentPARP;
+
+ // Loop through the P-ARP addresses on the interface, and
+ // see if we can find this one.
+ CurrentPARP = AI->ai_parpaddr;
+ while (CurrentPARP != NULL) {
+ if (CurrentPARP == PArpAddr)
+ break;
+ else
+ CurrentPARP = CurrentPARP->apa_next;
+ }
+
+ // If we found a match, PARPAddr points to where to begin
+ // reading. Otherwise, fail the request.
+ if (CurrentPARP == NULL) {
+ // Didn't find a match, so fail the request.
+ CTEFreeLock(&AI->ai_lock, Handle);
+ return TDI_INVALID_PARAMETER;
+ }
+ } else
+ PArpAddr = AI->ai_parpaddr;
+
+ // PARPAddr points to the next entry to put in the buffer, if
+ // there is one.
+ while (PArpAddr != NULL) {
+ if ((int)(BufferSize - BytesCopied) >=
+ (int)sizeof(ProxyArpEntry)) {
+ ProxyArpEntry *TempPArp;
+
+ TempPArp = (ProxyArpEntry *)InfoBuff;
+ TempPArp->pae_status = PAE_STATUS_VALID;
+ TempPArp->pae_addr = PArpAddr->apa_addr;
+ TempPArp->pae_mask = PArpAddr->apa_mask;
+ BytesCopied += sizeof(ProxyArpEntry);
+ Buffer = CopyToNdis(Buffer, (uchar *)TempPArp,
+ sizeof(ProxyArpEntry), &Offset);
+ PArpAddr = PArpAddr->apa_next;
+ } else
+ break;
+ }
+
+ // We're done copying. Free the lock and return the correct
+ // status.
+ CTEFreeLock(&AI->ai_lock, Handle);
+ *Size = BytesCopied;
+ **(ARPPArpAddr ***)&Context = PArpAddr;
+ return (PArpAddr == NULL) ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW;
+ }
+
+ if (ID->toi_id == AT_MIB_ADDRXLAT_INFO_ID) {
+ AddrXlatInfo *AXI;
+
+ // It's for the count. Just return the number of entries in the
+ // table.
+ if (BufferSize >= sizeof(AddrXlatInfo)) {
+ *Size = sizeof(AddrXlatInfo);
+ AXI = (AddrXlatInfo *)InfoBuff;
+ AXI->axi_count = AI->ai_count;
+ AXI->axi_index = AI->ai_index;
+ (void)CopyToNdis(Buffer, (uchar *)AXI, sizeof(AddrXlatInfo),
+ &Offset);
+ return TDI_SUCCESS;
+ } else
+ return TDI_BUFFER_TOO_SMALL;
+ }
+
+ if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID) {
+ // He's trying to read the table.
+ // Make sure we have a valid context.
+ CTEGetLock(&AI->ai_ARPTblLock, &Handle);
+ DataLeft = ARPValidateContext(Context, AI, &ContextValid);
+
+ // If the context is valid, we'll continue trying to read.
+ if (!ContextValid) {
+ CTEFreeLock(&AI->ai_ARPTblLock, Handle);
+ return TDI_INVALID_PARAMETER;
+ }
+
+ while (DataLeft) {
+ // The invariant here is that there is data in the table to
+ // read. We may or may not have room for it. So DataLeft
+ // is TRUE, and BufferSize - BytesCopied is the room left
+ // in the buffer.
+ if ((int)(BufferSize - BytesCopied) >=
+ (int)sizeof(IPNetToMediaEntry)) {
+ DataLeft = ARPReadNext(Context, AI, InfoBuff);
+ BytesCopied += sizeof(IPNetToMediaEntry);
+ Buffer = CopyToNdis(Buffer, InfoBuff,
+ sizeof(IPNetToMediaEntry), &Offset);
+ } else
+ break;
+
+ }
+
+ *Size = BytesCopied;
+
+ CTEFreeLock(&AI->ai_ARPTblLock, Handle);
+ return (!DataLeft ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW);
+ }
+
+ return TDI_INVALID_PARAMETER;
+ }
+
+ if (ID->toi_class != INFO_CLASS_PROTOCOL)
+ return TDI_INVALID_PARAMETER;
+
+ // He must be asking for interface level information. See if we support
+ // what he's asking for.
+ if (ID->toi_id == IF_MIB_STATS_ID) {
+ IFEntry *IFE = (IFEntry *)InfoBuff;
+
+
+ // He's asking for statistics. Make sure his buffer is at least big
+ // enough to hold the fixed part.
+
+ if (BufferSize < IFE_FIXED_SIZE) {
+ return TDI_BUFFER_TOO_SMALL;
+ }
+
+ // He's got enough to hold the fixed part. Build the IFEntry structure,
+ // and copy it to his buffer.
+ IFE->if_index = AI->ai_index;
+ switch (AI->ai_media) {
+ case NdisMedium802_3:
+ IFE->if_type = IF_TYPE_ETHERNET;
+ break;
+ case NdisMedium802_5:
+ IFE->if_type = IF_TYPE_TOKENRING;
+ break;
+ case NdisMediumFddi:
+ IFE->if_type = IF_TYPE_FDDI;
+ break;
+ case NdisMediumArcnet878_2:
+ default:
+ IFE->if_type = IF_TYPE_OTHER;
+ break;
+ }
+ IFE->if_mtu = AI->ai_mtu;
+ IFE->if_speed = AI->ai_speed;
+ IFE->if_physaddrlen = AI->ai_addrlen;
+ CTEMemCopy(IFE->if_physaddr,AI->ai_addr, AI->ai_addrlen);
+ IFE->if_adminstatus = (uint)AI->ai_adminstate;
+ IFE->if_operstatus = (uint)AI->ai_operstate;
+ IFE->if_lastchange = AI->ai_lastchange;
+ IFE->if_inoctets = AI->ai_inoctets;
+ IFE->if_inucastpkts = AI->ai_inpcount[AI_UCAST_INDEX];
+ IFE->if_innucastpkts = AI->ai_inpcount[AI_NONUCAST_INDEX];
+ IFE->if_indiscards = AI->ai_indiscards;
+ IFE->if_inerrors = AI->ai_inerrors;
+ IFE->if_inunknownprotos = AI->ai_uknprotos;
+ IFE->if_outoctets = AI->ai_outoctets;
+ IFE->if_outucastpkts = AI->ai_outpcount[AI_UCAST_INDEX];
+ IFE->if_outnucastpkts = AI->ai_outpcount[AI_NONUCAST_INDEX];
+ IFE->if_outdiscards = AI->ai_outdiscards;
+ IFE->if_outerrors = AI->ai_outerrors;
+ IFE->if_outqlen = AI->ai_qlen;
+ IFE->if_descrlen = AI->ai_desclen;
+ Buffer = CopyToNdis(Buffer, (uchar *)IFE, IFE_FIXED_SIZE, &Offset);
+
+ // See if he has room for the descriptor string.
+ if (BufferSize >= (IFE_FIXED_SIZE + AI->ai_desclen)) {
+ // He has room. Copy it.
+ if (AI->ai_desclen != 0) {
+ (void)CopyToNdis(Buffer, AI->ai_desc, AI->ai_desclen, &Offset);
+ }
+ *Size = IFE_FIXED_SIZE + AI->ai_desclen;
+ return TDI_SUCCESS;
+ } else {
+ // Not enough room to copy the desc. string.
+ *Size = IFE_FIXED_SIZE;
+ return TDI_BUFFER_OVERFLOW;
+ }
+
+ }
+
+ return TDI_INVALID_PARAMETER;
+
+}
+
+//* ARPSetInfo - ARP set information handler.
+//
+// The ARP set information handler. We support setting of an I/F admin
+// status, and setting/deleting of ARP table entries.
+//
+// Input: Context - Pointer to I/F to set on.
+// ID - The object ID
+// Buffer - Pointer to buffer containing value to set.
+// Size - Size in bytes of Buffer.
+//
+// Returns: Status of attempt to set information.
+//
+int
+ARPSetInfo(void *Context, TDIObjectID *ID, void *Buffer, uint Size)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ CTELockHandle Handle, EntryHandle;
+ int Status;
+ IFEntry *IFE = (IFEntry *)Buffer;
+ IPNetToMediaEntry *IPNME;
+ ARPTableEntry *PrevATE, *CurrentATE;
+ ARPTable *Table;
+ ENetHeader *Header;
+ uint Entity, Instance;
+
+ Entity = ID->toi_entity.tei_entity;
+ Instance = ID->toi_entity.tei_instance;
+
+ // First, make sure it's possibly an ID we can handle.
+ if ((Entity != AT_ENTITY || Instance != Interface->ai_atinst) &&
+ (Entity != IF_ENTITY || Instance != Interface->ai_ifinst)) {
+ return TDI_INVALID_REQUEST;
+ }
+
+ if (ID->toi_type != INFO_TYPE_PROVIDER) {
+ return TDI_INVALID_PARAMETER;
+ }
+
+ // Might be able to handle this.
+ if (Entity == IF_ENTITY) {
+
+ // It's for the I/F level, see if it's for the statistics.
+ if (ID->toi_class != INFO_CLASS_PROTOCOL)
+ return TDI_INVALID_PARAMETER;
+
+ if (ID->toi_id == IF_MIB_STATS_ID) {
+ // It's for the stats. Make sure it's a valid size.
+ if (Size >= IFE_FIXED_SIZE) {
+ // It's a valid size. See what he wants to do.
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ switch (IFE->if_adminstatus) {
+ case IF_STATUS_UP:
+ // He's marking it up. If the operational state is
+ // alse up, mark the whole interface as up.
+ Interface->ai_adminstate = IF_STATUS_UP;
+ if (Interface->ai_operstate == IF_STATUS_UP)
+ Interface->ai_state = INTERFACE_UP;
+ Status = TDI_SUCCESS;
+ break;
+ case IF_STATUS_DOWN:
+ // He's taking it down. Mark both the admin state and
+ // the interface state down.
+ Interface->ai_adminstate = IF_STATUS_DOWN;
+ Interface->ai_state = INTERFACE_DOWN;
+ Status = TDI_SUCCESS;
+ break;
+ case IF_STATUS_TESTING:
+ // He's trying to cause up to do testing, which we
+ // don't support. Just return success.
+ Status = TDI_SUCCESS;
+ break;
+ default:
+ Status = TDI_INVALID_PARAMETER;
+ break;
+ }
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return Status;
+ } else
+ return TDI_INVALID_PARAMETER;
+ } else {
+ return TDI_INVALID_PARAMETER;
+ }
+ }
+
+ // Not for the interface level. See if it's an implementation or protocol
+ // class.
+ if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) {
+ ProxyArpEntry *PArpEntry;
+ ARPIPAddr *Addr;
+ IPAddr AddAddr;
+ IPMask Mask;
+
+ // It's for the implementation. It should be the proxy-ARP ID.
+ if (ID->toi_id != AT_ARP_PARP_ENTRY_ID || Size < sizeof(ProxyArpEntry))
+ return TDI_INVALID_PARAMETER;
+
+ PArpEntry = (ProxyArpEntry *)Buffer;
+ AddAddr = PArpEntry->pae_addr;
+ Mask = PArpEntry->pae_mask;
+
+ // See if he's trying to add or delete a proxy arp entry.
+ if (PArpEntry->pae_status == PAE_STATUS_VALID) {
+ // We're trying to add an entry. We won't allow an entry
+ // to be added that we believe to be invalid or conflicting
+ // with our local addresses.
+
+ if (!IP_ADDR_EQUAL(AddAddr & Mask, AddAddr) ||
+ IP_ADDR_EQUAL(AddAddr, NULL_IP_ADDR) ||
+ IP_ADDR_EQUAL(AddAddr, IP_LOCAL_BCST) ||
+ CLASSD_ADDR(AddAddr))
+ return TDI_INVALID_PARAMETER;
+
+ // Walk through the list of addresses on the interface, and see
+ // if they would match the AddAddr. If so, fail the request.
+ CTEGetLock(&Interface->ai_lock, &Handle);
+
+ if (IsBCastOnIF(Interface, AddAddr & Mask)) {
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return TDI_INVALID_PARAMETER;
+ }
+
+ Addr = &Interface->ai_ipaddr;
+ do {
+ if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) {
+ if (IP_ADDR_EQUAL(Addr->aia_addr & Mask, AddAddr))
+ break;
+ }
+ Addr = Addr->aia_next;
+ } while (Addr != NULL);
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ if (Addr != NULL)
+ return TDI_INVALID_PARAMETER;
+
+ // At this point, we believe we're ok. Try to add the address.
+ if (ARPAddAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask, NULL))
+ return TDI_SUCCESS;
+ else
+ return TDI_NO_RESOURCES;
+ } else {
+ if (PArpEntry->pae_status == PAE_STATUS_INVALID) {
+ // He's trying to delete a proxy ARP address.
+ if (ARPDeleteAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask))
+ return TDI_SUCCESS;
+ }
+ return TDI_INVALID_PARAMETER;
+ }
+
+ }
+
+ if (ID->toi_class != INFO_CLASS_PROTOCOL)
+ return TDI_INVALID_PARAMETER;
+
+
+ if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID &&
+ Size >= sizeof(IPNetToMediaEntry)) {
+ // He does want to set an ARP table entry. See if he's trying to
+ // create or delete one.
+
+ IPNME = (IPNetToMediaEntry *)Buffer;
+ if (IPNME->inme_type == INME_TYPE_INVALID) {
+ uint Index = ARP_HASH(IPNME->inme_addr);
+
+ // We're trying to delete an entry. See if we can find it,
+ // and then delete it.
+ CTEGetLock(&Interface->ai_ARPTblLock, &Handle);
+ Table = Interface->ai_ARPTbl;
+ PrevATE = STRUCT_OF(ARPTableEntry, &((*Table)[Index]), ate_next);
+ CurrentATE = (*Table)[Index];
+ while (CurrentATE != (ARPTableEntry *)NULL) {
+ if (CurrentATE->ate_dest == IPNME->inme_addr) {
+ // Found him. Break out of the loop.
+ break;
+ } else {
+ PrevATE = CurrentATE;
+ CurrentATE = CurrentATE->ate_next;
+ }
+ }
+
+ if (CurrentATE != NULL) {
+ CTEGetLock(&CurrentATE->ate_lock, &EntryHandle);
+ RemoveARPTableEntry(PrevATE, CurrentATE);
+ Interface->ai_count--;
+ CTEFreeLock(&CurrentATE->ate_lock, EntryHandle);
+
+ if (CurrentATE->ate_packet != NULL)
+ IPSendComplete(Interface->ai_context, CurrentATE->ate_packet,
+ NDIS_STATUS_SUCCESS);
+
+ CTEFreeMem(CurrentATE);
+ Status = TDI_SUCCESS;
+ } else
+ Status = TDI_INVALID_PARAMETER;
+
+ CTEFreeLock(&Interface->ai_ARPTblLock, Handle);
+ return Status;
+ }
+
+ // We're not trying to delete. See if we're trying to create.
+ if (IPNME->inme_type != INME_TYPE_DYNAMIC &&
+ IPNME->inme_type != INME_TYPE_STATIC) {
+ // Not creating, return an error.
+ return TDI_INVALID_PARAMETER;
+ }
+
+ // Make sure he's trying to create a valid address.
+ if (IPNME->inme_physaddrlen != Interface->ai_addrlen)
+ return TDI_INVALID_PARAMETER;
+
+ // We're trying to create an entry. Call CreateARPTableEntry to create
+ // one, and fill it in.
+ CurrentATE = CreateARPTableEntry(Interface, IPNME->inme_addr, &Handle);
+ if (CurrentATE == NULL) {
+ return TDI_NO_RESOURCES;
+ }
+
+ // We've created or found an entry. Fill it in.
+ Header = (ENetHeader *)CurrentATE->ate_addr;
+
+ switch (Interface->ai_media) {
+ case NdisMedium802_5:
+ {
+ TRHeader *Temp = (TRHeader *)Header;
+
+ // Fill in the TR specific parts, and set the length to the
+ // size of a TR header.
+
+ Temp->tr_ac = ARP_AC;
+ Temp->tr_fc = ARP_FC;
+ CTEMemCopy(&Temp->tr_saddr[ARP_802_ADDR_LENGTH], ARPSNAP,
+ sizeof(SNAPHeader));
+
+ Header = (ENetHeader *)&Temp->tr_daddr;
+ CurrentATE->ate_addrlength = sizeof(TRHeader) +
+ sizeof(SNAPHeader);
+ }
+ break;
+ case NdisMedium802_3:
+ CurrentATE->ate_addrlength = sizeof(ENetHeader);
+ break;
+ case NdisMediumFddi:
+ {
+ FDDIHeader *Temp = (FDDIHeader *)Header;
+
+ Temp->fh_pri = ARP_FDDI_PRI;
+ CTEMemCopy(&Temp->fh_saddr[ARP_802_ADDR_LENGTH], ARPSNAP,
+ sizeof(SNAPHeader));
+ Header = (ENetHeader *)&Temp->fh_daddr;
+ CurrentATE->ate_addrlength = sizeof(FDDIHeader) +
+ sizeof(SNAPHeader);
+ }
+ break;
+ case NdisMediumArcnet878_2:
+ {
+ ARCNetHeader *Temp = (ARCNetHeader *)Header;
+
+ Temp->ah_saddr = Interface->ai_addr[0];
+ Temp->ah_daddr = IPNME->inme_physaddr[0];
+ Temp->ah_prot = ARP_ARCPROT_IP;
+ CurrentATE->ate_addrlength = sizeof(ARCNetHeader);
+ }
+ break;
+ default:
+ DEBUGCHK;
+ break;
+ }
+
+
+ // Copy in the source and destination addresses.
+
+ if (Interface->ai_media != NdisMediumArcnet878_2) {
+ CTEMemCopy(Header->eh_daddr, IPNME->inme_physaddr,
+ ARP_802_ADDR_LENGTH);
+ CTEMemCopy(Header->eh_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+
+ // Now fill in the Ethertype.
+ *(ushort *)&CurrentATE->ate_addr[CurrentATE->ate_addrlength-2] =
+ net_short(ARP_ETYPE_IP);
+ }
+
+ // If he's creating a static entry, mark it as always valid. Otherwise
+ // mark him as valid now.
+ if (IPNME->inme_type == INME_TYPE_STATIC)
+ CurrentATE->ate_valid = ALWAYS_VALID;
+ else
+ CurrentATE->ate_valid = CTESystemUpTime();
+
+ CurrentATE->ate_state = ARP_GOOD;
+
+ CTEFreeLock(&CurrentATE->ate_lock, Handle);
+ return TDI_SUCCESS;
+
+ }
+
+ return TDI_INVALID_PARAMETER;
+
+
+}
+
+
+static uint ARPPackets = ARP_DEFAULT_PACKETS;
+static uint ARPBuffers = ARP_DEFAULT_BUFFERS;
+
+#pragma BEGIN_INIT
+//** ARPInit - Initialize the ARP module.
+//
+// This functions intializes all of the ARP module, including allocating
+// the ARP table and any other necessary data structures.
+//
+// Entry: nothing.
+//
+// Exit: Returns 0 if we fail to init., !0 if we succeed.
+//
+int
+ARPInit()
+{
+ NDIS_STATUS Status; // Status for NDIS calls.
+
+// BUGBUG - Get configuration information dynamically.
+
+#ifdef NT
+ RtlInitUnicodeString(&(ARPCharacteristics.Name), ARPName);
+#else // NT
+ ARPCharacteristics.Name.Buffer = ARPName;
+#endif // NT
+
+ NdisRegisterProtocol(&Status, &ARPHandle, (NDIS_PROTOCOL_CHARACTERISTICS *)
+ &ARPCharacteristics, sizeof(ARPCharacteristics));
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+ return(1);
+ }
+ else {
+ return(0);
+ }
+}
+#pragma END_INIT
+
+#ifndef CHICAGO
+#pragma BEGIN_INIT
+#else
+#pragma code_seg("_LTEXT", "LCODE")
+#endif
+
+//* FreeARPInterface - Free an ARP interface
+//
+// Called in the event of some sort of initialization failure. We free all
+// the memory associated with an ARP interface.
+//
+// Entry: Interface - Pointer to interface structure to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeARPInterface(ARPInterface *Interface)
+{
+ NDIS_STATUS Status;
+ ARPBufferTracker *Tracker;
+ ARPTable *Table; // ARP table.
+ uint i; // Index variable.
+ ARPTableEntry *ATE;
+ CTELockHandle LockHandle;
+ NDIS_HANDLE Handle;
+
+ CTEStopTimer(&Interface->ai_timer);
+
+// If we're bound to the adapter, close it now.
+ CTEInitBlockStruc(&Interface->ai_block);
+
+ CTEGetLock(&Interface->ai_lock, &LockHandle);
+ if (Interface->ai_handle != (NDIS_HANDLE)NULL) {
+ Handle = Interface->ai_handle;
+ Interface->ai_handle = NULL;
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+
+ NdisCloseAdapter(&Status, Handle);
+
+ if (Status == NDIS_STATUS_PENDING)
+ Status = CTEBlock(&Interface->ai_block);
+ } else {
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+ }
+
+ // First free any outstanding ARP table entries.
+ Table = Interface->ai_ARPTbl;
+ if (Table != NULL) {
+ for (i = 0; i < ARP_TABLE_SIZE;i++) {
+ while ((*Table)[i] != NULL) {
+ ATE = (*Table)[i];
+ RemoveARPTableEntry(STRUCT_OF(ARPTableEntry, &((*Table)[i]),
+ ate_next),ATE);
+ CTEFreeMem(ATE);
+ }
+ }
+ CTEFreeMem(Table);
+ }
+
+ Interface->ai_ARPTbl = NULL;
+
+ if (Interface->ai_ppool != (NDIS_HANDLE)NULL)
+ NdisFreePacketPool(Interface->ai_ppool);
+
+ if (Interface->ai_bpool != (NDIS_HANDLE)NULL)
+ NdisFreeBufferPool(Interface->ai_bpool);
+
+ Tracker = Interface->ai_buflist;
+ while (Tracker != NULL) {
+ Interface->ai_buflist = Tracker->abt_next;
+ NdisFreeBufferPool(Tracker->abt_handle);
+ CTEFreeMem(Tracker->abt_buffer);
+ CTEFreeMem(Tracker);
+ Tracker = Interface->ai_buflist;
+ }
+
+ if (Interface->ai_bbbase != (uchar *)NULL)
+ CTEFreeMem(Interface->ai_bbbase);
+
+ // Free the interface itself.
+ CTEFreeMem(Interface);
+}
+
+//** ARPOpen - Open an adapter for reception.
+//
+// This routine is called when the upper layer is done initializing and wishes to
+// begin receiveing packets. The adapter is actually 'open', we just call InitAdapter
+// to set the packet filter and lookahead size.
+//
+// Input: Context - Interface pointer we gave to IP earlier.
+//
+// Returns: Nothing
+//
+void
+ARPOpen(void *Context)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ InitAdapter(Interface); // Set the packet filter - we'll begin receiving.
+}
+
+//* ARPGetEList - Get the entity list.
+//
+// Called at init time to get an entity list. We fill our stuff in, and
+// then call the interfaces below us to allow them to do the same.
+//
+// Input: EntityList - Pointer to entity list to be filled in.
+// Count - Pointer to number of entries in the list.
+//
+// Returns Status of attempt to get the info.
+//
+int
+ARPGetEList(void *Context, TDIEntityID *EntityList, uint *Count)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ uint ECount;
+ uint MyATBase;
+ uint MyIFBase;
+ uint i;
+
+ ECount = *Count;
+
+ // Walk down the list, looking for existing AT or IF entities, and
+ // adjust our base instance accordingly.
+
+ MyATBase = 0;
+ MyIFBase = 0;
+ for (i = 0; i < ECount; i++, EntityList++) {
+ if (EntityList->tei_entity == AT_ENTITY)
+ MyATBase = MAX(MyATBase, EntityList->tei_instance + 1);
+ else
+ if (EntityList->tei_entity == IF_ENTITY)
+ MyIFBase = MAX(MyIFBase, EntityList->tei_instance + 1);
+ }
+
+ // EntityList points to the start of where we want to begin filling in.
+ // Make sure we have enough room. We need one for the ICMP instance,
+ // and one for the CL_NL instance.
+
+ if ((ECount + 2) > MAX_TDI_ENTITIES)
+ return FALSE;
+
+ // At this point we've figure out our base instance. Save for later use.
+ Interface->ai_atinst = MyATBase;
+ Interface->ai_ifinst = MyIFBase;
+
+ // Now fill it in.
+ EntityList->tei_entity = AT_ENTITY;
+ EntityList->tei_instance = MyATBase;
+ EntityList++;
+ EntityList->tei_entity = IF_ENTITY;
+ EntityList->tei_instance = MyIFBase;
+ *Count += 2;
+
+ return TRUE;
+}
+
+
+extern uint UseEtherSNAP(PNDIS_STRING Name);
+extern void GetAlwaysSourceRoute(uint *pArpAlwaysSourceRoute, uint *pIPAlwaysSourceRoute);
+extern uint GetArpCacheLife(void);
+
+
+//** ARPRegister - Register a protocol with the ARP module.
+//
+// We register a protocol for ARP processing. We also open the
+// NDIS adapter here.
+//
+// Note that much of the information passed in here is unused, as
+// ARP currently only works with IP.
+//
+// Entry:
+// Adapter - Name of the adapter to bind to.
+// IPContext - Value to be passed to IP on upcalls.
+//
+#ifndef _PNP_POWER
+int
+ARPRegister(PNDIS_STRING Adapter, void *IPContext, IPRcvRtn RcvRtn,
+ IPTxCmpltRtn TxCmpltRtn, IPStatusRtn StatusRtn, IPTDCmpltRtn TDCmpltRtn,
+ IPRcvCmpltRtn RcvCmpltRtn, struct LLIPBindInfo *Info, uint NumIFBound)
+#else
+int
+ARPRegister(PNDIS_STRING Adapter, uint *Flags, struct ARPInterface **Interface)
+#endif
+{
+ ARPInterface *ai; // Pointer to interface struct. for this
+ // interface.
+ NDIS_STATUS Status, OpenStatus; // Status values.
+ uint i = 0; // Medium index.
+ NDIS_MEDIUM MediaArray[MAX_MEDIA];
+ uchar sbsize;
+ uchar *buffer; // Pointer to our buffers.
+ uint mss;
+ uint speed;
+ uint Needed;
+ uint MacOpts;
+ uchar bcastmask, bcastval, bcastoff, addrlen, hdrsize, snapsize;
+ uint OID;
+ uint PF;
+ PNDIS_BUFFER Buffer;
+
+ if ((ai = CTEAllocMem(sizeof(ARPInterface))) == (ARPInterface *)NULL)
+ return FALSE; // Couldn't allocate memory for this one.
+
+#ifdef _PNP_POWER
+ *Interface = ai;
+#endif
+
+ CTEMemSet(ai, 0, sizeof(ARPInterface));
+ CTEInitTimer(&ai->ai_timer);
+
+#ifdef NT
+ ExInitializeSListHead(&ai->ai_sblist);
+#endif
+
+
+ MediaArray[MEDIA_DIX] = NdisMedium802_3;
+ MediaArray[MEDIA_TR] = NdisMedium802_5;
+ MediaArray[MEDIA_FDDI] = NdisMediumFddi;
+ MediaArray[MEDIA_ARCNET] = NdisMediumArcnet878_2;
+
+ // Initialize this adapter interface structure.
+ ai->ai_state = INTERFACE_INIT;
+ ai->ai_adminstate = IF_STATUS_DOWN;
+ ai->ai_operstate = IF_STATUS_DOWN;
+ ai->ai_bcast = IP_LOCAL_BCST;
+ ai->ai_maxhdrs = ARP_DEFAULT_MAXHDRS;
+
+#ifndef _PNP_POWER
+ ai->ai_index = NumIFBound + 1;
+ ai->ai_context = IPContext;
+ Info->lip_context = ai;
+ Info->lip_transmit = ARPTransmit;
+ Info->lip_transfer = ARPXferData;
+ Info->lip_close = ARPClose;
+ Info->lip_addaddr = ARPAddAddr;
+ Info->lip_deladdr = ARPDeleteAddr;
+ Info->lip_invalidate = ARPInvalidate;
+ Info->lip_open = ARPOpen;
+ Info->lip_qinfo = ARPQueryInfo;
+ Info->lip_setinfo = ARPSetInfo;
+ Info->lip_getelist = ARPGetEList;
+
+ Info->lip_index = ai->ai_index;
+#endif
+
+ // Initialize the locks.
+ CTEInitLock(&ai->ai_lock);
+ CTEInitLock(&ai->ai_ARPTblLock);
+
+ GetAlwaysSourceRoute(&sArpAlwaysSourceRoute, &sIPAlwaysSourceRoute);
+
+ ArpCacheLife = GetArpCacheLife();
+
+ if (!ArpCacheLife) {
+ ArpCacheLife = 1;
+ }
+
+ ArpCacheLife = (ArpCacheLife * 1000L) / ARP_TIMER_TIME;
+
+ // Allocate the buffer and packet pools.
+ NdisAllocatePacketPool(&Status, &ai->ai_ppool, ARPPackets, sizeof(struct PCCommon));
+ if (Status != NDIS_STATUS_SUCCESS) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ NdisAllocateBufferPool(&Status, &ai->ai_bpool, ARPBuffers);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ // Allocate the ARP table
+ if ((ai->ai_ARPTbl = (ARPTable *)CTEAllocMem(ARP_TABLE_SIZE * sizeof(ARPTableEntry *))) ==
+ (ARPTable *)NULL) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ //
+ // NULL out the pointers
+ //
+ CTEMemSet(ai->ai_ARPTbl, 0, ARP_TABLE_SIZE * sizeof(ARPTableEntry *));
+
+ CTEInitBlockStruc(&ai->ai_block);
+
+ // Open the NDIS adapter.
+ NdisOpenAdapter(&Status, &OpenStatus, &ai->ai_handle, &i, MediaArray,
+ MAX_MEDIA, ARPHandle, ai, Adapter, 0, NULL);
+
+ // Block for open to complete.
+ if (Status == NDIS_STATUS_PENDING)
+ Status = (NDIS_STATUS)CTEBlock(&ai->ai_block);
+
+ ai->ai_media = MediaArray[i]; // Fill in media type.
+
+ // Open adapter completed. If it succeeded, we'll finish our intialization.
+ // If it failed, bail out now.
+ if (Status != NDIS_STATUS_SUCCESS) {
+ ai->ai_handle = NULL;
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ // Read the local address.
+ switch (ai->ai_media) {
+ case NdisMedium802_3:
+ addrlen = ARP_802_ADDR_LENGTH;
+ bcastmask = ENET_BCAST_MASK;
+ bcastval = ENET_BCAST_VAL;
+ bcastoff = ENET_BCAST_OFF;
+ OID = OID_802_3_CURRENT_ADDRESS;
+ sbsize = ARP_MAX_MEDIA_ENET;
+ hdrsize = sizeof(ENetHeader);
+ if (!UseEtherSNAP(Adapter)) {
+ snapsize = 0;
+ } else {
+ snapsize = sizeof(SNAPHeader);
+ sbsize += sizeof(SNAPHeader);
+ }
+
+ PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED |
+ NDIS_PACKET_TYPE_MULTICAST;
+ break;
+ case NdisMedium802_5:
+ addrlen = ARP_802_ADDR_LENGTH;
+ bcastmask = TR_BCAST_MASK;
+ bcastval = TR_BCAST_VAL;
+ bcastoff = TR_BCAST_OFF;
+ OID = OID_802_5_CURRENT_ADDRESS;
+ sbsize = ARP_MAX_MEDIA_TR;
+ hdrsize = sizeof(TRHeader);
+ snapsize = sizeof(SNAPHeader);
+ PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED;
+ break;
+ case NdisMediumFddi:
+ addrlen = ARP_802_ADDR_LENGTH;
+ bcastmask = FDDI_BCAST_MASK;
+ bcastval = FDDI_BCAST_VAL;
+ bcastoff = FDDI_BCAST_OFF;
+ OID = OID_FDDI_LONG_CURRENT_ADDR;
+ sbsize = ARP_MAX_MEDIA_FDDI;
+ hdrsize = sizeof(FDDIHeader);
+ snapsize = sizeof(SNAPHeader);
+ PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED |
+ NDIS_PACKET_TYPE_MULTICAST;
+ break;
+ case NdisMediumArcnet878_2:
+ addrlen = 1;
+ bcastmask = ARC_BCAST_MASK;
+ bcastval = ARC_BCAST_VAL;
+ bcastoff = ARC_BCAST_OFF;
+ OID = OID_ARCNET_CURRENT_ADDRESS;
+ sbsize = ARP_MAX_MEDIA_ARC;
+ hdrsize = sizeof(ARCNetHeader);
+ snapsize = 0;
+ PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED;
+ break;
+ default:
+ DEBUGCHK;
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ ai->ai_bcastmask = bcastmask;
+ ai->ai_bcastval = bcastval;
+ ai->ai_bcastoff = bcastoff;
+ ai->ai_addrlen = addrlen;
+ ai->ai_hdrsize = hdrsize;
+ ai->ai_snapsize = snapsize;
+ ai->ai_pfilter = PF;
+
+ Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID,
+ ai->ai_addr, addrlen, NULL);
+
+ if (Status != NDIS_STATUS_SUCCESS) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+#ifndef _PNP_POWER
+ Info->lip_addrlen = addrlen;
+ Info->lip_addr = ai->ai_addr;
+#endif
+
+ // Read the maximum frame size.
+ if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation,
+ OID_GEN_MAXIMUM_FRAME_SIZE, &mss, sizeof(mss), NULL)) != NDIS_STATUS_SUCCESS) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ // If this is token ring, figure out the RC len stuff now.
+ mss -= (uint)ai->ai_snapsize;
+
+ if (ai->ai_media == NdisMedium802_5) {
+ mss -= (sizeof(RC) + (ARP_MAX_RD * sizeof(ushort)));
+ } else {
+ if (ai->ai_media == NdisMediumFddi) {
+ mss = MIN(mss, ARP_FDDI_MSS);
+ }
+ }
+
+ ai->ai_mtu = (ushort)mss;
+
+#ifndef _PNP_POWER
+ Info->lip_mss = mss;
+#endif
+
+ // Read the speed for local purposes.
+ if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation,
+ OID_GEN_LINK_SPEED, &speed, sizeof(speed), NULL)) == NDIS_STATUS_SUCCESS) {
+ ai->ai_speed = speed * 100L;
+#ifndef _PNP_POWER
+ Info->lip_speed = ai->ai_speed;
+#endif
+ }
+
+ // Read and save the options.
+ Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_GEN_MAC_OPTIONS,
+ &MacOpts, sizeof(MacOpts), NULL);
+
+ if (Status != NDIS_STATUS_SUCCESS)
+#ifndef _PNP_POWER
+ Info->lip_flags = 0;
+#else
+ *Flags = 0;
+#endif
+ else
+#ifndef _PNP_POWER
+ Info->lip_flags =
+#else
+ *Flags =
+#endif
+ (MacOpts & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? LIP_COPY_FLAG : 0;
+
+ // Read and store the vendor description string.
+ Status = DoNDISRequest(ai, NdisRequestQueryInformation,
+ OID_GEN_VENDOR_DESCRIPTION, &ai->ai_desc, 0, &Needed);
+
+ if ((Status == NDIS_STATUS_INVALID_LENGTH) ||
+ (Status == NDIS_STATUS_BUFFER_TOO_SHORT)) {
+ // We know the size we need. Allocate a buffer.
+ buffer = CTEAllocMem(Needed);
+ if (buffer != NULL) {
+ Status = DoNDISRequest(ai, NdisRequestQueryInformation,
+ OID_GEN_VENDOR_DESCRIPTION, buffer, Needed, NULL);
+ if (Status == NDIS_STATUS_SUCCESS) {
+ ai->ai_desc = buffer;
+ ai->ai_desclen = Needed;
+ }
+ }
+ }
+
+ // Allocate our small and big buffer pools.
+
+ if ((sbsize & 0x3)) {
+ //
+ // Must 32 bit align the buffers so pointers to them will be aligned.
+ //
+ sbsize = ((sbsize >> 2) + 1) << 2;
+ }
+
+ ai->ai_sbsize = sbsize;
+
+ // Pre-prime the ARP header buffer list.
+ Buffer = GrowARPHeaders(ai);
+ if (Buffer != NULL) {
+ FreeARPBuffer(ai, Buffer);
+ }
+
+
+ if ((buffer = CTEAllocMem((sbsize+sizeof(ARPHeader)) * ARPPackets)) == (uchar *)NULL) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ // Link big buffers into the list.
+ ai->ai_bbbase = buffer;
+ ai->ai_bblist = (uchar *)NULL;
+ for (i = 0; i < ARPPackets; i++) {
+ *(char **)&*buffer = ai->ai_bblist;
+ ai->ai_bblist = buffer;
+ buffer += sbsize+sizeof(ARPHeader);
+ }
+
+ // Everything's set up, so get the ARP timer running.
+ CTEStartTimer(&ai->ai_timer, ARP_TIMER_TIME, ARPTimeout, ai);
+
+ return TRUE;
+
+}
+
+#ifndef CHICAGO
+#pragma END_INIT
+#endif
+
+#ifdef _PNP_POWER
+
+//* ARPDynRegister - Dynamically register IP.
+//
+// Called by IP when he's about done binding to register with us. Since we
+// call him directly, we don't save his info here. We do keep his context
+// and index number.
+//
+// Input: See ARPRegister
+//
+// Returns: Nothing.
+//
+int
+ARPDynRegister(PNDIS_STRING Adapter, void *IPContext, IPRcvRtn RcvRtn,
+ IPTxCmpltRtn TxCmpltRtn, IPStatusRtn StatusRtn, IPTDCmpltRtn TDCmpltRtn,
+ IPRcvCmpltRtn RcvCmpltRtn, struct LLIPBindInfo *Info, uint NumIFBound)
+{
+ ARPInterface *Interface = (ARPInterface *)Info->lip_context;
+
+ Interface->ai_context = IPContext;
+ Interface->ai_index = NumIFBound;
+
+ return TRUE;
+}
+
+//* ARPBindAdapter - Bind and initialize an adapter.
+//
+// Called in a PNP environment to initialize and bind an adapter. We open
+// the adapter and get it running, and then we call up to IP to tell him
+// about it. IP will initialize, and if all goes well call us back to start
+// receiving.
+//
+// Input: RetStatus - Where to return the status of this call.
+// BindContext - Handle to use for calling BindAdapterComplete.
+// AdapterName - Pointer to name of adapter.
+// SS1 - System specific 1 parameter.
+// SS2 - System specific 2 parameter.
+//
+// Returns: Nothing.
+//
+void NDIS_API
+ARPBindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE BindContext,
+ PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2)
+{
+ uint Flags; // MAC binding flags.
+ ARPInterface *Interface; // Newly created interface.
+ PNDIS_STRING ConfigName; // Name used by IP for config. info.
+ IP_STATUS Status; // State of IPAddInterface call.
+ LLIPBindInfo BindInfo; // Binding informatio for IP.
+ NDIS_HANDLE Handle ;
+
+ CTERefillMem();
+
+ if (!OpenIFConfig(SS1, &Handle)) {
+ *RetStatus = NDIS_STATUS_FAILURE;
+ return;
+ }
+
+ // If IsLLInterfaceValueNull is FALSE then this means that some other ARP module is
+ // used for this device so we skip it.
+ //
+ if (IsLLInterfaceValueNull(Handle) == FALSE) {
+ *RetStatus = NDIS_STATUS_FAILURE;
+ CloseIFConfig(Handle);
+ return ;
+ }
+
+ CloseIFConfig(Handle);
+
+
+ // First, open the adapter and get the info.
+ if (!ARPRegister(AdapterName, &Flags, &Interface)) {
+ *RetStatus = NDIS_STATUS_FAILURE;
+ return;
+ }
+
+ CTERefillMem();
+
+ // OK, we're opened the adapter. Call IP to tell him about it.
+ BindInfo.lip_context = Interface;
+ BindInfo.lip_transmit = ARPTransmit;
+ BindInfo.lip_transfer = ARPXferData;
+ BindInfo.lip_close = ARPClose;
+ BindInfo.lip_addaddr = ARPAddAddr;
+ BindInfo.lip_deladdr = ARPDeleteAddr;
+ BindInfo.lip_invalidate = ARPInvalidate;
+ BindInfo.lip_open = ARPOpen;
+ BindInfo.lip_qinfo = ARPQueryInfo;
+ BindInfo.lip_setinfo = ARPSetInfo;
+ BindInfo.lip_getelist = ARPGetEList;
+ BindInfo.lip_mss = Interface->ai_mtu;
+ BindInfo.lip_speed = Interface->ai_speed;
+ BindInfo.lip_flags = Flags;
+ BindInfo.lip_addrlen = Interface->ai_addrlen;
+ BindInfo.lip_addr = Interface->ai_addr;
+
+ Status = IPAddInterface((PNDIS_STRING)SS1, SS2, Interface, ARPDynRegister,
+ &BindInfo);
+
+ if (Status != IP_SUCCESS) {
+ // Need to close the binding. FreeARPInterface will do that, as well
+ // as freeing resources.
+
+ FreeARPInterface(Interface);
+ *RetStatus = NDIS_STATUS_FAILURE;
+ } else
+ *RetStatus = NDIS_STATUS_SUCCESS;
+
+}
+
+//* ARPUnbindAdapter - Unbind from an adapter.
+//
+// Called when we need to unbind from an adapter. We'll call up to IP to tell
+// him. When he's done, we'll free our memory and return.
+//
+// Input: RetStatus - Where to return status from call.
+// ProtBindContext - The context we gave NDIS earlier - really a
+// pointer to an ARPInterface structure.
+// UnbindContext - Context for completeing this request.
+//
+// Returns: Nothing.
+//
+void NDIS_API
+ARPUnbindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE ProtBindContext,
+ NDIS_HANDLE UnbindContext)
+{
+ ARPInterface *Interface = (ARPInterface *)ProtBindContext;
+ NDIS_STATUS Status; // Status of close call.
+ CTELockHandle LockHandle;
+ NDIS_HANDLE Handle;
+
+ // Shut him up, so we don't get any more frames.
+ Interface->ai_pfilter = 0;
+ DoNDISRequest(Interface, NdisRequestSetInformation,
+ OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint),
+ NULL);
+
+ // Mark him as down.
+ Interface->ai_state = INTERFACE_DOWN;
+ Interface->ai_adminstate = IF_STATUS_DOWN;
+
+ // Now tell IP he's gone. We need to make sure that we don't tell him twice.
+ // To do this we set the context to NULL after we tell him the first time,
+ // and we check to make sure it's non-NULL before notifying him.
+
+ if (Interface->ai_context != NULL) {
+ IPDelInterface(Interface->ai_context);
+ Interface->ai_context = NULL;
+ }
+
+ // Finally, close him. We do this here so we can return a valid status.
+
+ CTEGetLock(&Interface->ai_lock, &LockHandle);
+
+ if (Interface->ai_handle != NULL) {
+ Handle = Interface->ai_handle;
+ Interface->ai_handle = NULL;
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+
+ CTEInitBlockStruc(&Interface->ai_block);
+ NdisCloseAdapter(&Status, Handle);
+
+ // Block for close to complete.
+ if (Status == NDIS_STATUS_PENDING)
+ Status = (NDIS_STATUS)CTEBlock(&Interface->ai_block);
+ } else {
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+ Status = NDIS_STATUS_SUCCESS;
+ }
+
+ *RetStatus = Status;
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+
+ FreeARPInterface(Interface);
+ }
+}
+
+extern ulong VIPTerminate;
+
+//* ARPUnloadProtocol - Unload.
+//
+// Called when we need to unload. All we do is call up to IP, and return.
+//
+// Input: Nothing.
+//
+// Returns: Nothing.
+//
+void NDIS_API
+ARPUnloadProtocol(void)
+{
+ NDIS_STATUS Status;
+
+#ifdef CHICAGO
+
+ IPULUnloadNotify();
+
+ if (VIPTerminate) {
+ NdisDeregisterProtocol(&Status, ARPHandle);
+ CTEUnload(NULL);
+ }
+
+#endif
+
+}
+
+#endif
+
diff --git a/private/ntos/tdi/tcpip/ip/arp.h b/private/ntos/tdi/tcpip/ip/arp.h
new file mode 100644
index 000000000..d294b2dce
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/arp.h
@@ -0,0 +1,21 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** ARP.H - Exports from ARP.
+//
+// This file contains the public definitons from ARP.
+extern int ARPInit(void);
+#ifndef _PNP_POWER
+extern int ARPRegister(PNDIS_STRING, void *, IPRcvRtn, IPTxCmpltRtn,
+ IPStatusRtn, IPTDCmpltRtn, IPRcvCmpltRtn, struct LLIPBindInfo *,
+ uint);
+#else
+int
+ARPRegister(PNDIS_STRING Adapter, uint *Flags, struct ARPInterface **Interface);
+#endif
+
+
+
diff --git a/private/ntos/tdi/tcpip/ip/arpdef.h b/private/ntos/tdi/tcpip/ip/arpdef.h
new file mode 100644
index 000000000..6278fdb60
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/arpdef.h
@@ -0,0 +1,340 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** arpdef.h - ARP definitions
+//
+// This file containes all of the private ARP related definitions.
+
+
+#define MEDIA_DIX 0
+#define MEDIA_TR 1
+#define MEDIA_FDDI 2
+#define MEDIA_ARCNET 3
+
+#define MAX_MEDIA 4
+
+#define INTERFACE_UP 0 // Interface is up.
+#define INTERFACE_INIT 1 // Interface is initializing.
+#define INTERFACE_DOWN 2 // Interface is down.
+
+#define LOOKAHEAD_SIZE 128 // A reasonable lookahead size
+
+// Definitions for state of an ATE. The 'RESOLVING' indicators must occur first.
+#define ARP_RESOLVING_LOCAL 0 // Address is being resolved (on local ring, if TR)
+#define ARP_RESOLVING_GLOBAL 1 // Address is being resolved globally.
+#define ARP_RESOLVING ARP_RESOLVING_GLOBAL
+#define ARP_GOOD 2 // ATE is good.
+#define ARP_BAD 3 // ATE is bad.
+#define ARP_FLOOD_RATE 1000L // No more than once a second.
+#define ARP_802_ADDR_LENGTH 6 // Length of an 802 address.
+
+#define MIN_ETYPE 0x600 // Minimum valid Ethertype
+#define SNAP_SAP 170
+#define SNAP_UI 3
+
+
+//* Structure of an Ethernet header.
+struct ENetHeader {
+ uchar eh_daddr[ARP_802_ADDR_LENGTH];
+ uchar eh_saddr[ARP_802_ADDR_LENGTH];
+ ushort eh_type;
+}; /* ENetHeader */
+
+typedef struct ENetHeader ENetHeader;
+
+//* Structure of a token ring header.
+struct TRHeader {
+ uchar tr_ac;
+ uchar tr_fc;
+ uchar tr_daddr[ARP_802_ADDR_LENGTH];
+ uchar tr_saddr[ARP_802_ADDR_LENGTH];
+}; /* TRHeader */
+#define ARP_AC 0x10
+#define ARP_FC 0x40
+#define TR_RII 0x80
+
+typedef struct TRHeader TRHeader;
+struct RC {
+ uchar rc_blen; // Broadcast indicator and length.
+ uchar rc_dlf; // Direction and largest frame.
+}; /* RC */
+#define RC_DIR 0x80
+#define RC_LENMASK 0x1f
+#define RC_SRBCST 0xc2 // Single route broadcast RC.
+#define RC_ARBCST 0x82 // All route broadcast RC.
+#define RC_LMASK 0x1F // Mask for length field for route
+ // information
+#define RC_LEN 0x2 // Length to put in the length bits
+ // when sending source routed
+ // frames
+#define RC_BCST_LEN 0x70 // Length for a broadcast.
+#define RC_LF_MASK 0x70 // Mask for length bits.
+
+typedef struct RC RC;
+
+//* Structure of source routing information.
+struct SRInfo {
+ RC sri_rc; // Routing control info.
+ ushort sri_rd[1]; // Routing designators.
+}; /* SRInfo */
+
+#define ARP_MAX_RD 8
+
+typedef struct SRInfo SRInfo;
+
+//* Structure of an FDDI header.
+struct FDDIHeader {
+ uchar fh_pri;
+ uchar fh_daddr[ARP_802_ADDR_LENGTH];
+ uchar fh_saddr[ARP_802_ADDR_LENGTH];
+}; /* FDDIHeader */
+
+typedef struct FDDIHeader FDDIHeader;
+
+#define ARP_FDDI_PRI 0x57
+#define ARP_FDDI_MSS 4352
+
+//* Structure of an ARCNET header.
+struct ARCNetHeader {
+ uchar ah_saddr;
+ uchar ah_daddr;
+ uchar ah_prot;
+}; /* ARCNetHeader */
+
+typedef struct ARCNetHeader ARCNetHeader;
+
+//* Structure of a SNAP header.
+struct SNAPHeader {
+ uchar sh_dsap;
+ uchar sh_ssap;
+ uchar sh_ctl;
+ uchar sh_protid[3];
+ ushort sh_etype;
+}; /* SNAPHeader */
+
+typedef struct SNAPHeader SNAPHeader;
+
+#define ARP_MAX_MEDIA_ENET sizeof(ENetHeader)
+#define ARP_MAX_MEDIA_TR (sizeof(TRHeader)+sizeof(RC)+(ARP_MAX_RD*sizeof(ushort))+sizeof(SNAPHeader))
+#define ARP_MAX_MEDIA_FDDI (sizeof(FDDIHeader)+sizeof(SNAPHeader))
+#define ARP_MAX_MEDIA_ARC sizeof(ARCNetHeader)
+
+#define ENET_BCAST_MASK 0x01
+#define TR_BCAST_MASK 0x80
+#define FDDI_BCAST_MASK 0x01
+#define ARC_BCAST_MASK 0xff
+
+#define ENET_BCAST_VAL 0x01
+#define TR_BCAST_VAL 0x80
+#define FDDI_BCAST_VAL 0x01
+#define ARC_BCAST_VAL 0x00
+
+#define ENET_BCAST_OFF 0x00
+#define TR_BCAST_OFF offsetof(struct TRHeader, tr_daddr)
+#define FDDI_BCAST_OFF offsetof(struct FDDIHeader, fh_daddr)
+#define ARC_BCAST_OFF offsetof(struct ARCNetHeader, ah_daddr)
+
+//* Structure of an ARP table entry.
+typedef struct ARPTableEntry {
+ struct ARPTableEntry *ate_next; // Next ATE in hash chain
+ ulong ate_valid; // Last time ATE was known to be valid.
+ IPAddr ate_dest; // IP address represented.
+ PNDIS_PACKET ate_packet; // Packet (if any) queued for resolution
+ RouteCacheEntry *ate_rce; // List of RCEs that reference this ATE.
+ DEFINE_LOCK_STRUCTURE(ate_lock) // Lock for this ATE.
+ uint ate_useticks; // Number of ticks left until this
+ // goes away.
+ uchar ate_addrlength; // Length of the address.
+ uchar ate_state; // State of this entry
+ uchar ate_addr[1]; // Address that maps to dest
+} ARPTableEntry;
+
+#define ALWAYS_VALID 0xffffffff
+
+//* Structure of the ARP table.
+#define ARP_TABLE_SIZE 32
+#define ARP_HASH(x) ((((uchar *)&(x))[3]) % ARP_TABLE_SIZE)
+typedef ARPTableEntry *ARPTable[];
+
+//* List structure for local representation of an IPAddress.
+typedef struct ARPIPAddr {
+ struct ARPIPAddr *aia_next; // Next in list.
+ uint aia_age;
+ IPAddr aia_addr; // The address.
+ IPMask aia_mask;
+ void *aia_context;
+} ARPIPAddr;
+
+#define ARPADDR_NOT_LOCAL 4
+#define ARPADDR_NEW_LOCAL 3
+#define ARPADDR_OLD_LOCAL 0
+
+//* List structure for Proxy-ARP addresses.
+typedef struct ARPPArpAddr {
+ struct ARPPArpAddr *apa_next; // Next in list.
+ IPAddr apa_addr; // The address.
+ IPMask apa_mask; // And the mask.
+} ARPPArpAddr;
+
+//* List structure for a multicast IP address.
+typedef struct ARPMCastAddr {
+ struct ARPMCastAddr *ama_next; // Next in list.
+ IPAddr ama_addr; // The (masked) address.
+ uint ama_refcnt; // Reference count for this address.
+} ARPMCastAddr;
+
+#define ARP_MCAST_MASK 0xffff7f00
+
+#define ARP_TIMER_TIME 1000L
+#define ARP_RESOLVE_TIMEOUT 1000L
+#define ARP_MIN_VALID_TIMEOUT 600000L
+
+#ifdef VXD
+#define ARP_DEFAULT_MAXHDRS 100
+#else
+#define ARP_DEFAULT_MAXHDRS 0xffffffff
+#endif
+
+typedef struct ARPBufferTracker {
+ struct ARPBufferTracker *abt_next;
+ NDIS_HANDLE abt_handle;
+ uchar *abt_buffer;
+} ARPBufferTracker;
+
+//* Structure of information we keep on a per-interface basis.
+typedef struct ARPInterface {
+ void *ai_context; // Upper layer context info.
+ NDIS_HANDLE ai_handle; // NDIS bind handle.
+ NDIS_MEDIUM ai_media; // Media type.
+ NDIS_HANDLE ai_bpool; // Handle for buffer pool.
+ NDIS_HANDLE ai_ppool; // Handle for packet pool.
+ DEFINE_LOCK_STRUCTURE(ai_lock) // Lock for this structure.
+ DEFINE_LOCK_STRUCTURE(ai_ARPTblLock) // ARP Table lock for this structure.
+#ifdef NT
+ SLIST_HEADER ai_sblist; // Free list of header buffers.
+#else
+ PNDIS_BUFFER ai_sblist; // Free list of header buffers.
+#endif
+ uchar *ai_bblist; // Free list of 'big' buffers.
+ ARPTable *ai_ARPTbl; // Pointer to the ARP table for this interface
+ ARPIPAddr ai_ipaddr; // Local IP address list.
+ ARPPArpAddr *ai_parpaddr; // Proxy ARP address list.
+ IPAddr ai_bcast; // Broadcast mask for this interface.
+ // SNMP required counters
+ uint ai_inoctets; // Input octets.
+ uint ai_inpcount[2]; // Count of nonunicast and unicast
+ // packets received.
+ uint ai_outoctets; // Output octets
+ uint ai_outpcount[2];// Count of nonunicast and unicast
+ // packets sent.
+ uint ai_qlen; // Output q length.
+ uchar ai_addr[ARP_802_ADDR_LENGTH]; // Local HW address.
+ uchar ai_sbsize; // Size of a small buffer
+ uchar ai_state; // State of the interface. Union of
+ // admin and operational states.
+ uchar ai_addrlen; // Length of ai_addr.
+ uchar ai_bcastmask; // Mask for checking unicast.
+ uchar ai_bcastval; // Value to check against.
+ uchar ai_bcastoff; // Offset in frame to check against.
+ uchar ai_hdrsize; // Size of 'typical' header.
+ uchar ai_snapsize; // Size of snap header, if any.
+ uchar ai_pad[2]; // PAD PAD
+ uint ai_pfilter; // Packet filter for this i/f.
+ uint ai_count; // Number of entries in the ARPTable.
+ uint ai_parpcount; // Number of proxy ARP entries.
+ CTETimer ai_timer; // ARP timer for this interface.
+ CTEBlockStruc ai_block; // Structure for blocking on.
+ ushort ai_mtu; // MTU for this interface.
+ uchar ai_adminstate; // Admin state.
+ uchar ai_operstate; // Operational state;
+ uint ai_speed; // Speed.
+ uint ai_lastchange; // Last change time.
+ uint ai_indiscards; // In discards.
+ uint ai_inerrors; // Input errors.
+ uint ai_uknprotos; // Unknown protocols received.
+ uint ai_outdiscards; // Output packets discarded.
+ uint ai_outerrors; // Output errors.
+ uint ai_desclen; // Length of desc. string.
+ uint ai_index; // Global I/F index ID.
+ uint ai_atinst; // AT instance number.
+ uint ai_ifinst; // IF instance number.
+ char *ai_desc; // Descriptor string.
+ ARPMCastAddr *ai_mcast; // Multicast list.
+ uint ai_mcastcnt; // Count of elements on mcast list.
+ void *ai_bbbase; // Base of big buffers.
+ uint ai_curhdrs; // Current number of headers.
+ uint ai_maxhdrs; // Maximum allowed number of headers.
+ ARPBufferTracker *ai_buflist; // List of header buffer handles.
+} ARPInterface;
+
+typedef struct ARPNotifyStruct {
+ CTEEvent ans_event;
+ uint ans_shutoff;
+ IPAddr ans_addr;
+ uint ans_hwaddrlen;
+ uchar ans_hwaddr[1];
+} ARPNotifyStruct;
+
+//* NOTE: These two values MUST stay at 0 and 1.
+#define AI_UCAST_INDEX 0
+#define AI_NONUCAST_INDEX 1
+
+#define ARP_DEFAULT_PACKETS 10 // Default to this many packets.
+#define ARP_DEFAULT_BUFFERS 50 // And this many buffers.
+
+#define ARP_HDRBUF_GROW_SIZE 32 // This many header buffers.
+
+//* Structure of information passed as context in RCE.
+typedef struct ARPContext {
+ RouteCacheEntry *ac_next; // Next RCE in ARP table chain.
+ ARPTableEntry *ac_ate; // Back pointer to ARP table entry.
+} ARPContext;
+
+typedef struct IPNMEContext {
+ uint inc_index;
+ ARPTableEntry *inc_entry;
+} IPNMEContext;
+
+#ifdef NT
+//
+// This structure must be packed under NT.
+//
+#include <packon.h>
+#endif // NT
+
+// Structure of an ARP header.
+struct ARPHeader {
+ ushort ah_hw; // Hardware address space.
+ ushort ah_pro; // Protocol address space.
+ uchar ah_hlen; // Hardware address length.
+ uchar ah_plen; // Protocol address length.
+ ushort ah_opcode; // Opcode.
+ uchar ah_shaddr[ARP_802_ADDR_LENGTH]; // Source HW address.
+ IPAddr ah_spaddr; // Source protocol address.
+ uchar ah_dhaddr[ARP_802_ADDR_LENGTH]; // Destination HW address.
+ IPAddr ah_dpaddr; // Destination protocol address.
+}; /* ARPHeader */
+
+#ifdef NT
+#include <packoff.h>
+#endif // NT
+
+typedef struct ARPHeader ARPHeader;
+
+#define ARP_ETYPE_IP 0x800
+#define ARP_ETYPE_ARP 0x806
+#define ARP_REQUEST 1
+#define ARP_RESPONSE 2
+#define ARP_HW_ENET 1
+#define ARP_HW_802 6
+#define ARP_HW_ARCNET 7
+
+#define ARP_ARCPROT_ARP 0xd5
+#define ARP_ARCPROT_IP 0xd4
+
+// The size we need to back off the buffer length because ARCNet address
+// are one bytes instead of six.
+#define ARCNET_ARPHEADER_ADJUSTMENT 10
diff --git a/private/ntos/tdi/tcpip/ip/dirs b/private/ntos/tdi/tcpip/ip/dirs
new file mode 100644
index 000000000..0dab2f056
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/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/tcpip/ip/icmp.c b/private/ntos/tdi/tcpip/ip/icmp.c
new file mode 100644
index 000000000..5faafa486
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/icmp.c
@@ -0,0 +1,1698 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** icmp.c - IP ICMP routines.
+//
+// This module contains all of the ICMP related routines.
+//
+
+#include "oscfg.h"
+#include "cxport.h"
+#include "ndis.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "icmp.h"
+#include "info.h"
+#include "iproute.h"
+#include "ipinit.h"
+#include "ipxmit.h"
+#include <icmpif.h>
+
+extern ProtInfo IPProtInfo[]; // Protocol information table.
+
+extern void *IPRegisterProtocol(uchar, void *, void *, void *, void *);
+extern ULStatusProc FindULStatus(uchar);
+extern uchar IPUpdateRcvdOptions(IPOptInfo *, IPOptInfo *, IPAddr, IPAddr);
+extern void IPInitOptions(IPOptInfo *);
+extern IP_STATUS IPCopyOptions(uchar *, uint, IPOptInfo *);
+extern IP_STATUS IPFreeOptions(IPOptInfo *);
+extern uchar IPGetLocalAddr(IPAddr, IPAddr *);
+void ICMPRouterTimer(NetTableEntry *);
+
+extern NDIS_HANDLE BufferPool;
+
+extern NetTableEntry *NetTableList; // Pointer to the net table list.
+extern ProtInfo *RawPI; // Raw IP protinfo
+
+DEFINE_LOCK_STRUCTURE(ICMPHeaderLock)
+ICMPHeader *ICMPHeaderList;
+uint CurrentICMPHeaders;
+uint MaxICMPHeaders;
+
+ICMPStats ICMPInStats;
+ICMPStats ICMPOutStats;
+
+
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+
+void ICMPInit(uint NumBuffers);
+
+IP_STATUS
+ICMPEchoRequest(
+ void *InputBuffer,
+ uint InputBufferLength,
+ EchoControl *ControlBlock,
+ EchoRtn Callback
+ );
+
+#pragma alloc_text(INIT, ICMPInit)
+#pragma alloc_text(PAGE, ICMPEchoRequest)
+
+#endif // ALLOC_PRAGMA
+#endif // NT
+
+
+//* UpdateICMPStats - Update ICMP statistics.
+//
+// A routine to update the ICMP statistics.
+//
+// Input: Stats - Pointer to stat. structure to update (input or output).
+// Type - Type of stat to update.
+//
+// Returns: Nothing.
+//
+void
+UpdateICMPStats(ICMPStats *Stats, uchar Type)
+{
+ switch (Type) {
+ case ICMP_DEST_UNREACH:
+ Stats->icmps_destunreachs++;
+ break;
+ case ICMP_TIME_EXCEED:
+ Stats->icmps_timeexcds++;
+ break;
+ case ICMP_PARAM_PROBLEM:
+ Stats->icmps_parmprobs++;
+ break;
+ case ICMP_SOURCE_QUENCH:
+ Stats->icmps_srcquenchs++;
+ break;
+ case ICMP_REDIRECT:
+ Stats->icmps_redirects++;
+ break;
+ case ICMP_TIMESTAMP:
+ Stats->icmps_timestamps++;
+ break;
+ case ICMP_TIMESTAMP_RESP:
+ Stats->icmps_timestampreps++;
+ break;
+ case ICMP_ECHO:
+ Stats->icmps_echos++;
+ break;
+ case ICMP_ECHO_RESP:
+ Stats->icmps_echoreps++;
+ break;
+ case ADDR_MASK_REQUEST:
+ Stats->icmps_addrmasks++;
+ break;
+ case ADDR_MASK_REPLY:
+ Stats->icmps_addrmaskreps++;
+ break;
+ default:
+ break;
+ }
+
+}
+
+//** GetICMPBuffer - Get an ICMP buffer, and allocate an NDIS_BUFFER that maps it.
+//
+// A routine to allocate an ICMP buffer and map an NDIS_BUFFER to it.
+//
+// Entry: Size - Size in bytes header buffer should be mapped as.
+// Buffer - Pointer to pointer to NDIS_BUFFER to return.
+//
+// Returns: Pointer to ICMP buffer if allocated, or NULL.
+//
+ICMPHeader *
+GetICMPBuffer(uint Size, PNDIS_BUFFER *Buffer)
+{
+ CTELockHandle Handle;
+ ICMPHeader **Header;
+ NDIS_STATUS Status;
+
+
+ CTEGetLock(&ICMPHeaderLock, &Handle);
+
+ Header = (ICMPHeader **)ICMPHeaderList;
+
+ if (Header == NULL) {
+ // Couldn't get a header from our free list. Try to allocate one.
+ Header = CTEAllocMem(sizeof(ICMPHeader) + sizeof(IPHeader) +
+ sizeof(IPHeader) + MAX_OPT_SIZE + 8);
+ if (Header == NULL) {
+ CTEFreeLock(&ICMPHeaderLock, Handle);
+ return (ICMPHeader *) NULL;
+ }
+ CurrentICMPHeaders++;
+ }
+ else {
+ ICMPHeaderList = *Header;
+ }
+
+ CTEFreeLock(&ICMPHeaderLock, Handle);
+
+ NdisAllocateBuffer(&Status, Buffer, BufferPool, Header, Size
+ + sizeof(IPHeader));
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+ NdisBufferLength(*Buffer) = Size;
+ Header = (ICMPHeader **)((uchar *)Header + sizeof(IPHeader));
+
+ (*(ICMPHeader **)&Header)->ich_xsum = 0;
+ return (ICMPHeader *)Header;
+ }
+
+ // Couldn't get an NDIS_BUFFER, free the ICMP buffer.
+ CTEGetLock(&ICMPHeaderLock, &Handle);
+
+ if (CurrentICMPHeaders > MaxICMPHeaders) {
+ CurrentICMPHeaders--;
+ CTEFreeMem(Header);
+ } else {
+ *Header = ICMPHeaderList;
+ ICMPHeaderList = (ICMPHeader *)Header;
+ }
+
+ CTEFreeLock(&ICMPHeaderLock, Handle);
+
+ return (ICMPHeader *)NULL;
+}
+
+//** FreeICMPBuffer - Free an ICMP buffer.
+//
+// This routine puts an ICMP buffer back on our free list.
+//
+// Entry: Buffer - Pointer to NDIS_BUFFER to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeICMPBuffer(PNDIS_BUFFER Buffer)
+{
+ CTELockHandle Handle;
+ ICMPHeader **Header;
+ uint Length;
+
+ NdisQueryBuffer(Buffer, (PVOID *)&Header, &Length);
+ CTEGetLock(&ICMPHeaderLock, &Handle);
+ if (CurrentICMPHeaders > MaxICMPHeaders) {
+ CurrentICMPHeaders--;
+ CTEFreeMem(Header);
+ } else {
+ *Header = ICMPHeaderList;
+ ICMPHeaderList = (ICMPHeader *)Header;
+ }
+
+ CTEFreeLock(&ICMPHeaderLock, Handle);
+ NdisFreeBuffer(Buffer);
+}
+
+//** DeleteEC - Remove an EchoControl from an NTE, and return a pointer to it.
+//
+// This routine is called when we need to remove an echo control structure from
+// an NTE. We walk the list of EC structures on the NTE, and if we find a match
+// we remove it and return a pointer to it.
+//
+// Entry: NTE - Pointer to NTE to be searched.
+// Seq - Seq. # identifting the EC.
+//
+// Returns: Pointer to the EC if it finds it.
+//
+EchoControl *
+DeleteEC(NetTableEntry *NTE, ushort Seq)
+{
+ EchoControl *Prev, *Current;
+ CTELockHandle Handle;
+
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ Prev = STRUCT_OF(EchoControl, &NTE->nte_echolist, ec_next);
+ Current = NTE->nte_echolist;
+ while(Current != (EchoControl *)NULL)
+ if (Current->ec_seq == Seq) {
+ Prev->ec_next = Current->ec_next;
+ break;
+ }
+ else {
+ Prev = Current;
+ Current = Current->ec_next;
+ }
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ return Current;
+
+}
+
+//** ICMPSendComplete< - Complete an ICMP send.
+//
+// This routine is called when an ICMP send completes. We free the header buffer,
+// the data buffer if there is one, and the NDIS_BUFFER chain.
+//
+// Entry: DataPtr - Pointer to data buffer, if any.
+// BufferChain - Pointer to NDIS_BUFFER chain.
+//
+// Returns: Nothing
+//
+void
+ICMPSendComplete(void *DataPtr, PNDIS_BUFFER BufferChain)
+{
+ PNDIS_BUFFER DataBuffer;
+
+ NdisGetNextBuffer(BufferChain, &DataBuffer);
+ FreeICMPBuffer(BufferChain);
+
+ if (DataBuffer != (PNDIS_BUFFER)NULL) { // We had data with this ICMP send.
+#ifdef DEBUG
+ if (DataPtr == (void *)NULL)
+ DEBUGCHK;
+#endif
+ CTEFreeMem(DataPtr);
+ NdisFreeBuffer(DataBuffer);
+ }
+
+}
+
+//* XsumBufChain - Checksum a chain of buffers.
+//
+// Called when we need to checksum an IPRcvBuf chain.
+//
+// Input: BufChain - Buffer chain to be checksummed.
+//
+// Returns: The checksum.
+//
+ushort
+XsumBufChain(IPRcvBuf *BufChain)
+{
+ ulong CheckSum = 0;
+
+ if (BufChain == NULL)
+ DEBUGCHK;
+
+ do {
+ CheckSum += (ulong)xsum(BufChain->ipr_buffer, BufChain->ipr_size);
+ BufChain = BufChain->ipr_next;
+ } while (BufChain != NULL);
+
+ // Fold the checksum down.
+ CheckSum = (CheckSum >> 16) + (CheckSum & 0xffff);
+ CheckSum += (CheckSum >> 16);
+
+ return (ushort)CheckSum;
+}
+
+
+//** SendEcho - Send an ICMP Echo or Echo response.
+//
+// This routine sends an ICMP echo or echo response. The Echo/EchoResponse may
+// carry data. If it does we'll copy the data here. The request may also have
+// options. Options are not copied, as the IPTransmit routine will copy options.
+//
+// Entry: Dest - Destination to send to.
+// Source - Source to send from.
+// Type - Type of request (ECHO or ECHO_RESP)
+// ID - ID of request.
+// Seq - Seq. # of request.
+// Data - Pointer to data (NULL if none).
+// DataLength - Length in bytes of data
+// OptInfo - Pointer to IP Options structure.
+//
+// Returns: IP_STATUS of request.
+//
+IP_STATUS
+SendEcho(IPAddr Dest, IPAddr Source, uchar Type, ushort ID, ushort Seq,
+ IPRcvBuf *Data, uint DataLength, IPOptInfo *OptInfo)
+{
+ uchar *DataBuffer = (uchar *)NULL; // Pointer to data buffer.
+ PNDIS_BUFFER HeaderBuffer, Buffer; // Buffers for our header and user data.
+ NDIS_STATUS Status;
+ ICMPHeader *Header;
+ ushort header_xsum;
+ IP_STATUS IStatus; // Status of transmit
+
+ ICMPOutStats.icmps_msgs++;
+
+ Header = GetICMPBuffer(sizeof(ICMPHeader), &HeaderBuffer);
+ if (Header == (ICMPHeader *)NULL) {
+ ICMPOutStats.icmps_errors++;
+ return IP_NO_RESOURCES;
+ }
+
+#ifdef DEBUG
+ if (Type != ICMP_ECHO_RESP && Type != ICMP_ECHO)
+ DEBUGCHK;
+#endif
+
+ Header->ich_type = Type;
+ Header->ich_code = 0;
+ *(ushort *)&Header->ich_param = ID;
+ *((ushort *)&Header->ich_param + 1) = Seq;
+ header_xsum = xsum(Header, sizeof(ICMPHeader));
+ Header->ich_xsum = ~header_xsum;
+
+ // If there's data, get a buffer and copy it now. If we can't do this fail the request.
+ if (DataLength != 0) {
+ ulong TempXsum;
+ uint BytesToCopy, CopyIndex;
+
+ DataBuffer = CTEAllocMem(DataLength);
+ if (DataBuffer == (void *)NULL) { // Couldn't get a buffer
+ FreeICMPBuffer(HeaderBuffer);
+ ICMPOutStats.icmps_errors++;
+ return IP_NO_RESOURCES;
+ }
+
+ BytesToCopy = DataLength;
+ CopyIndex = 0;
+ do {
+ uint CopyLength;
+#ifdef DEBUG
+ if (Data == NULL) {
+ DEBUGCHK;
+ break;
+ }
+#endif
+
+ CopyLength = MIN(BytesToCopy, Data->ipr_size);
+
+ CTEMemCopy(DataBuffer + CopyIndex, Data->ipr_buffer, CopyLength);
+ Data = Data->ipr_next;
+ CopyIndex += CopyLength;
+ BytesToCopy -= CopyLength;
+ } while (BytesToCopy);
+
+ NdisAllocateBuffer(&Status, &Buffer, BufferPool, DataBuffer, DataLength);
+ if (Status != NDIS_STATUS_SUCCESS) { // Couldn't get an NDIS_BUFFER
+ CTEFreeMem(DataBuffer);
+ FreeICMPBuffer(HeaderBuffer);
+ ICMPOutStats.icmps_errors++;
+ return IP_NO_RESOURCES;
+ }
+
+ // Compute rest of xsum.
+ TempXsum = (ulong)header_xsum + (ulong)xsum(DataBuffer, DataLength);
+ TempXsum = (TempXsum >> 16) + (TempXsum & 0xffff);
+ TempXsum += (TempXsum >> 16);
+ Header->ich_xsum = ~(ushort)TempXsum;
+ NDIS_BUFFER_LINKAGE(HeaderBuffer) = Buffer;
+ }
+
+
+ UpdateICMPStats(&ICMPOutStats, Type);
+
+ IStatus = IPTransmit(IPProtInfo, DataBuffer, HeaderBuffer,
+ DataLength + sizeof(ICMPHeader), Dest, Source, OptInfo, NULL,
+ PROT_ICMP);
+
+ if (IStatus != IP_PENDING)
+ ICMPSendComplete(DataBuffer, HeaderBuffer);
+
+ return IStatus;
+}
+
+//** SendICMPMsg - Send an ICMP message
+//
+// This is the general ICMP message sending routine, called for most ICMP sends besides
+// echo. Basically, all we do is get a buffer, format the info, copy the input
+// header, and send the message.
+//
+// Entry: Src - IPAddr of source.
+// Dest - IPAddr of destination
+// Type - Type of request.
+// Code - Subcode of request.
+// Pointer - Pointer value for request.
+// Data - Pointer to data (NULL if none).
+// DataLength - Length in bytes of data
+//
+// Returns: IP_STATUS of request.
+//
+IP_STATUS
+SendICMPMsg(IPAddr Src, IPAddr Dest, uchar Type, uchar Code, ulong Pointer,
+ uchar *Data, uchar DataLength)
+{
+ PNDIS_BUFFER HeaderBuffer; // Buffer for our header
+ ICMPHeader *Header;
+ IP_STATUS IStatus; // Status of transmit
+ IPOptInfo OptInfo; // Options for this transmit.
+
+
+ ICMPOutStats.icmps_msgs++;
+
+ Header = GetICMPBuffer(sizeof(ICMPHeader) + DataLength, &HeaderBuffer);
+ if (Header == (ICMPHeader *)NULL) {
+ ICMPOutStats.icmps_errors++;
+ return IP_NO_RESOURCES;
+ }
+
+
+ Header->ich_type = Type;
+ Header->ich_code = Code;
+ Header->ich_param = Pointer;
+ if (Data)
+ CTEMemCopy(Header + 1, Data, DataLength);
+ Header->ich_xsum = ~xsum(Header, sizeof(ICMPHeader) + DataLength);
+
+ IPInitOptions(&OptInfo);
+
+ UpdateICMPStats(&ICMPOutStats, Type);
+
+ IStatus = IPTransmit(IPProtInfo, NULL, HeaderBuffer,
+ DataLength + sizeof(ICMPHeader), Dest, Src, &OptInfo, NULL,
+ PROT_ICMP);
+
+ if (IStatus != IP_PENDING)
+ ICMPSendComplete(NULL, HeaderBuffer);
+
+ return IStatus;
+
+}
+
+//** SendICMPErr - Send an ICMP error message
+//
+// This is the routine used to send an ICMP error message, such as Destination Unreachable.
+// We examine the header to find the length of the data, and also make sure we're not
+// replying to another ICMP error message or a broadcast message. Then we call SendICMPMsg
+// to send it.
+//
+// Entry: Src - IPAddr of source.
+// Header - Pointer to IP Header that caused the problem.
+// Type - Type of request.
+// Code - Subcode of request.
+// Pointer - Pointer value for request.
+//
+// Returns: IP_STATUS of request.
+//
+IP_STATUS
+SendICMPErr(IPAddr Src, IPHeader UNALIGNED *Header, uchar Type, uchar Code,
+ ulong Pointer)
+{
+ uchar HeaderLength; // Length in bytes if header.
+ uchar DType;
+
+ HeaderLength = (Header->iph_verlen & (uchar)~IP_VER_FLAG) << 2;
+
+ if (Header->iph_protocol == PROT_ICMP) {
+ ICMPHeader UNALIGNED *ICH = (ICMPHeader UNALIGNED *)
+ ((uchar *)Header + HeaderLength);
+
+ if (ICH->ich_type != ICMP_ECHO)
+ return IP_SUCCESS;
+ }
+
+ // Don't respond to sends to a broadcast destination.
+ DType = GetAddrType(Header->iph_dest);
+ if (DType == DEST_INVALID || IS_BCAST_DEST(DType))
+ return IP_SUCCESS;
+
+ // Don't respond if the source address is bad.
+ DType = GetAddrType(Header->iph_src);
+ if (DType == DEST_INVALID || IS_BCAST_DEST(DType) ||
+ (IP_LOOPBACK(Header->iph_dest) && DType != DEST_LOCAL))
+ return IP_SUCCESS;
+
+ // Make sure the source we're sending from is good.
+ if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR) || GetAddrType(Src) != DEST_LOCAL)
+ return IP_SUCCESS;
+
+ // Double check to make sure it's an initial fragment.
+ if ((Header->iph_offset & IP_OFFSET_MASK) != 0)
+ return IP_SUCCESS;
+
+ return SendICMPMsg(Src, Header->iph_src, Type, Code, Pointer, (uchar *)Header,
+ (uchar)(HeaderLength + 8));
+
+}
+
+
+//** ICMPTimer - Timer for ICMP
+//
+// This is the timer routine called periodically by global IP timer. We walk through
+// the list of pending pings, and if we find one that's timed out we remove it and
+// call the finish routine.
+//
+// Entry: NTE - Pointer to NTE being timed out.
+//
+// Returns: Nothing
+//
+void
+ICMPTimer(NetTableEntry *NTE)
+{
+ CTELockHandle Handle;
+ EchoControl *TimeoutList = (EchoControl *)NULL; // Timed out entries.
+ EchoControl *Prev, *Current;
+ ulong Now = CTESystemUpTime();
+
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ Prev = STRUCT_OF(EchoControl, &NTE->nte_echolist, ec_next);
+ Current = NTE->nte_echolist;
+ while(Current != (EchoControl *)NULL)
+ if ((Current->ec_active) && (Current->ec_to < Now)) { // This one's timed out.
+ Prev->ec_next = Current->ec_next;
+ // Link him on timed out list.
+ Current->ec_next = TimeoutList;
+ TimeoutList = Current;
+ Current = Prev->ec_next;
+ }
+ else {
+ Prev = Current;
+ Current = Current->ec_next;
+ }
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ // Now go through the timed out entries, and call the completion routine.
+ while (TimeoutList != (EchoControl *)NULL) {
+ EchoRtn Rtn;
+
+ Current = TimeoutList;
+ TimeoutList = Current->ec_next;
+
+ Rtn = (EchoRtn)Current->ec_rtn;
+ (*Rtn)(Current, IP_REQ_TIMED_OUT, NULL, 0, NULL);
+ }
+
+ //
+ // [BUGBUG] Disabled for 4.0 sp2
+ //
+ // ICMPRouterTimer(NTE);
+
+}
+
+//* CompleteEcho - Complete an echo request.
+//
+// Called when we need to complete an echo request, either because of a response
+// or a received ICMP error message. We look it up, and then call the completion routine.
+//
+// Input: Header - Pointer to ICMP header causing completion.
+// Status - Final status of request.
+// Data - Data to be returned, if any.
+// DataSize - Size in bytes of data.
+// OptInfo - Option info structure.
+//
+// Returns: Nothing.
+//
+void
+CompleteEcho(ICMPHeader UNALIGNED *Header, IP_STATUS Status, IPRcvBuf *Data, uint DataSize,
+ IPOptInfo *OptInfo)
+{
+ ushort NTEContext;
+ EchoControl *EC;
+ EchoRtn Rtn;
+ NetTableEntry *NTE;
+
+ // Look up and remove the matching echo control block.
+ NTEContext = (*(ushort UNALIGNED *)&Header->ich_param);
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next)
+ if (NTEContext == NTE->nte_context)
+ break;
+
+ if (NTE == NULL)
+ return; // Bad context value.
+
+ EC = DeleteEC(NTE, *(((ushort UNALIGNED *)&Header->ich_param) + 1));
+ if (EC != (EchoControl *)NULL) { // Found a match.
+ Rtn = (EchoRtn)EC->ec_rtn;
+ (*Rtn)(EC, Status, Data, DataSize, OptInfo);
+ }
+
+
+}
+
+//** ICMPStatus - ICMP status handling procedure.
+//
+// This is the procedure called during a status change, either from an incoming ICMP
+// message or a hardware status change. ICMP ignores most of these, unless we get an
+// ICMP status message that was caused be an echo request. In that case we will complete
+// the corresponding echo request with the appropriate error code.
+//
+// Input: StatusType - Type of status (NET or HW)
+// StatusCode - Code identifying IP_STATUS.
+// OrigDest - If this is net status, the original dest. of DG that triggered it.
+// OrigSrc - " " " " " , the original src.
+// Src - IP address of status originator (could be local or remote).
+// Param - Additional information for status - i.e. the param field of
+// an ICMP message.
+// Data - Data pertaining to status - for net status, this is the first
+// 8 bytes of the original DG.
+//
+// Returns: Nothing
+//
+void
+ICMPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, IPAddr OrigSrc, IPAddr Src,
+ ulong Param, void *Data)
+{
+ if (StatusType == IP_NET_STATUS) {
+ ICMPHeader UNALIGNED *ICH = (ICMPHeader UNALIGNED *)Data;
+ // ICH is the datagram that caused the message.
+
+ if (ICH->ich_type == ICMP_ECHO) { // And it was an echo request.
+ IPRcvBuf RcvBuf;
+
+ RcvBuf.ipr_next = NULL;
+ RcvBuf.ipr_buffer = (uchar *)&Src;
+ RcvBuf.ipr_size = sizeof(IPAddr);
+ CompleteEcho(ICH, StatusCode, &RcvBuf, sizeof(IPAddr), NULL);
+ }
+ }
+
+}
+
+//* ICMPMapStatus - Map an ICMP error to an IP status code.
+//
+// Called by ICMP status when we need to map from an incoming ICMP error code and type
+// to an ICMP status.
+//
+// Entry: Type - Type of ICMP error.
+// Code - Subcode of error.
+//
+// Returns: Corresponding IP status.
+//
+IP_STATUS
+ICMPMapStatus(uchar Type, uchar Code)
+{
+ switch (Type) {
+
+ case ICMP_DEST_UNREACH:
+ switch (Code) {
+ case NET_UNREACH:
+ case HOST_UNREACH:
+ case PROT_UNREACH:
+ case PORT_UNREACH:
+ return IP_DEST_UNREACH_BASE + Code;
+ break;
+ case FRAG_NEEDED:
+ return IP_PACKET_TOO_BIG;
+ break;
+ case SR_FAILED:
+ return IP_BAD_ROUTE;
+ break;
+ case DEST_NET_UNKNOWN:
+ case SRC_ISOLATED:
+ case DEST_NET_ADMIN:
+ case NET_UNREACH_TOS:
+ return IP_DEST_NET_UNREACHABLE;
+ break;
+ case DEST_HOST_UNKNOWN:
+ case DEST_HOST_ADMIN:
+ case HOST_UNREACH_TOS:
+ return IP_DEST_HOST_UNREACHABLE;
+ break;
+ default:
+ return IP_DEST_NET_UNREACHABLE;
+ }
+ break;
+ case ICMP_TIME_EXCEED:
+ if (Code == TTL_IN_TRANSIT)
+ return IP_TTL_EXPIRED_TRANSIT;
+ else
+ return IP_TTL_EXPIRED_REASSEM;
+ break;
+ case ICMP_PARAM_PROBLEM:
+ return IP_PARAM_PROBLEM;
+ break;
+ case ICMP_SOURCE_QUENCH:
+ return IP_SOURCE_QUENCH;
+ break;
+ default:
+ return IP_GENERAL_FAILURE;
+ break;
+ }
+
+}
+
+void
+SendRouterSolicitation(NetTableEntry *NTE)
+{
+ if (NTE->nte_rtrdiscovery) {
+ SendICMPMsg(NTE->nte_addr, NTE->nte_rtrdiscaddr, ICMP_ROUTER_SOLICITATION,
+ 0, 0, NULL, 0);
+ }
+}
+
+//** ICMPRouterTimer - Timeout default gateway entries
+//
+// This is the router advertisement timeout handler. When a router
+// advertisement is received, we add the routers to our default gateway
+// list if applicable. We then run a timer on the entries and refresh
+// the list as new advertisements are received. If we fail to hear an
+// update for a router within the specified lifetime we will delete the
+// route from our routing tables.
+//
+
+void
+ICMPRouterTimer(NetTableEntry *NTE)
+{
+ CTELockHandle Handle;
+ IPRtrEntry *rtrentry;
+ IPRtrEntry *temprtrentry;
+ IPRtrEntry *lastrtrentry = NULL;
+ uint SendIt = FALSE;
+
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ rtrentry = NTE->nte_rtrlist;
+ while (rtrentry != NULL) {
+ if (--rtrentry->ire_lifetime == 0) {
+ if (lastrtrentry == NULL) {
+ NTE->nte_rtrlist = rtrentry->ire_next;
+ } else {
+ lastrtrentry->ire_next = rtrentry->ire_next;
+ }
+ temprtrentry = rtrentry;
+ rtrentry = rtrentry->ire_next;
+// DbgPrint("DeleteRoute: RtrAddr = %08x\n",temprtrentry->ire_addr);
+ DeleteRoute(NULL_IP_ADDR, DEFAULT_MASK,
+ temprtrentry->ire_addr, NTE->nte_if);
+ CTEFreeMem(temprtrentry);
+ } else {
+ lastrtrentry = rtrentry;
+ rtrentry = rtrentry->ire_next;
+ }
+ }
+ if (NTE->nte_rtrdisccount != 0) {
+ NTE->nte_rtrdisccount--;
+ if ((NTE->nte_rtrdiscstate == NTE_RTRDISC_SOLICITING) &&
+ ((NTE->nte_rtrdisccount%SOLICITATION_INTERVAL) == 0)) {
+ SendIt = TRUE;
+ }
+ if ((NTE->nte_rtrdiscstate == NTE_RTRDISC_DELAYING) &&
+ (NTE->nte_rtrdisccount == 0)) {
+ NTE->nte_rtrdisccount = (SOLICITATION_INTERVAL)*(MAX_SOLICITATIONS-1);
+ NTE->nte_rtrdiscstate = NTE_RTRDISC_SOLICITING;
+ SendIt = TRUE;
+ }
+ }
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ if (SendIt) {
+ SendRouterSolicitation(NTE);
+ }
+
+}
+
+//** ProcessRouterAdvertisement - Process a router advertisement
+//
+// This is the router advertisement handler. When a router advertisement
+// is received, we add the routers to our default gateway list if applicable.
+//
+
+uint
+ProcessRouterAdvertisement(IPAddr Src, IPAddr LocalAddr, NetTableEntry *NTE,
+ ICMPRouterAdHeader UNALIGNED *AdHeader, IPRcvBuf *RcvBuf, uint Size)
+{
+ uchar NumAddrs = AdHeader->irah_numaddrs;
+ uchar AddrEntrySize = AdHeader->irah_addrentrysize;
+ ushort Lifetime = net_short(AdHeader->irah_lifetime);
+ ICMPRouterAdAddrEntry UNALIGNED *RouterAddr = (ICMPRouterAdAddrEntry UNALIGNED *)RcvBuf->ipr_buffer;
+ uint i;
+ CTELockHandle Handle;
+ IPRtrEntry *rtrentry;
+ IPRtrEntry *lastrtrentry = NULL;
+ int Update = FALSE;
+
+// DbgPrint("ProcessRouterAdvertisement: NumAddrs = %d\n",NumAddrs);
+// DbgPrint("ProcessRouterAdvertisement: AddrEntrySize = %d\n",AddrEntrySize);
+// DbgPrint("ProcessRouterAdvertisement: Lifetime = %d\n",Lifetime);
+
+ if ((NumAddrs == 0) || (AddrEntrySize < 2)) // per rfc 1256
+ return FALSE;
+
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ for ( i=0; i<NumAddrs; i++, RouterAddr++) {
+ if ((RouterAddr->irae_addr & NTE->nte_mask) != (NTE->nte_addr & NTE->nte_mask)) {
+ continue;
+ }
+ if (!IsRouteICMP(NULL_IP_ADDR, DEFAULT_MASK, RouterAddr->irae_addr, NTE->nte_if)) {
+ continue;
+ }
+
+// DbgPrint("ProcessRouterAdvertisement: RtrAddr = %08x\n",RouterAddr->irae_addr);
+// DbgPrint("ProcessRouterAdvertisement: RtrPreference = %d\n",net_long(RouterAddr->irae_preference));
+ rtrentry = NTE->nte_rtrlist;
+ while (rtrentry != NULL) {
+ if (rtrentry->ire_addr == RouterAddr->irae_addr) {
+ rtrentry->ire_lifetime = Lifetime*2;
+ if (rtrentry->ire_preference != RouterAddr->irae_preference) {
+ rtrentry->ire_preference = RouterAddr->irae_preference;
+ Update = TRUE;
+ }
+ break;
+ }
+ lastrtrentry = rtrentry;
+ rtrentry = rtrentry->ire_next;
+ }
+
+ if (rtrentry == NULL) {
+ rtrentry = (IPRtrEntry *) CTEAllocMem(sizeof(IPRtrEntry));
+ if (rtrentry == NULL) {
+ return FALSE;
+ }
+ rtrentry->ire_next = NULL;
+ rtrentry->ire_addr = RouterAddr->irae_addr;
+ rtrentry->ire_preference = RouterAddr->irae_preference;
+ rtrentry->ire_lifetime = Lifetime*2;
+ if (lastrtrentry == NULL) {
+ NTE->nte_rtrlist = rtrentry;
+ } else {
+ lastrtrentry->ire_next = rtrentry;
+ }
+ Update = TRUE;
+ }
+
+ if (Update && (RouterAddr->irae_preference != (long)0x00000080)) { // per rfc 1256
+// DbgPrint("AddRoute: RtrAddr = %08x\n",RouterAddr->irae_addr);
+ AddRoute(NULL_IP_ADDR, DEFAULT_MASK, RouterAddr->irae_addr,
+ NTE->nte_if, NTE->nte_mss,
+ (uint)(1000-net_long(RouterAddr->irae_preference)), // invert for metric
+ IRE_PROTO_ICMP, ATYPE_OVERRIDE, NULL);
+ }
+ Update = FALSE;
+ }
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ return TRUE;
+}
+
+//** ICMPRcv - Receive an ICMP datagram.
+//
+// Called by the main IP code when we receive an ICMP datagram. The action we
+// take depends on what the DG is. For some DGs, we call upper layer status
+// handlers. For Echo Requests, we call the echo responder.
+//
+// Entry: NTE - Pointer to NTE on which ICMP message was received.
+// Dest - IPAddr of destionation.
+// Src - IPAddr of source
+// LocalAddr - Local address of network which caused this to be
+// received.
+// SrcAddr - Address of local interface which received the
+// packet
+// IPHdr - Pointer to IP Header
+// IPHdrLength - Bytes in Header.
+// RcvBuf - ICMP message buffer.
+// Size - Size in bytes of ICMP message.
+// IsBCast - Boolean indicator of whether or not this came in
+// as a bcast.
+// Protocol - Protocol this came in on.
+// OptInfo - Pointer to info structure for received options.
+//
+// Returns: Status of reception
+//
+IP_STATUS
+ICMPRcv(NetTableEntry *NTE, IPAddr Dest, IPAddr Src, IPAddr LocalAddr,
+ IPAddr SrcAddr, IPHeader UNALIGNED *IPHdr, uint IPHdrLength,
+ IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, uchar Protocol,
+ IPOptInfo *OptInfo)
+{
+ ICMPHeader UNALIGNED *Header;
+ void *Data; // Pointer to data received.
+ IPHeader UNALIGNED *IPH; // Pointer to IP Header in error messages.
+ uint HeaderLength; // Size of IP header.
+ ULStatusProc ULStatus; // Pointer to upper layer status procedure.
+ IPOptInfo NewOptInfo;
+ uchar DType;
+ uint PassUp = FALSE;
+
+
+ ICMPInStats.icmps_msgs++;
+
+ DType = GetAddrType(Src);
+ if (Size < sizeof(ICMPHeader) || DType == DEST_INVALID ||
+ IS_BCAST_DEST(DType) || (IP_LOOPBACK(Dest) && DType != DEST_LOCAL) ||
+ XsumBufChain(RcvBuf) != (ushort)0xffff) {
+ ICMPInStats.icmps_errors++;
+ return IP_SUCCESS; // Bad checksum.
+ }
+
+ Header = (ICMPHeader UNALIGNED *)RcvBuf->ipr_buffer;
+
+
+ RcvBuf->ipr_buffer += sizeof(ICMPHeader);
+ RcvBuf->ipr_size -= sizeof(ICMPHeader);
+
+ // Set up the data pointer for most requests, i.e. those that take less
+ // than MIN_FIRST_SIZE data.
+
+ if (Size -= sizeof(ICMPHeader))
+ Data = (void *)(Header + 1);
+ else
+ Data = (void *)NULL;
+
+ switch (Header->ich_type) {
+
+ case ICMP_DEST_UNREACH:
+ case ICMP_TIME_EXCEED:
+ case ICMP_PARAM_PROBLEM:
+ case ICMP_SOURCE_QUENCH:
+ case ICMP_REDIRECT:
+
+ if (IsBCast)
+ return IP_SUCCESS; // ICMP doesn't respond to bcast requests.
+
+ if (Data == NULL || Size < sizeof(IPHeader)) {
+ ICMPInStats.icmps_errors++;
+ return IP_SUCCESS; // No data, error.
+ }
+
+ IPH = (IPHeader UNALIGNED *)Data;
+ HeaderLength = (IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2;
+ if (Size < (HeaderLength + MIN_ERRDATA_LENGTH)) {
+ ICMPInStats.icmps_errors++;
+ return IP_SUCCESS; // Not enough data for this
+ // ICMP message.
+ }
+
+ // Make sure that the source address of the datagram that triggered
+ // the message is one of ours.
+
+ if (GetAddrType(IPH->iph_src) != DEST_LOCAL) {
+ ICMPInStats.icmps_errors++;
+ return IP_SUCCESS; // Bad src in header.
+ }
+
+ if (Header->ich_type != ICMP_REDIRECT) {
+
+ UpdateICMPStats(&ICMPInStats, Header->ich_type);
+
+ if (ULStatus = FindULStatus(IPH->iph_protocol)) {
+ (void)(*ULStatus)(IP_NET_STATUS,
+ ICMPMapStatus(Header->ich_type, Header->ich_code),
+ IPH->iph_dest, IPH->iph_src, Src, Header->ich_param,
+ (uchar *)IPH + HeaderLength);
+ }
+ if (Header->ich_code == FRAG_NEEDED)
+ RouteFragNeeded(
+ IPH,
+ (ushort)net_short(
+ *((ushort UNALIGNED *)&Header->ich_param + 1)
+ )
+ );
+ } else {
+ ICMPInStats.icmps_redirects++;
+ Redirect(NTE, Src, IPH->iph_dest, IPH->iph_src,
+ Header->ich_param);
+ }
+
+ PassUp = TRUE;
+
+ break;
+
+
+ case ICMP_ECHO_RESP:
+ if (IsBCast)
+ return IP_SUCCESS; // ICMP doesn't respond to bcast requests.
+ ICMPInStats.icmps_echoreps++;
+ // Look up and remove the matching echo control block.
+ CompleteEcho(Header, IP_SUCCESS, RcvBuf, Size, OptInfo);
+
+ PassUp = TRUE;
+
+ break;
+
+ case ICMP_ECHO:
+ if (IsBCast)
+ return IP_SUCCESS; // ICMP doesn't respond to bcast requests.
+ ICMPInStats.icmps_echos++;
+
+ // Create our new optinfo structure.
+ IPInitOptions(&NewOptInfo);
+ NewOptInfo.ioi_tos = OptInfo->ioi_tos;
+ NewOptInfo.ioi_flags = OptInfo->ioi_flags;
+
+ // If we have options, we need to reverse them and update any
+ // record route info. We can use the option buffer supplied by the
+ // IP layer, since we're part of him.
+ if (OptInfo->ioi_options != (uchar *)NULL)
+ IPUpdateRcvdOptions(OptInfo, &NewOptInfo, Src, LocalAddr);
+
+
+ SendEcho(Src, LocalAddr, ICMP_ECHO_RESP,
+ *(ushort UNALIGNED *)&Header->ich_param,
+ *((ushort UNALIGNED *)&Header->ich_param + 1),
+ RcvBuf, Size, &NewOptInfo);
+
+ IPFreeOptions(&NewOptInfo);
+ break;
+
+ case ADDR_MASK_REQUEST:
+ if (IsBCast)
+ return IP_SUCCESS; // ICMP doesn't respond to bcast requests.
+ ICMPInStats.icmps_addrmasks++;
+
+ Dest = Src;
+
+ SendICMPMsg(LocalAddr, Dest, ADDR_MASK_REPLY, 0, Header->ich_param,
+ (uchar *)&NTE->nte_mask, sizeof(IPMask));
+ break;
+
+ case ICMP_ROUTER_ADVERTISEMENT:
+ if (Header->ich_code != 0)
+ return IP_SUCCESS; // Code must be 0 as per RFC1256
+ if (NTE->nte_rtrdiscovery) {
+ if (!ProcessRouterAdvertisement(Src, LocalAddr, NTE,
+ (ICMPRouterAdHeader *)&Header->ich_param, RcvBuf, Size))
+ return IP_SUCCESS; // An error was returned
+ }
+ PassUp = TRUE;
+ break;
+
+ case ICMP_ROUTER_SOLICITATION:
+ if (Header->ich_code != 0)
+ return IP_SUCCESS; // Code must be 0 as per RFC1256
+ PassUp = TRUE;
+ break;
+
+ default:
+ PassUp = TRUE;
+ UpdateICMPStats(&ICMPInStats, Header->ich_type);
+ break;
+ }
+
+ //
+ // Pass the packet up to the raw layer if applicable.
+ //
+ if (PassUp && (RawPI != NULL)) {
+ if (RawPI->pi_rcv != NULL) {
+ //
+ // Restore the original values.
+ //
+ RcvBuf->ipr_buffer -= sizeof(ICMPHeader);
+ RcvBuf->ipr_size += sizeof(ICMPHeader);
+ Size += sizeof(ICMPHeader);
+ Data = (void *) Header;
+
+ (*(RawPI->pi_rcv))(NTE, Dest, Src, LocalAddr, SrcAddr, IPHdr,
+ IPHdrLength, RcvBuf, Size, IsBCast, Protocol, OptInfo);
+ }
+ }
+
+ return IP_SUCCESS;
+}
+
+
+//** ICMPEcho - Send an echo to the specified address.
+//
+// Entry: ControlBlock - Pointer to an EchoControl structure. This structure
+// must remain valid until the req. completes.
+// Timeout - Time in milliseconds to wait for response.
+// Data - Pointer to data to send with echo.
+// DataSize - Size in bytes of data.
+// Callback - Routine to call when request is responded to or times out.
+// Dest - Address to be pinged.
+// OptInfo - Pointer to opt info structure to use for ping.
+//
+// Returns: IP_STATUS of attempt to ping..
+//
+IP_STATUS
+ICMPEcho(EchoControl *ControlBlock, ulong Timeout, void *Data, uint DataSize, EchoRtn Callback,
+ IPAddr Dest, IPOptInfo *OptInfo)
+{
+ IPAddr Dummy;
+ NetTableEntry *NTE;
+ CTELockHandle Handle;
+ ushort Seq;
+ IP_STATUS Status;
+ IPOptInfo NewOptInfo;
+ IPRcvBuf RcvBuf;
+ uint MTU;
+ Interface *IF;
+ uchar DType;
+ EchoControl *Current;
+
+ if (OptInfo->ioi_ttl == 0)
+ return IP_BAD_OPTION;
+
+ IPInitOptions(&NewOptInfo);
+ NewOptInfo.ioi_ttl = OptInfo->ioi_ttl;
+ NewOptInfo.ioi_flags = OptInfo->ioi_flags;
+ NewOptInfo.ioi_tos = OptInfo->ioi_tos & 0xfc;
+
+ if (OptInfo->ioi_optlength != 0) {
+ Status = IPCopyOptions(OptInfo->ioi_options, OptInfo->ioi_optlength,
+ &NewOptInfo);
+
+ if (Status != IP_SUCCESS)
+ return Status;
+ }
+
+ if (!IP_ADDR_EQUAL(NewOptInfo.ioi_addr, NULL_IP_ADDR))
+ Dest = NewOptInfo.ioi_addr;
+
+ DType = GetAddrType(Dest);
+ if (DType == DEST_INVALID) {
+ IPFreeOptions(&NewOptInfo);
+ return IP_BAD_DESTINATION;
+ }
+
+ if ((IF = LookupNextHopWithBuffer(Dest, NULL_IP_ADDR, &Dummy, &MTU, 0x1, NULL, 0)) == NULL) {
+ IPFreeOptions(&NewOptInfo);
+ return IP_DEST_HOST_UNREACHABLE; // Don't know how to get there.
+ }
+
+ // Loop through the NetTable, looking for a matching NTE.
+ CTEGetLock(&RouteTableLock, &Handle);
+ if (DHCPActivityCount != 0)
+ NTE = NULL;
+ else
+ NTE = BestNTEForIF(Dummy, IF);
+ CTEFreeLock(&RouteTableLock, Handle);
+
+#ifdef _PNP_POWER
+ // We're done with the interface, so dereference it.
+ DerefIF(IF);
+#endif
+
+ if (NTE == NULL) {
+ // Couldn't find a matching NTE. This is very bad.
+ //DEBUGCHK;
+
+ DbgPrint("ICMP: Failed to find NTE when going to %x\n",Dest);
+
+ IPFreeOptions(&NewOptInfo);
+ return IP_DEST_HOST_UNREACHABLE;
+ }
+
+ // Figure out the timeout.
+ ControlBlock->ec_to = CTESystemUpTime() + Timeout;
+ ControlBlock->ec_rtn = Callback;
+ ControlBlock->ec_active = 0; // Prevent from timing out until sent
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ // Link onto ping list, and get seq. # */
+ Seq = ++NTE->nte_icmpseq;
+ ControlBlock->ec_seq = Seq;
+ ControlBlock->ec_next = NTE->nte_echolist;
+ NTE->nte_echolist = ControlBlock;
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ RcvBuf.ipr_next = NULL;
+ RcvBuf.ipr_buffer = Data;
+ RcvBuf.ipr_size = DataSize;
+ Status = SendEcho(Dest, NTE->nte_addr, ICMP_ECHO, NTE->nte_context,
+ Seq, &RcvBuf, DataSize, &NewOptInfo);
+
+ IPFreeOptions(&NewOptInfo);
+
+ if (Status != IP_PENDING && Status != IP_SUCCESS) { // We had an error on the send.
+ if (DeleteEC(NTE, Seq) != (EchoControl *)NULL)
+ return Status; // We found it.
+ }
+
+ //
+ // If the request is still pending, activate the timer
+ //
+ CTEGetLock(&NTE->nte_lock, &Handle);
+
+ for (
+ Current = NTE->nte_echolist;
+ Current != (EchoControl *)NULL;
+ Current = Current->ec_next
+ ) {
+ if (Current == ControlBlock) {
+ ControlBlock->ec_active = 1; // start the timer
+ break;
+ }
+ }
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ return IP_PENDING;
+
+}
+
+
+//** ICMPEchoRequest - Common dispatch routine for echo requests
+//
+// This is the routine called by the OS-specific code on behalf of a user to issue an
+// echo request.
+//
+// Entry: InputBuffer - Pointer to an ICMP_ECHO_REQUEST structure.
+// InputBufferLength - Size in bytes of the InputBuffer.
+// ControlBlock - Pointer to an EchoControl structure. This
+// structure must remain valid until the
+// request completes.
+// Callback - Routine to call when request is responded to
+// or times out.
+//
+// Returns: IP_STATUS of attempt to ping.
+//
+IP_STATUS
+ICMPEchoRequest(
+ void *InputBuffer,
+ uint InputBufferLength,
+ EchoControl *ControlBlock,
+ EchoRtn Callback
+ )
+{
+ PICMP_ECHO_REQUEST requestBuffer;
+ struct IPOptInfo optionInfo;
+ PUCHAR endOfRequestBuffer;
+ IP_STATUS status;
+
+
+#ifdef NT
+
+ PAGED_CODE();
+
+#endif //NT
+
+ requestBuffer = (PICMP_ECHO_REQUEST) InputBuffer;
+ endOfRequestBuffer = ((PUCHAR) requestBuffer) + InputBufferLength;
+
+ //
+ // Validate the request.
+ //
+ if (InputBufferLength < sizeof(ICMP_ECHO_REQUEST)) {
+ status = IP_BUF_TOO_SMALL;
+ goto common_echo_exit;
+ }
+
+ if (requestBuffer->DataSize > 0) {
+ if ( (requestBuffer->DataOffset < sizeof(ICMP_ECHO_REQUEST))
+ ||
+ ( ( ((PUCHAR)requestBuffer) + requestBuffer->DataOffset +
+ requestBuffer->DataSize
+ )
+ >
+ endOfRequestBuffer
+ )
+ ) {
+ status = IP_GENERAL_FAILURE;
+ goto common_echo_exit;
+ }
+ }
+
+ if (requestBuffer->OptionsSize > 0) {
+ if ( (requestBuffer->OptionsOffset < sizeof(ICMP_ECHO_REQUEST))
+ ||
+ ( ( ((PUCHAR)requestBuffer) + requestBuffer->OptionsOffset +
+ requestBuffer->OptionsSize
+ )
+ >
+ endOfRequestBuffer
+ )
+ ) {
+ status = IP_GENERAL_FAILURE;
+ goto common_echo_exit;
+ }
+ }
+
+ //
+ // Copy the options to a local structure.
+ //
+ if (requestBuffer->OptionsValid) {
+ optionInfo.ioi_optlength = requestBuffer->OptionsSize;
+
+ if (requestBuffer->OptionsSize > 0) {
+ optionInfo.ioi_options = ((uchar *) requestBuffer) +
+ requestBuffer->OptionsOffset;
+ }
+ else {
+ optionInfo.ioi_options = NULL;
+ }
+ optionInfo.ioi_addr = 0;
+ optionInfo.ioi_ttl = requestBuffer->Ttl;
+ optionInfo.ioi_tos = requestBuffer->Tos;
+ optionInfo.ioi_flags = requestBuffer->Flags;
+ }
+ else {
+ optionInfo.ioi_optlength = 0;
+ optionInfo.ioi_options = NULL;
+ optionInfo.ioi_addr = 0;
+ optionInfo.ioi_ttl = DEFAULT_TTL;
+ optionInfo.ioi_tos = 0;
+ optionInfo.ioi_flags = 0;
+ }
+
+ status = ICMPEcho(
+ ControlBlock,
+ requestBuffer->Timeout,
+ ((uchar *)requestBuffer) + requestBuffer->DataOffset,
+ requestBuffer->DataSize,
+ Callback,
+ (IPAddr) requestBuffer->Address,
+ &optionInfo
+ );
+
+common_echo_exit:
+
+ return(status);
+
+} // ICMPEchoRequest
+
+
+//** ICMPEchoComplete - Common completion routine for echo requests
+//
+// This is the routine is called by the OS-specific code to process an
+// ICMP echo response.
+//
+// Entry: OutputBuffer - Pointer to an ICMP_ECHO_REPLY structure.
+// OutputBufferLength - Size in bytes of the OutputBuffer.
+// Status - The status of the reply.
+// Data - The reply data (may be NULL).
+// DataSize - The amount of reply data.
+// OptionInfo - A pointer to the reply options
+//
+// Returns: The number of bytes written to the output buffer
+//
+ulong
+ICMPEchoComplete(
+ EchoControl *ControlBlock,
+ IP_STATUS Status,
+ void *Data,
+ uint DataSize,
+ struct IPOptInfo *OptionInfo
+ )
+{
+ PICMP_ECHO_REPLY replyBuffer;
+ IPRcvBuf *dataBuffer;
+ uchar optionsLength;
+ uchar *tmp;
+ ulong bytesReturned = sizeof(ICMP_ECHO_REPLY);
+
+
+ replyBuffer = (PICMP_ECHO_REPLY) ControlBlock->ec_replybuf;
+ dataBuffer = (IPRcvBuf *) Data;
+
+ if (OptionInfo != NULL) {
+ optionsLength = OptionInfo->ioi_optlength;
+ }
+ else {
+ optionsLength = 0;
+ }
+
+ //
+ // Initialize the reply buffer
+ //
+ replyBuffer->Options.OptionsSize = 0;
+ replyBuffer->Options.OptionsData = (unsigned char FAR *) (replyBuffer + 1);
+ replyBuffer->DataSize = 0;
+ replyBuffer->Data = replyBuffer->Options.OptionsData;
+
+ if ( (Status != IP_SUCCESS) && (DataSize == 0)) {
+ //
+ // Timed out or internal error.
+ //
+ replyBuffer->Reserved = 0; // indicate no replies.
+ replyBuffer->Status = Status;
+ }
+ else {
+ if (Status != IP_SUCCESS) {
+ //
+ // A message other than an echo reply was received.
+ // The IP Address of the system that reported the error is
+ // in the data buffer. There is no other data.
+ //
+ CTEAssert(dataBuffer->ipr_size == sizeof(IPAddr));
+
+ CTEMemCopy(
+ &(replyBuffer->Address),
+ dataBuffer->ipr_buffer,
+ sizeof(IPAddr)
+ );
+
+ DataSize = 0;
+ dataBuffer = NULL;
+ }
+ // else {
+ //
+ // BUGBUG - we currently depend on the fact that the destination
+ // address is still in the request buffer. The reply address
+ // should just be a parameter to this function. In NT, this
+ // just works since the input and output buffers are the same.
+ // In the VXD, the destination address must be put into the
+ // reply buffer by the OS-specific code.
+ // }
+
+ //
+ // Check that the reply buffer is large enough to hold all the data.
+ //
+ if ( ControlBlock->ec_replybuflen <
+ (sizeof(ICMP_ECHO_REPLY) + DataSize + optionsLength)
+ ) {
+ //
+ // Not enough space to hold the reply.
+ //
+ replyBuffer->Reserved = 0; // indicate no replies
+ replyBuffer->Status = IP_BUF_TOO_SMALL;
+ }
+ else {
+ replyBuffer->Reserved = 1; // indicate one reply
+ replyBuffer->Status = Status;
+ replyBuffer->RoundTripTime = CTESystemUpTime() -
+ ControlBlock->ec_starttime;
+
+ //
+ // Copy the reply options.
+ //
+ if (OptionInfo != NULL) {
+ replyBuffer->Options.Ttl = OptionInfo->ioi_ttl;
+ replyBuffer->Options.Tos = OptionInfo->ioi_tos;
+ replyBuffer->Options.Flags = OptionInfo->ioi_flags;
+ replyBuffer->Options.OptionsSize = optionsLength;
+
+ if (optionsLength > 0) {
+
+ CTEMemCopy(
+ replyBuffer->Options.OptionsData,
+ OptionInfo->ioi_options,
+ optionsLength
+ );
+ }
+ }
+
+ //
+ // Copy the reply data
+ //
+ replyBuffer->DataSize = (ushort) DataSize;
+ replyBuffer->Data = replyBuffer->Options.OptionsData +
+ replyBuffer->Options.OptionsSize;
+
+ if (DataSize > 0) {
+ uint bytesToCopy;
+
+ CTEAssert(Data != NULL);
+
+ tmp = replyBuffer->Data;
+
+ while (DataSize) {
+ CTEAssert(dataBuffer != NULL);
+
+ bytesToCopy = (DataSize > dataBuffer->ipr_size) ?
+ dataBuffer->ipr_size : DataSize;
+
+ CTEMemCopy(
+ tmp,
+ dataBuffer->ipr_buffer,
+ bytesToCopy
+ );
+
+ tmp += bytesToCopy;
+ DataSize -= bytesToCopy;
+ dataBuffer = dataBuffer->ipr_next;
+ }
+ }
+
+ bytesReturned += replyBuffer->DataSize + optionsLength;
+
+ //
+ // Convert the kernel pointers to offsets from start of reply buffer.
+ //
+ replyBuffer->Options.OptionsData = (unsigned char FAR *)
+ (((unsigned long) replyBuffer->Options.OptionsData) -
+ ((unsigned long) replyBuffer));
+
+ replyBuffer->Data = (void FAR *)
+ (((unsigned long) replyBuffer->Data) -
+ ((unsigned long) replyBuffer));
+ }
+ }
+
+ return(bytesReturned);
+}
+
+
+#ifdef VXD
+
+struct _pending_echo {
+ EchoControl ControlBlock;
+ CTEBlockStruc BlockStruc;
+};
+
+typedef struct _pending_echo PendingEcho;
+
+
+//** VXDEchoComplete - OS-specific icmp echo completion routine.
+//
+// This routine is called by the OS-indepenent code to process a completed
+// ICMP echo request. It calls common code to package the response.
+//
+// Entry: Context - A pointer to an EchoControl structure.
+// Status - The status of the request
+// Data - A pointer to the response data.
+// DataSize - The amount of response data.
+// OptionInfo - A pointer to the options contained in the reply.
+//
+// Returns: Nothing.
+//
+void
+VXDEchoComplete(
+ void *Context,
+ IP_STATUS Status,
+ void *Data,
+ uint DataSize,
+ struct IPOptInfo *OptionInfo
+ )
+{
+ PendingEcho *pendingEcho;
+ EchoControl *controlBlock;
+ ulong bytesReturned;
+
+
+ controlBlock = (EchoControl *) Context;
+
+ bytesReturned = ICMPEchoComplete(
+ controlBlock,
+ Status,
+ Data,
+ DataSize,
+ OptionInfo
+ );
+
+ //
+ // The request thread will copy the returned byte count from
+ // the control block.
+ //
+ controlBlock->ec_replybuflen = bytesReturned;
+
+ //
+ // Signal waiting request thread.
+ //
+ pendingEcho = STRUCT_OF(PendingEcho, controlBlock, ControlBlock);
+ CTESignal(&(pendingEcho->BlockStruc), Status);
+
+ return;
+}
+
+
+//** VXDEchoRequest - Send an echo to the specified address.
+//
+// This routine dispatches an echo request on the VXD platform to the
+// common code
+//
+// Entry: InBuf - Pointer to input buffer.
+// InBufLen - Pointer to input buffer length.
+// OutBuf - Pointer to output buffer.
+// OutBufLen - Pointer to output buffer length.
+//
+// Returns: DWORD Win32 completion status.
+//
+ULONG
+VXDEchoRequest(
+ void * InBuf,
+ ulong * InBufLen,
+ void * OutBuf,
+ ulong * OutBufLen
+ )
+{
+ IP_STATUS ipStatus;
+ PendingEcho pendingEcho;
+
+ pendingEcho.ControlBlock.ec_starttime = CTESystemUpTime();
+ pendingEcho.ControlBlock.ec_replybuf = OutBuf;
+ pendingEcho.ControlBlock.ec_replybuflen = *OutBufLen;
+ CTEInitBlockStruc(&pendingEcho.BlockStruc);
+
+ ipStatus = ICMPEchoRequest(
+ InBuf,
+ *InBufLen,
+ &(pendingEcho.ControlBlock),
+ VXDEchoComplete
+ );
+
+ if (ipStatus == IP_PENDING) {
+ ipStatus = CTEBlock(&(pendingEcho.BlockStruc));
+
+ if (ipStatus == IP_SUCCESS) {
+ PICMP_ECHO_REQUEST requestBuffer;
+ PICMP_ECHO_REPLY replyBuffer;
+
+ //
+ // BUGBUG:
+ //
+ // It is necessary to copy the original destination address into
+ // the reply buffer because the src address is not provided to
+ // the completion routine for an echo response. This is the only
+ // reason why the signalling status is the reply status instead
+ // of just success.
+ //
+ requestBuffer = (PICMP_ECHO_REQUEST) InBuf;
+ replyBuffer = (PICMP_ECHO_REPLY) OutBuf;
+
+ replyBuffer->Address = requestBuffer->Address;
+ }
+
+ //
+ // The ioctl is considered successful as long as we can submit
+ // the request. The status of the request is contained in the
+ // reply buffer. The completion routine stuffed the return
+ // buffer length back into the control block.
+ //
+ ipStatus = IP_SUCCESS;
+ *OutBufLen = pendingEcho.ControlBlock.ec_replybuflen;
+ }
+ else {
+ //
+ // An internal error of some kind occurred. Since the VXD can
+ // return the IP_STATUS directly, do so.
+ //
+ CTEAssert(ipStatus != IP_SUCCESS);
+
+ *OutBufLen = 0;
+ }
+
+ return(ipStatus);
+}
+
+#endif // VXD
+
+
+#pragma BEGIN_INIT
+//** ICMPInit - Initialize ICMP.
+//
+// This routine initializes ICMP. All we do is allocate and link up some header buffers,
+/// and register our protocol with IP.
+//
+// Entry: NumBuffers - Number of ICMP buffers to allocate.
+//
+// Returns: Nothing
+//
+void
+ICMPInit(uint NumBuffers)
+{
+ ICMPHeader **IHP; // Pointer to current ICMP header.
+
+
+ CTEInitLock(&ICMPHeaderLock);
+ MaxICMPHeaders = NumBuffers;
+ CurrentICMPHeaders = 0;
+ ICMPHeaderList= (ICMPHeader *)NULL;
+
+ while (NumBuffers--) {
+ IHP = (ICMPHeader **) CTEAllocMem(
+ sizeof(ICMPHeader) + sizeof(IPHeader) +
+ sizeof(IPHeader) + MAX_OPT_SIZE + 8
+ );
+
+ if (IHP == (ICMPHeader **)NULL) {
+ break;
+ }
+
+ *IHP = ICMPHeaderList;
+ ICMPHeaderList = (ICMPHeader *)IHP;
+ CurrentICMPHeaders++;
+ }
+
+ IPRegisterProtocol(PROT_ICMP, ICMPRcv, ICMPSendComplete, ICMPStatus, NULL);
+
+}
+
+#pragma END_INIT
diff --git a/private/ntos/tdi/tcpip/ip/icmp.h b/private/ntos/tdi/tcpip/ip/icmp.h
new file mode 100644
index 000000000..f45dac536
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/icmp.h
@@ -0,0 +1,70 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** icmp.h - IP ICMP header.
+//
+// This module contains private ICMP definitions.
+//
+
+#define PROT_ICMP 1
+
+#define ICMP_ECHO_RESP 0
+#define ICMP_ECHO 8
+#define ICMP_TIMESTAMP 13
+#define ICMP_TIMESTAMP_RESP 14
+
+#define MIN_ERRDATA_LENGTH 8 // Minimum amount of data we need.
+
+// Structure of an ICMP header.
+
+struct ICMPHeader {
+ uchar ich_type; // Type of ICMP packet.
+ uchar ich_code; // Subcode of type.
+ ushort ich_xsum; // Checksum of packet.
+ ulong ich_param; // Type-specific parameter field.
+}; /* ICMPHeader */
+
+struct ICMPRouterAdHeader {
+ uchar irah_numaddrs; // Number of addresses
+ uchar irah_addrentrysize; // Address Entry Size
+ ushort irah_lifetime; // Lifetime
+}; /* ICMPRouterAdHeader */
+
+struct ICMPRouterAdAddrEntry {
+ IPAddr irae_addr; // Router Address
+ long irae_preference; // Preference Level
+}; /* ICMPRouterAdAddrEntry */
+
+/*NOINC*/
+typedef struct ICMPHeader ICMPHeader;
+typedef struct ICMPRouterAdHeader ICMPRouterAdHeader;
+typedef struct ICMPRouterAdAddrEntry ICMPRouterAdAddrEntry;
+
+typedef void (*EchoRtn)(void *, IP_STATUS, void *, uint, IPOptInfo *);
+/*INC*/
+
+struct EchoControl {
+ struct EchoControl *ec_next; // Next control structure in list.
+ ulong ec_to; // Timeout
+ void *ec_rtn; // Pointer to routine to call when completing request.
+ ushort ec_seq; // Seq. # of this ping request.
+ uchar ec_active; // Set when packet has been sent
+ uchar ec_pad; // Pad.
+ ulong ec_starttime; // time request was issued
+ void *ec_replybuf; // buffer to store replies
+ ulong ec_replybuflen; // size of reply buffer
+}; /* EchoControl */
+
+/*NOINC*/
+typedef struct EchoControl EchoControl;
+/*INC*/
+
+extern ICMPHeader *GetICMPBuffer(uint Size, PNDIS_BUFFER *Buffer);
+extern void FreeICMPBuffer(PNDIS_BUFFER Buffer);
+extern void ICMPSendComplete(void *DataPtr, PNDIS_BUFFER BufferChain);
+
+
+
diff --git a/private/ntos/tdi/tcpip/ip/igmp.c b/private/ntos/tdi/tcpip/ip/igmp.c
new file mode 100644
index 000000000..340f0380f
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/igmp.c
@@ -0,0 +1,799 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** igmp.c - IP multicast routines.
+//
+// This file contains all the routines related to the IGMP protocol.
+
+#include "oscfg.h"
+#include "cxport.h"
+#include "ndis.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "igmp.h"
+#include "icmp.h"
+#include "ipxmit.h"
+#include "llipif.h"
+#include "iproute.h"
+
+
+#define IGMP_QUERY 0x11 //Membership query
+#define IGMP_REPORT_V1 0x12 //Version 1 membership report
+#define IGMP_REPORT_V2 0x16 //Version 2 membership report
+#define IGMP_LEAVE 0x17 //Leave Group
+
+
+#define IGMPV1 2 //IGMP version 1
+#define IGMPV2 3 //IGMP version 2
+
+//
+// undefine for 4.0 sp2
+//
+#undef IGMPV2
+
+#define ALL_HOST_MCAST 0x010000E0
+
+#define MAX_DELAY_TICKS 20 //used when sending a report after a
+ //mcast group has been added. The
+ //report is sent at a interval of
+ //500 msecs to 9.5 secs
+
+//
+// The following values are used to initialize counters that keep time in
+// 1/2 a sec.
+//
+#define MAX_DELAY_IGMPV1_QUERY_RESP 20 //10 secs
+
+//
+// The amount of time we stay in the "IGMPV1 Router Present" state in the
+// absence of an IGMPV1 query
+//
+#define VERSION1_ROUTER_TIMEOUT 800 //400 secs
+
+int RandomValue;
+int Seed;
+
+// Structure of an IGMP header.
+typedef struct IGMPHeader {
+ uchar igh_vertype; // Type of igmp message
+ uchar igh_rsvd; // max. resp. time for igmpv2 messages; will be 0
+ // for igmpv1 messages
+ ushort igh_xsum;
+ IPAddr igh_addr;
+} IGMPHeader;
+
+
+typedef struct IGMPBlockStruct {
+ struct IGMPBlockStruct *ibs_next;
+ CTEBlockStruc ibs_block;
+} IGMPBlockStruct;
+
+void *IGMPProtInfo;
+
+IGMPBlockStruct *IGMPBlockList;
+uchar IGMPBlockFlag;
+
+DEFINE_LOCK_STRUCTURE(IGMPLock)
+
+extern ProtInfo *RawPI; // Raw IP protinfo
+
+extern IP_STATUS IPCopyOptions(uchar *, uint, IPOptInfo *);
+extern void IPInitOptions(IPOptInfo *);
+extern void *IPRegisterProtocol(uchar Protocol, void *RcvHandler,
+ void *XmitHandler, void *StatusHandler, void *RcvCmpltHandler);
+
+
+#ifdef NT
+
+//
+// All of the init code can be discarded
+//
+#ifdef ALLOC_PRAGMA
+
+uint IGMPInit(void);
+
+#pragma alloc_text(INIT, IGMPInit)
+
+#endif // ALLOC_PRAGMA
+
+#endif // NT
+
+//* IGMPRandomTicks - Generate a random value of timer ticks.
+//
+// A random number routine to generate a random number of timer ticks,
+// between 1 and time (in units of half secs) passed. The random number
+// algorithm is adapted from the book 'System Simulation' by Geoffrey Gordon.
+//
+// Input: Nothing.
+//
+// Returns: A random value between 1 and TimeDelayInHalfSec.
+//
+uint
+IGMPRandomTicks(uint TimeDelayInHalfSec)
+{
+
+ RandomValue = RandomValue * 1220703125;
+
+ if (RandomValue < 0) {
+ RandomValue += 2147483647; // inefficient, but avoids warnings.
+ RandomValue++;
+ }
+
+ // Not sure if RandomValue can get to 0, but if it does the algorithm
+ // degenerates, so fix this if it happens.
+ if (RandomValue == 0)
+ RandomValue = ((Seed + (int)CTESystemUpTime()) % 100000000) | 1;
+
+ return (uint)(((uint)RandomValue % TimeDelayInHalfSec) + 1);
+}
+
+
+//* FindIGMPAddr - Find an mcast entry on an NTE.
+//
+// Called to search an NTE for an IGMP entry for a given class D address.
+// We walk down the chain on the NTE looking for it. If we find it,
+// we return a pointer to it and the one immediately preceding it. If we
+// don't find it we return NULL. We assume the caller has taken the lock
+// on the NTE before calling us.
+//
+// Input: NTE - NTE on which to search.
+// Addr - Class D address to find.
+// PrevPtr - Where to return pointer to preceding entry.
+//
+// Returns: Pointer to matching IGMPAddr structure if found, or NULL if not
+// found.
+//
+IGMPAddr *
+FindIGMPAddr(NetTableEntry *NTE, IPAddr Addr, IGMPAddr **PrevPtr)
+{
+ IGMPAddr *Current, *Temp;
+
+ Temp = STRUCT_OF(IGMPAddr, &NTE->nte_igmplist, iga_next);
+ Current = NTE->nte_igmplist;
+
+ while (Current != NULL) {
+ if (IP_ADDR_EQUAL(Current->iga_addr, Addr)) {
+ // Found a match, so return it.
+ *PrevPtr = Temp;
+ break;
+ }
+ Temp = Current;
+ Current = Current->iga_next;
+ }
+
+ return Current;
+
+}
+
+//** IGMPRcv - Receive an IGMP datagram.
+//
+// Called by IP when we receive an IGMP datagram. We validate it to make sure
+// it's reasonable. Then if it it's a query for a group to which we belong
+// we'll start a response timer. If it's a report to a group to which we belong
+// we'll stop any running timer.
+//
+// The IGMP header is only 8 bytes long, and so should always fit in exactly
+// one IP rcv buffer. We check this to make sure, and if it takes multiple
+// buffers we discard it.
+//
+// Entry: NTE - Pointer to NTE on which IGMP message was received.
+// Dest - IPAddr of destination (should be a Class D address).
+// Src - IPAddr of source
+// LocalAddr - Local address of network which caused this to be
+// received.
+// SrcAddr - Address of local interface which received the
+// packet
+// IPHdr - Pointer to the IP Header.
+// IPHdrLength - Bytes in IPHeader.
+// RcvBuf - Pointer to IP receive buffer chain.
+// Size - Size in bytes of IGMP message.
+// IsBCast - Boolean indicator of whether or not this came in
+// as a bcast (should always be true).
+// Protocol - Protocol this came in on.
+// OptInfo - Pointer to info structure for received options.
+//
+// Returns: Status of reception
+IP_STATUS
+IGMPRcv(NetTableEntry *NTE, IPAddr Dest, IPAddr Src, IPAddr LocalAddr,
+ IPAddr SrcAddr, IPHeader UNALIGNED *IPHdr, uint IPHdrLength,
+ IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, uchar Protocol,
+ IPOptInfo *OptInfo)
+{
+ IGMPHeader UNALIGNED *IGH;
+ CTELockHandle Handle;
+ IGMPAddr *AddrPtr, *PrevPtr;
+ uchar DType;
+ uint ReportingDelayInHalfSec;
+
+
+ CTEAssert(CLASSD_ADDR(Dest));
+ CTEAssert(IsBCast);
+
+ // Make sure we're running at least level 2 of IGMP support.
+ if (IGMPLevel != 2)
+ return IP_SUCCESS;
+
+ // Discard packets with invalid or broadcast source addresses.
+ DType = GetAddrType(Src);
+ if (DType == DEST_INVALID || IS_BCAST_DEST(DType))
+ return IP_SUCCESS;
+
+ // Check the size to make sure it's valid.
+ if (Size != sizeof(IGMPHeader) || RcvBuf->ipr_size != sizeof(IGMPHeader))
+ return IP_SUCCESS;
+
+ // Now get the pointer to the header, and validate the xsum.
+ IGH = (IGMPHeader UNALIGNED *)RcvBuf->ipr_buffer;
+
+ if (xsum(IGH, sizeof(IGMPHeader)) != 0xffff) {
+ // Bad checksum, so fail.
+ return IP_SUCCESS;
+ }
+
+ // If we sent it, don't process this message.
+ if (IP_ADDR_EQUAL(Src, LocalAddr))
+ return IP_SUCCESS;
+
+ // OK, we may need to process this. See if we are a member of the
+ // destination group. If we aren't, there's no need to proceed further.
+ CTEGetLock(&NTE->nte_lock, &Handle);
+
+ if (NTE->nte_flags & NTE_VALID) {
+ //
+ // The NTE is valid. Demux on type.
+ //
+ switch (IGH->igh_vertype) {
+
+ case IGMP_QUERY:
+
+ //
+ // If it is an IGMPV1 query, set the timer value for staying in
+ // igmpv1 mode
+ //
+#ifdef IGMPV2
+ if (IGH->igh_rsvd == 0) {
+ //
+ // Since for any interface we always get notified with
+ // same NTE, locking the NTE is fine. We don't have to
+ // lock the interface structure
+ //
+ if (NTE->nte_if->IgmpVersion == IGMPV2) {
+ NTE->nte_if->IgmpVersion = IGMPV1;
+ }
+ NTE->nte_if->IgmpVer1Timeout = VERSION1_ROUTER_TIMEOUT;
+ ReportingDelayInHalfSec = MAX_DELAY_IGMPV1_QUERY_RESP;
+ }
+ else {
+ ReportingDelayInHalfSec = IGH->igh_rsvd * 5; //field's unit are in 100ms
+ }
+#else
+ ReportingDelayInHalfSec = MAX_DELAY_IGMPV1_QUERY_RESP;
+#endif
+
+ //
+ // This is a query. Walk our list and set a random report timer for
+ // all those class D addresses that don't already have one running
+ // (except for the all host's address).
+ //
+ for (AddrPtr = NTE->nte_igmplist; AddrPtr != NULL; AddrPtr = AddrPtr->iga_next) {
+ if (!IP_ADDR_EQUAL(AddrPtr->iga_addr, ALL_HOST_MCAST)) {
+ if (AddrPtr->iga_timer == 0) {
+ AddrPtr->iga_timer = IGMPRandomTicks(ReportingDelayInHalfSec);
+ }
+ }
+ }
+
+ break;
+
+ case IGMP_REPORT_V1:
+ case IGMP_REPORT_V2:
+ //
+ // This is a report. Check it's validity and see if we have a
+ // report timer running for that address. If we do, stop it.
+ // Make sure the destination address matches the address in the
+ // IGMP header.
+ //
+ if (IP_ADDR_EQUAL(Dest, IGH->igh_addr)) {
+ // The addresses match. See if we have a membership in this
+ // group.
+ AddrPtr = FindIGMPAddr(NTE, IGH->igh_addr, &PrevPtr);
+ if (AddrPtr != NULL) {
+ // We found a matching class D address. Stop the timer.
+ AddrPtr->iga_timer = 0;
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ //
+ // Pass the packet up to the raw layer if applicable.
+ //
+ if (RawPI != NULL) {
+ if (RawPI->pi_rcv != NULL) {
+ (*(RawPI->pi_rcv))(NTE, Dest, Src, LocalAddr, SrcAddr, IPHdr,
+ IPHdrLength, RcvBuf, Size, IsBCast, Protocol, OptInfo);
+ }
+ }
+
+ return IP_SUCCESS;
+ }
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ return IP_SUCCESS;
+}
+
+//* SendIGMPReport - Send an IGMP report.
+//
+// Called when we want to send an IGMP report for some reason. For this
+// purpose we steal ICMP buffers. What we'll do is get one, fill it in,
+// and send it.
+//
+// Input: Dest - Destination to send to.
+// Src - Source to send from.
+//
+// Returns: Nothing.
+//
+void
+SendIGMPReport(uint ChangeType, uint IgmpVersion, IPAddr Dest, IPAddr Src)
+{
+ IGMPHeader *IGH;
+ PNDIS_BUFFER Buffer;
+ IPOptInfo OptInfo; // Options for this transmit.
+ IP_STATUS Status;
+ int ReportType;
+
+ CTEAssert(CLASSD_ADDR(Dest));
+ CTEAssert(!IP_ADDR_EQUAL(Src, NULL_IP_ADDR));
+
+ // Make sure we never send a report for the all-hosts mcast address.
+ if (IP_ADDR_EQUAL(Dest, ALL_HOST_MCAST)) {
+ DEBUGCHK;
+ return;
+ }
+ //
+ // If the report to be sent is a "Leave Group" report but we have
+ // detected an igmp v1 router on this net, do not send the report
+ //
+#ifdef IGMPV2
+ if (IgmpVersion == IGMPV1) {
+ if (ChangeType == IGMP_DELETE) {
+ return;
+ } else {
+#endif
+ ReportType = IGMP_REPORT_V1;
+#ifdef IGMPV2
+ }
+ } else {
+ if (ChangeType == IGMP_DELETE) {
+ ReportType = IGMP_LEAVE;
+ } else {
+ ReportType = IGMP_REPORT_V2;
+ }
+ }
+#endif
+
+ IGH = (IGMPHeader *)GetICMPBuffer(sizeof(IGMPHeader), &Buffer);
+ if (IGH != NULL) {
+ // We got the buffer. Fill it in and send it.
+ IGH->igh_vertype = ReportType;
+ IGH->igh_rsvd = 0;
+ IGH->igh_xsum = 0;
+ IGH->igh_addr = Dest;
+ IGH->igh_xsum = ~xsum(IGH, sizeof(IGMPHeader));
+
+ IPInitOptions(&OptInfo);
+ OptInfo.ioi_ttl = 1;
+
+ Status = IPTransmit(IGMPProtInfo, NULL, Buffer, sizeof(IGMPHeader),
+ Dest, Src, &OptInfo, NULL, PROT_IGMP);
+
+ if (Status != IP_PENDING)
+ ICMPSendComplete(NULL, Buffer);
+ }
+
+}
+
+//* IGMPAddrChange - Change the IGMP address list on an NTE.
+//
+// Called to add or delete an IGMP address. We're given the relevant NTE,
+// the address, and the action to be performed. We validate the NTE, the
+// address, and the IGMP level, and then attempt to perform the action.
+//
+// There are a bunch of strange race conditions that can occur during adding/
+// deleting addresses, related to trying to add the same address twice and
+// having it fail, or adding and deleting the same address simultaneously. Most
+// of these happen because we have to free the lock to call the interface,
+// and the call to the interface can fail. To prevent this we serialize all
+// access to this routine. Only one thread of execution can go through here
+// at a time, all others are blocked.
+//
+// Input: NTE - NTE with list to be altered.
+// Addr - Address affected.
+// ChangeType - Type of change - IGMP_ADD, IGMP_DELETE,
+// IGMP_DELETE_ALL.
+//
+// Returns: IP_STATUS of attempt to perform action.
+//
+IP_STATUS
+IGMPAddrChange(NetTableEntry *NTE, IPAddr Addr, uint ChangeType)
+{
+ CTELockHandle Handle;
+ IGMPAddr *AddrPtr, *PrevPtr;
+ IP_STATUS Status;
+ Interface *IF;
+ uint AddrAdded;
+ IGMPBlockStruct Block;
+ IGMPBlockStruct *BlockPtr;
+ uint IgmpVersion;
+
+ // First make sure we're at level 2 of IGMP support.
+
+ if (IGMPLevel != 2)
+ return IP_BAD_REQ;
+
+ CTEInitBlockStruc(&Block.ibs_block);
+
+ // Make sure we're the only ones in this routine. If someone else is
+ // already here, block.
+
+ CTEGetLock(&IGMPLock, &Handle);
+ if (IGMPBlockFlag) {
+
+ // Someone else is already here. Walk down the block list, and
+ // put ourselves on the end. Then free the lock and block on our
+ // IGMPBlock structure.
+ BlockPtr = STRUCT_OF(IGMPBlockStruct, &IGMPBlockList, ibs_next);
+ while (BlockPtr->ibs_next != NULL)
+ BlockPtr = BlockPtr->ibs_next;
+
+ Block.ibs_next = NULL;
+ BlockPtr->ibs_next = &Block;
+ CTEFreeLock(&IGMPLock, Handle);
+ CTEBlock(&Block.ibs_block);
+ } else {
+ // Noone else here, set the flag so noone else gets in and free the
+ // lock.
+ IGMPBlockFlag = 1;
+ CTEFreeLock(&IGMPLock, Handle);
+ }
+
+ // Now we're in the routine, and we won't be reentered here by another
+ // thread of execution. Make sure everything's valid, and figure out
+ // what to do.
+
+ Status = IP_SUCCESS;
+
+ // Now get the lock on the NTE and make sure it's valid.
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ if ((NTE->nte_flags & NTE_VALID) || ChangeType == IGMP_DELETE_ALL) {
+ // The NTE is valid. Try to find an existing IGMPAddr structure
+ // that matches the input address.
+ AddrPtr = FindIGMPAddr(NTE, Addr, &PrevPtr);
+ IF = NTE->nte_if;
+
+#ifdef IGMPV2
+ IgmpVersion = IF->IgmpVersion;
+#else
+ IgmpVersion = IGMPV1;
+#endif
+ // Now figure out the action to be performed.
+ switch (ChangeType) {
+
+ case IGMP_ADD:
+
+ // We're to add this. If AddrPtr is NULL, we'll need to
+ // allocate memory and link the new IGMP address in. Otherwise
+ // we can just increment the reference count on the existing
+ // address structure.
+ if (AddrPtr == NULL) {
+ // AddrPtr is NULL, i.e. the address doesn't currently
+ // exist. Allocate memory for it, then try to add the
+ // address locally.
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ // If this is not a class D address, fail the request.
+ if (!CLASSD_ADDR(Addr)) {
+ Status = IP_BAD_REQ;
+ break;
+ }
+
+ AddrPtr = CTEAllocMem(sizeof(IGMPAddr));
+ if (AddrPtr != NULL) {
+
+ // Got memory. Try to add the address locally.
+ AddrAdded = (*IF->if_addaddr)(IF->if_lcontext,
+ LLIP_ADDR_MCAST, Addr, 0, NULL);
+
+ // See if we added it succesfully. If we did, fill in
+ // the stucture and link it in.
+
+ if (AddrAdded) {
+
+ AddrPtr->iga_addr = Addr;
+ AddrPtr->iga_refcnt = 1;
+ AddrPtr->iga_timer = 0;
+
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ AddrPtr->iga_next = NTE->nte_igmplist;
+ NTE->nte_igmplist = AddrPtr;
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ if (!IP_ADDR_EQUAL(Addr, ALL_HOST_MCAST)) {
+ // This isn't the all host address, so send a
+ // report for it.
+ AddrPtr->iga_timer = IGMPRandomTicks(MAX_DELAY_TICKS);
+ SendIGMPReport(ChangeType, IgmpVersion, Addr,
+ NTE->nte_addr);
+ }
+ } else {
+ // Couldn't add the local address. Free the memory
+ // and fail the request.
+ CTEFreeMem(AddrPtr);
+ Status = IP_NO_RESOURCES;
+ }
+
+ } else {
+ Status = IP_NO_RESOURCES;
+ }
+ } else {
+ // Already have this one. Bump his count.
+ (AddrPtr->iga_refcnt)++;
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ }
+ break;
+ case IGMP_DELETE:
+
+ // This is a delete request. If we didn't find the requested
+ // address, fail the request. Otherwise dec his refcnt, and if
+ // it goes to 0 delete the address locally.
+ if (AddrPtr != NULL) {
+ // Have one. We won't let the all-hosts mcast address go
+ // away, but for other's we'll check to see if it's time
+ // to delete them.
+ if (!IP_ADDR_EQUAL(Addr, ALL_HOST_MCAST) &&
+ --(AddrPtr->iga_refcnt) == 0) {
+ // This one is to be deleted. Pull him from the
+ // list, and call the lower interface to delete him.
+ PrevPtr->iga_next = AddrPtr->iga_next;
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ CTEFreeMem(AddrPtr);
+ (*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_MCAST,
+ Addr, 0);
+ //
+ // Send a report to indicate that we are leaving the
+ // group
+ //
+
+#ifdef IGMPV2
+ SendIGMPReport(ChangeType, IgmpVersion, Addr,
+ NTE->nte_addr);
+#endif
+ } else
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ } else {
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ Status = IP_BAD_REQ;
+ }
+ break;
+ case IGMP_DELETE_ALL:
+ // We've been called to delete all of this addresses,
+ // regardless of their reference count. This should only
+ // happen when the NTE is going away.
+ AddrPtr = NTE->nte_igmplist;
+ NTE->nte_igmplist = NULL;
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ // Walk down the list, deleteing each one.
+ while (AddrPtr != NULL) {
+ (*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_MCAST,
+ AddrPtr->iga_addr, 0);
+#ifdef IGMPV2
+ if (!IP_ADDR_EQUAL(AddrPtr->iga_addr, ALL_HOST_MCAST)) {
+ SendIGMPReport(IGMP_DELETE, IgmpVersion, AddrPtr->iga_addr, NTE->nte_addr);
+ }
+#endif
+ PrevPtr = AddrPtr;
+ AddrPtr = AddrPtr->iga_next;
+ CTEFreeMem(PrevPtr);
+ }
+
+ // All done.
+ break;
+ default:
+ DEBUGCHK;
+ break;
+ }
+ } else {
+ // NTE isn't valid.
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ Status = IP_BAD_REQ;
+ }
+
+
+ // We finished the request, and Status contains the completion status.
+ // If there are any pending blocks for this routine, signal the next
+ // one now. Otherwise clear the block flag.
+ CTEGetLock(&IGMPLock, &Handle);
+ if ((BlockPtr = IGMPBlockList) != NULL) {
+ // Someone is blocking. Pull him from the list and signal him.
+ IGMPBlockList = BlockPtr->ibs_next;
+ CTEFreeLock(&IGMPLock, Handle);
+
+ CTESignal(&BlockPtr->ibs_block, IP_SUCCESS);
+ } else {
+ // No one blocking, just clear the flag.
+ IGMPBlockFlag = 0;
+ CTEFreeLock(&IGMPLock, Handle);
+ }
+
+ return Status;
+
+}
+
+//* IGMPTimer - Handle an IGMP timer event.
+//
+// This function is called every 500 ms. by IP. If we're at level 2 of
+// IGMP functionality we run down the NTE looking for running timers. If
+// we find one, we see if it has expired and if so we send an
+// IGMP report.
+//
+// Input: NTE - Pointer to NTE to check.
+//
+// Returns: Nothing.
+//
+void
+IGMPTimer(NetTableEntry *NTE)
+{
+ CTELockHandle Handle;
+ IGMPAddr *AddrPtr, *PrevPtr;
+ uint IgmpVersion;
+
+ if (IGMPLevel == 2) {
+ // We are doing IGMP. Run down the addresses active on this NTE.
+ CTEGetLock(&NTE->nte_lock, &Handle);
+
+ //
+ // if we haven't heard any query or report from an igmpv1 router or
+ // host during timeout period, revert to igmpv2. No need to check
+ // whether NTE is valid or not
+ //
+#ifdef IGMPV2
+ if ((NTE->nte_if->IgmpVer1Timeout != 0) && (--(NTE->nte_if->IgmpVer1Timeout) == 0)) {
+ NTE->nte_if->IgmpVersion = IGMPV2;
+ }
+#endif
+ PrevPtr = STRUCT_OF(IGMPAddr, &NTE->nte_igmplist, iga_next);
+ AddrPtr = PrevPtr->iga_next;
+ while (AddrPtr != NULL) {
+ // We have one. See if it's running.
+ if (AddrPtr->iga_timer != 0) {
+ // It's running. See if it's expired.
+ if (--(AddrPtr->iga_timer) == 0 && NTE->nte_flags & NTE_VALID) {
+ // It's expired. Increment the ref count so it
+ // doesn't go away while we're here, and send a report.
+ AddrPtr->iga_refcnt++;
+#ifdef IGMPV2
+ IgmpVersion = NTE->nte_if->IgmpVersion;
+#else
+ IgmpVersion = IGMPV1;
+#endif
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ SendIGMPReport(IGMP_ADD, IgmpVersion, AddrPtr->iga_addr,
+ NTE->nte_addr);
+ // Now get the lock, and decrement the refcnt. If it goes
+ // to 0, it's been deleted so we need to free it.
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ if (--(AddrPtr->iga_refcnt) == 0) {
+ // It's been deleted.
+ PrevPtr->iga_next = AddrPtr->iga_next;
+ CTEFreeMem(AddrPtr);
+ AddrPtr = PrevPtr->iga_next;
+ continue;
+ }
+ }
+ }
+ // Either the timer isn't running or hasn't fired. Try the next
+ // one.
+ PrevPtr = AddrPtr;
+ AddrPtr = AddrPtr->iga_next;
+ }
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ }
+
+}
+
+//* InitIGMPForNTE - Called to do per-NTE initialization.
+//
+// Called when an NTE becomes valid. If we're at level 2, we put the all-host
+// mcast on the list and add the address to the interface.
+//
+// Input: NTE - NTE on which to act.
+//
+// Returns: Nothing.
+//
+void
+InitIGMPForNTE(NetTableEntry *NTE)
+{
+ if (IGMPLevel == 2) {
+ IGMPAddrChange(NTE, ALL_HOST_MCAST, IGMP_ADD);
+ if (NTE->nte_rtrdiscovery && (NTE->nte_rtrdiscaddr == ALL_ROUTER_MCAST)) {
+ IGMPAddrChange(NTE, ALL_ROUTER_MCAST, IGMP_ADD);
+ }
+ }
+ if (Seed == 0) {
+ // No random seed yet.
+ Seed = (int)NTE->nte_addr;
+
+ // Make sure the inital value is odd, and less than 9 decimal digits.
+ RandomValue = ((Seed + (int)CTESystemUpTime()) % 100000000) | 1;
+ }
+}
+
+//* StopIGMPForNTE - Called to do per-NTE shutdown.
+//
+// Called when we're shutting down and NTE, and want to stop IGMP on hi,
+//
+// Input: NTE - NTE on which to act.
+//
+// Returns: Nothing.
+//
+void
+StopIGMPForNTE(NetTableEntry *NTE)
+{
+ if (IGMPLevel == 2) {
+ IGMPAddrChange(NTE, NULL_IP_ADDR, IGMP_DELETE_ALL);
+ }
+}
+
+#pragma BEGIN_INIT
+
+//** IGMPInit - Initialize IGMP.
+//
+// This bit of code initializes IGMP generally. There is also some amount
+// of work done on a per-NTE basis that we do when each one is initialized.
+//
+// Input: Nothing.
+///
+// Returns: TRUE if we init, FALSE if we don't.
+//
+uint
+IGMPInit(void)
+{
+
+ if (IGMPLevel != 2)
+ return TRUE;
+
+ CTEInitLock(&IGMPLock);
+ IGMPBlockList = NULL;
+ IGMPBlockFlag = 0;
+ Seed = 0;
+
+ // We fake things a little bit. We register our receive handler, but
+ // since we steal buffers from ICMP we register the ICMP send complete
+ // handler.
+ IGMPProtInfo = IPRegisterProtocol(PROT_IGMP, IGMPRcv, ICMPSendComplete,
+ NULL, NULL);
+
+ if (IGMPProtInfo != NULL)
+ return TRUE;
+ else
+ return FALSE;
+
+}
+
+#pragma END_INIT
diff --git a/private/ntos/tdi/tcpip/ip/igmp.h b/private/ntos/tdi/tcpip/ip/igmp.h
new file mode 100644
index 000000000..ea7aefce5
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/igmp.h
@@ -0,0 +1,42 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** IGMP.H - IP multicast definitions.
+//
+// This file contains definitions related to IP multicast.
+
+#define PROT_IGMP 2
+
+extern uint IGMPLevel;
+
+// Structure used for local mcast address tracking.
+typedef struct IGMPAddr {
+ struct IGMPAddr *iga_next;
+ IPAddr iga_addr;
+ uint iga_refcnt;
+ uint iga_timer;
+} IGMPAddr;
+
+#define IGMP_ADD 0
+#define IGMP_DELETE 1
+#define IGMP_DELETE_ALL 2
+
+#define IGMPV1 2 //IGMP version 1
+#define IGMPV2 3 //IGMP version 2
+
+//
+// disable for 4.0 sp2
+//
+#undef IGMPV2
+
+
+extern void InitIGMPForNTE(NetTableEntry *NTE);
+extern void StopIGMPForNTE(NetTableEntry *NTE);
+extern IP_STATUS IGMPAddrChange(NetTableEntry *NTE, IPAddr Addr,
+ uint ChangeType);
+extern void IGMPTimer(NetTableEntry *NTE);
+
+
diff --git a/private/ntos/tdi/tcpip/ip/info.c b/private/ntos/tdi/tcpip/ip/info.c
new file mode 100644
index 000000000..a866c0cba
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/info.c
@@ -0,0 +1,609 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** INFO.C - Routines for querying and setting IP information.
+//
+// This file contains the code for dealing with Query/Set information
+// calls.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "info.h"
+#include "tdi.h"
+#include "tdiinfo.h"
+#include "llinfo.h"
+#include "tdistat.h"
+#include "iproute.h"
+#include "igmp.h"
+#include "ipfilter.h"
+#include "iprtdef.h"
+
+extern Interface *IFList;
+extern NetTableEntry *NetTableList;
+extern uint LoopIndex; // Index of loopback I/F.
+extern uint DefaultTTL;
+extern uint NumIF;
+extern uint NumNTE;
+extern RouteInterface DummyInterface; // Dummy interface.
+
+EXTERNAL_LOCK(RouteTableLock)
+
+extern uint RTEReadNext(void *Context, void *Buffer);
+extern uint RTValidateContext(void *Context, uint *Valid);
+extern uint RTReadNext(void *Context, void *Buffer);
+
+uint IPInstance;
+uint ICMPInstance;
+
+//* CopyToNdis - Copy a flat buffer to an NDIS_BUFFER chain.
+//
+// A utility function to copy a flat buffer to an NDIS buffer chain. We
+// assume that the NDIS_BUFFER chain is big enough to hold the copy amount;
+// in a debug build we'll debugcheck if this isn't true. We return a pointer
+// to the buffer where we stopped copying, and an offset into that buffer.
+// This is useful for copying in pieces into the chain.
+//
+// Input: DestBuf - Destination NDIS_BUFFER chain.
+// SrcBuf - Src flat buffer.
+// Size - Size in bytes to copy.
+// StartOffset - Pointer to start of offset into first buffer in
+// chain. Filled in on return with the offset to
+// copy into next.
+//
+// Returns: Pointer to next buffer in chain to copy into.
+//
+PNDIS_BUFFER
+CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size,
+ uint *StartOffset)
+{
+ uint CopySize;
+ uchar *DestPtr;
+ uint DestSize;
+ uint Offset = *StartOffset;
+ uchar *VirtualAddress;
+ uint Length;
+
+ CTEAssert(DestBuf != NULL);
+ CTEAssert(SrcBuf != NULL);
+
+ NdisQueryBuffer(DestBuf, &VirtualAddress, &Length);
+ CTEAssert(Length >= Offset);
+ DestPtr = VirtualAddress + Offset;
+ DestSize = Length - Offset;
+
+ for (;;) {
+ CopySize = MIN(Size, DestSize);
+ CTEMemCopy(DestPtr, SrcBuf, CopySize);
+
+ DestPtr += CopySize;
+ SrcBuf += CopySize;
+
+ if ((Size -= CopySize) == 0)
+ break;
+
+ if ((DestSize -= CopySize) == 0) {
+ DestBuf = NDIS_BUFFER_LINKAGE(DestBuf);
+ CTEAssert(DestBuf != NULL);
+ NdisQueryBuffer(DestBuf, &VirtualAddress, &Length);
+ DestPtr = VirtualAddress;
+ DestSize = Length;
+ }
+ }
+
+ *StartOffset = DestPtr - VirtualAddress;
+
+ return DestBuf;
+
+}
+
+
+//* IPQueryInfo - IP query information handler.
+//
+// Called by the upper layer when it wants to query information about us.
+// We take in an ID, a buffer and length, and a context value, and return
+// whatever information we can.
+//
+// Input: ID - Pointer to ID structure.
+// Buffer - Pointer to buffer chain.
+// Size - Pointer to size in bytes of buffer. On return, filled
+// in with bytes read.
+// Context - Pointer to context value.
+//
+// Returns: TDI_STATUS of attempt to read information.
+//
+long
+IPQueryInfo(TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, void *Context)
+{
+ uint BufferSize = *Size;
+ uint BytesCopied = 0;
+ uint Offset = 0;
+ TDI_STATUS Status;
+ ushort NTEContext;
+ uchar InfoBuff[sizeof(IPRouteEntry)];
+ IPAddrEntry *AddrEntry;
+ NetTableEntry *CurrentNTE;
+ uint Valid, DataLeft;
+ CTELockHandle Handle;
+ Interface *LowerIF;
+ IPInterfaceInfo *IIIPtr;
+ uint LLID = 0;
+ uint Entity;
+ uint Instance;
+ IPAddr IFAddr;
+
+
+ Entity = ID->toi_entity.tei_entity;
+ Instance = ID->toi_entity.tei_instance;
+
+ // See if it's something we might handle.
+
+ if (Entity != CL_NL_ENTITY && Entity != ER_ENTITY) {
+ // We need to pass this down to the lower layer. Loop through until
+ // we find one that takes it. If noone does, error out.
+ for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) {
+ Status = (*LowerIF->if_qinfo)(LowerIF->if_lcontext, ID, Buffer,
+ Size, Context);
+ if (Status != TDI_INVALID_REQUEST)
+ return Status;
+ }
+ // If we get here, noone took it. Return an error.
+ return TDI_INVALID_REQUEST;
+
+ }
+
+ if ((Entity == CL_NL_ENTITY && Instance != IPInstance) ||
+ Instance != ICMPInstance)
+ return TDI_INVALID_REQUEST;
+
+ // The request is for us.
+ *Size = 0; // Set to 0 in case of an error.
+
+ // Make sure it's something we support.
+ if (ID->toi_class == INFO_CLASS_GENERIC) {
+ if (ID->toi_type == INFO_TYPE_PROVIDER && ID->toi_id == ENTITY_TYPE_ID) {
+ // He's trying to see what type we are.
+ if (BufferSize >= sizeof(uint)) {
+ *(uint *)&InfoBuff[0] = (Entity == CL_NL_ENTITY) ? CL_NL_IP :
+ ER_ICMP;
+ (void)CopyToNdis(Buffer, InfoBuff, sizeof(uint), &Offset);
+ return TDI_SUCCESS;
+ } else
+ return TDI_BUFFER_TOO_SMALL;
+ }
+ return TDI_INVALID_PARAMETER;
+ } else
+ if (ID->toi_class != INFO_CLASS_PROTOCOL ||
+ ID->toi_type != INFO_TYPE_PROVIDER)
+ return TDI_INVALID_PARAMETER;
+
+ // If it's ICMP, just copy the statistics.
+ if (Entity == ER_ENTITY) {
+
+ // It is ICMP. Make sure the ID is valid.
+ if (ID->toi_id != ICMP_MIB_STATS_ID)
+ return TDI_INVALID_PARAMETER;
+
+ // He wants the stats. Copy what we can.
+ if (BufferSize < sizeof(ICMPSNMPInfo))
+ return TDI_BUFFER_TOO_SMALL;
+
+ Buffer = CopyToNdis(Buffer, (uchar *)&ICMPInStats, sizeof(ICMPStats),
+ &Offset);
+ (void)CopyToNdis(Buffer, (uchar *)&ICMPOutStats, sizeof(ICMPStats),
+ &Offset);
+
+ *Size = sizeof(ICMPSNMPInfo);
+ return TDI_SUCCESS;
+ }
+
+ // It's not ICMP. We need to figure out what it is, and take the
+ // appropriate action.
+
+ switch (ID->toi_id) {
+
+ case IP_MIB_STATS_ID:
+ if (BufferSize < sizeof(IPSNMPInfo))
+ return TDI_BUFFER_TOO_SMALL;
+ IPSInfo.ipsi_numif = NumIF;
+ IPSInfo.ipsi_numaddr = NumNTE;
+ IPSInfo.ipsi_defaultttl = DefaultTTL;
+ IPSInfo.ipsi_forwarding = ForwardPackets ? IP_FORWARDING :
+ IP_NOT_FORWARDING;
+ CopyToNdis(Buffer, (uchar *)&IPSInfo, sizeof(IPSNMPInfo), &Offset);
+ BytesCopied = sizeof(IPSNMPInfo);
+ Status = TDI_SUCCESS;
+ break;
+ case IP_MIB_ADDRTABLE_ENTRY_ID:
+ // He wants to read the address table. Figure out where we're
+ // starting from, and if it's valid begin copying from there.
+ NTEContext = *(ushort *)Context;
+ CurrentNTE = NetTableList;
+
+ if (NTEContext != 0) {
+ for (;CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next)
+ if (CurrentNTE->nte_context == NTEContext)
+ break;
+ if (CurrentNTE == NULL)
+ return TDI_INVALID_PARAMETER;
+ }
+
+ AddrEntry = (IPAddrEntry *)InfoBuff;
+ for (; CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) {
+ if ((int)(BufferSize - BytesCopied) >= (int)sizeof(IPAddrEntry)) {
+ // We have room to copy it. Build the entry, and copy
+ // it.
+ if (CurrentNTE->nte_flags & NTE_ACTIVE) {
+ if (CurrentNTE->nte_flags & NTE_VALID) {
+ AddrEntry->iae_addr = CurrentNTE->nte_addr;
+ AddrEntry->iae_mask = CurrentNTE->nte_mask;
+ } else {
+ AddrEntry->iae_addr = NULL_IP_ADDR;
+ AddrEntry->iae_mask = NULL_IP_ADDR;
+ }
+
+ AddrEntry->iae_index = CurrentNTE->nte_if->if_index;
+ AddrEntry->iae_bcastaddr =
+ *(int *)&(CurrentNTE->nte_if->if_bcast) & 1;
+ AddrEntry->iae_reasmsize = 0xffff;
+ AddrEntry->iae_context = CurrentNTE->nte_context;
+ Buffer = CopyToNdis(Buffer, (uchar *)AddrEntry,
+ sizeof(IPAddrEntry), &Offset);
+ BytesCopied += sizeof(IPAddrEntry);
+ }
+ } else
+ break;
+ }
+
+ if (CurrentNTE == NULL)
+ Status = TDI_SUCCESS;
+ else {
+ Status = TDI_BUFFER_OVERFLOW;
+ **(ushort **)&Context = CurrentNTE->nte_context;
+ }
+
+ break;
+ case IP_MIB_RTTABLE_ENTRY_ID:
+ // Make sure we have a valid context.
+ CTEGetLock(&RouteTableLock, &Handle);
+ DataLeft = RTValidateContext(Context, &Valid);
+
+ // If the context is valid, we'll continue trying to read.
+ if (!Valid) {
+ CTEFreeLock(&RouteTableLock, Handle);
+ return TDI_INVALID_PARAMETER;
+ }
+
+ while (DataLeft) {
+ // The invariant here is that there is data in the table to
+ // read. We may or may not have room for it. So DataLeft
+ // is TRUE, and BufferSize - BytesCopied is the room left
+ // in the buffer.
+ if ((int)(BufferSize - BytesCopied) >= (int)sizeof(IPRouteEntry)) {
+ DataLeft = RTReadNext(Context, InfoBuff);
+ BytesCopied += sizeof(IPRouteEntry);
+ Buffer = CopyToNdis(Buffer, InfoBuff, sizeof(IPRouteEntry),
+ &Offset);
+ } else
+ break;
+
+ }
+
+ CTEFreeLock(&RouteTableLock, Handle);
+ Status = (!DataLeft ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW);
+ break;
+ case IP_INTFC_INFO_ID:
+
+ IFAddr = *(IPAddr *)Context;
+ // Loop through the NTE table, looking for a match.
+ for (CurrentNTE = NetTableList; CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) {
+ if ((CurrentNTE->nte_flags & NTE_VALID) &&
+ IP_ADDR_EQUAL(CurrentNTE->nte_addr, IFAddr))
+ break;
+ }
+ if (CurrentNTE == NULL) {
+ Status = TDI_INVALID_PARAMETER;
+ break;
+ }
+
+ if (BufferSize < offsetof(IPInterfaceInfo, iii_addr)) {
+ Status = TDI_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ // We have the NTE. Get the interface, fill in a structure,
+ // and we're done.
+ LowerIF = CurrentNTE->nte_if;
+ IIIPtr = (IPInterfaceInfo *)InfoBuff;
+ IIIPtr->iii_flags = LowerIF->if_flags & IF_FLAGS_P2P ?
+ IP_INTFC_FLAG_P2P : 0;
+ IIIPtr->iii_mtu = LowerIF->if_mtu;
+ IIIPtr->iii_speed = LowerIF->if_speed;
+ IIIPtr->iii_addrlength = LowerIF->if_addrlen;
+ BytesCopied = offsetof(IPInterfaceInfo, iii_addr);
+ if (BufferSize >= (offsetof(IPInterfaceInfo, iii_addr) +
+ LowerIF->if_addrlen)) {
+ Status = TDI_SUCCESS;
+ Buffer = CopyToNdis(Buffer, InfoBuff,
+ offsetof(IPInterfaceInfo, iii_addr), &Offset);
+ CopyToNdis(Buffer, LowerIF->if_addr, LowerIF->if_addrlen,
+ &Offset);
+ BytesCopied += LowerIF->if_addrlen;
+ } else {
+ Status = TDI_BUFFER_TOO_SMALL;
+ }
+ break;
+
+ default:
+ return TDI_INVALID_PARAMETER;
+ break;
+ }
+
+ *Size = BytesCopied;
+ return Status;
+}
+
+//* IPSetInfo - IP set information handler.
+//
+// Called by the upper layer when it wants to set an object, which could
+// be a route table entry, an ARP table entry, or something else.
+//
+// Input: ID - Pointer to ID structure.
+// Buffer - Pointer to buffer containing element to set..
+// Size - Pointer to size in bytes of buffer.
+//
+// Returns: TDI_STATUS of attempt to read information.
+//
+long
+IPSetInfo(TDIObjectID *ID, void *Buffer, uint Size)
+{
+ uint Entity;
+ uint Instance;
+ Interface *LowerIF;
+ Interface *OutIF;
+ uint MTU;
+ IPRouteEntry *IRE;
+ NetTableEntry *OutNTE, *LocalNTE;
+ IP_STATUS Status;
+ IPAddr FirstHop, Dest, NextHop;
+
+ Entity = ID->toi_entity.tei_entity;
+ Instance = ID->toi_entity.tei_instance;
+
+ // If it's not for us, pass it down.
+ if (Entity != CL_NL_ENTITY) {
+ // We need to pass this down to the lower layer. Loop through until
+ // we find one that takes it. If noone does, error out.
+ for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) {
+ Status = (*LowerIF->if_setinfo)(LowerIF->if_lcontext, ID, Buffer,
+ Size);
+ if (Status != TDI_INVALID_REQUEST)
+ return Status;
+ }
+ // If we get here, noone took it. Return an error.
+ return TDI_INVALID_REQUEST;
+ }
+
+ if (Instance != IPInstance)
+ return TDI_INVALID_REQUEST;
+
+ // We're identified as the entity. Make sure the ID is correct.
+ if (ID->toi_id == IP_MIB_RTTABLE_ENTRY_ID) {
+ NetTableEntry *TempNTE;
+
+ // This is an attempt to set a route table entry. Make sure the
+ // size if correct.
+ if (Size < sizeof(IPRouteEntry))
+ return TDI_INVALID_PARAMETER;
+
+ IRE = (IPRouteEntry *)Buffer;
+
+ OutNTE = NULL;
+ LocalNTE = NULL;
+
+ Dest = IRE->ire_dest;
+ NextHop = IRE->ire_nexthop;
+
+ // Make sure that the nexthop is sensible. We don't allow nexthops
+ // to be broadcast or invalid or loopback addresses.
+ if (IP_ADDR_EQUAL(NextHop, NULL_IP_ADDR) || IP_LOOPBACK(NextHop) ||
+ CLASSD_ADDR(NextHop) || CLASSE_ADDR(NextHop))
+ return TDI_INVALID_PARAMETER;
+
+ // Also make sure that the destination we're routing to is sensible.
+ // Don't allow routes to be added to Class D or E or loopback
+ // addresses.
+ if (IP_LOOPBACK(Dest) || CLASSD_ADDR(Dest) || CLASSE_ADDR(Dest))
+ return TDI_INVALID_PARAMETER;
+
+ if (IRE->ire_index == LoopIndex)
+ return TDI_INVALID_PARAMETER;
+
+ if (IRE->ire_index != INVALID_IF_INDEX) {
+
+ // First thing to do is to find the outgoing NTE for specified
+ // interface, and also make sure that it matches the destination
+ // if the destination is one of my addresses.
+ for (TempNTE = NetTableList; TempNTE != NULL;
+ TempNTE = TempNTE->nte_next) {
+ if (OutNTE == NULL && IRE->ire_index == TempNTE->nte_if->if_index)
+ OutNTE = TempNTE;
+ if (IP_ADDR_EQUAL(NextHop, TempNTE->nte_addr) &&
+ (TempNTE->nte_flags & NTE_VALID))
+ LocalNTE = TempNTE;
+
+ // Don't let a route be set through a broadcast address.
+ if (IsBCastOnNTE(NextHop, TempNTE) != DEST_LOCAL)
+ return TDI_INVALID_PARAMETER;
+
+ // Don't let a route to a broadcast address be added or deleted.
+ if (IsBCastOnNTE(Dest, TempNTE) != DEST_LOCAL)
+ return TDI_INVALID_PARAMETER;
+ }
+
+ // At this point OutNTE points to the outgoing NTE, and LocalNTE
+ // points to the NTE for the local address, if this is a direct route.
+ // Make sure they point to the same interface, and that the type is
+ // reasonable.
+ if (OutNTE == NULL)
+ return TDI_INVALID_PARAMETER;
+
+ if (LocalNTE != NULL) {
+ // He's routing straight out a local interface. The interface for
+ // the local address must match the interface passed in, and the
+ // type must be DIRECT (if we're adding) or INVALID (if we're
+ // deleting).
+ if (LocalNTE->nte_if->if_index != IRE->ire_index)
+ return TDI_INVALID_PARAMETER;
+
+ if (IRE->ire_type != IRE_TYPE_DIRECT &&
+ IRE->ire_type != IRE_TYPE_INVALID)
+ return TDI_INVALID_PARAMETER;
+
+ OutNTE = LocalNTE;
+ }
+
+
+ // Figure out what the first hop should be. If he's routing straight
+ // through a local interface, or the next hop is equal to the
+ // destination, then the first hop is IPADDR_LOCAL. Otherwise it's the
+ // address of the gateway.
+ if (LocalNTE != NULL)
+ FirstHop = IPADDR_LOCAL;
+ else
+ if (IP_ADDR_EQUAL(Dest, NextHop))
+ FirstHop = IPADDR_LOCAL;
+ else
+ FirstHop = NextHop;
+
+ MTU = OutNTE->nte_mss;
+ OutIF = OutNTE->nte_if;
+
+ } else {
+ OutIF = (Interface *)&DummyInterface;
+ MTU = DummyInterface.ri_if.if_mtu - sizeof(IPHeader);
+ if (IP_ADDR_EQUAL(Dest, NextHop))
+ FirstHop = IPADDR_LOCAL;
+ else
+ FirstHop = NextHop;
+ }
+
+ // We've done the validation. See if he's adding or deleting a route.
+ if (IRE->ire_type != IRE_TYPE_INVALID) {
+ // He's adding a route.
+ Status = AddRoute(Dest, IRE->ire_mask, FirstHop, OutIF,
+ MTU, IRE->ire_metric1, IRE->ire_proto,
+ ATYPE_OVERRIDE, IRE->ire_context);
+
+ } else {
+ // He's deleting a route.
+ Status = DeleteRoute(Dest, IRE->ire_mask, FirstHop, OutIF);
+ }
+
+ if (Status == IP_SUCCESS)
+ return TDI_SUCCESS;
+ else
+ if (Status == IP_NO_RESOURCES)
+ return TDI_NO_RESOURCES;
+ else
+ return TDI_INVALID_PARAMETER;
+
+ } else {
+ if (ID->toi_id == IP_MIB_STATS_ID) {
+ IPSNMPInfo *Info = (IPSNMPInfo *)Buffer;
+
+ // Setting information about TTL and/or routing.
+ if (Info->ipsi_defaultttl > 255 || (!RouterConfigured &&
+ Info->ipsi_forwarding == IP_FORWARDING)) {
+ return TDI_INVALID_PARAMETER;
+ }
+
+ DefaultTTL = Info->ipsi_defaultttl;
+ ForwardPackets = Info->ipsi_forwarding == IP_FORWARDING ? TRUE :
+ FALSE;
+
+ return TDI_SUCCESS;
+ }
+ return TDI_INVALID_PARAMETER;
+ }
+
+}
+#ifndef CHICAGO
+#pragma BEGIN_INIT
+#endif
+
+//* IPGetEList - Get the entity list.
+//
+// Called at init time to get an entity list. We fill our stuff in, and
+// then call the interfaces below us to allow them to do the same.
+//
+// Input: EntityList - Pointer to entity list to be filled in.
+// Count - Pointer to number of entries in the list.
+//
+// Returns Status of attempt to get the info.
+//
+long
+IPGetEList(TDIEntityID *EList, uint *Count)
+{
+ uint ECount;
+ uint MyIPBase;
+ uint MyERBase;
+ int Status;
+ uint i;
+ Interface *LowerIF;
+ TDIEntityID *EntityList;
+
+ ECount = *Count;
+ EntityList = EList;
+
+ // Walk down the list, looking for existing CL_NL or ER entities, and
+ // adjust our base instance accordingly.
+
+ MyIPBase = 0;
+ MyERBase = 0;
+ for (i = 0; i < ECount; i++, EntityList++) {
+ if (EntityList->tei_entity == CL_NL_ENTITY)
+ MyIPBase = MAX(MyIPBase, EntityList->tei_instance + 1);
+ else
+ if (EntityList->tei_entity == ER_ENTITY)
+ MyERBase = MAX(MyERBase, EntityList->tei_instance + 1);
+ }
+
+ // At this point we've figure out our base instance. Save for later use.
+ IPInstance = MyIPBase;
+ ICMPInstance = MyERBase;
+
+ // EntityList points to the start of where we want to begin filling in.
+ // Make sure we have enough room. We need one for the ICMP instance,
+ // and one for the CL_NL instance.
+
+ if ((ECount + 2) > MAX_TDI_ENTITIES)
+ return TDI_REQ_ABORTED;
+
+ // Now fill it in.
+ EntityList->tei_entity = CL_NL_ENTITY;
+ EntityList->tei_instance = IPInstance;
+ EntityList++;
+ EntityList->tei_entity = ER_ENTITY;
+ EntityList->tei_instance = ICMPInstance;
+ *Count += 2;
+
+ // Loop through the interfaces, querying each of them.
+ for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) {
+ Status = (*LowerIF->if_getelist)(LowerIF->if_lcontext, EList, Count);
+ if (!Status)
+ return TDI_BUFFER_TOO_SMALL;
+ }
+
+ return TDI_SUCCESS;
+}
+
+#ifndef CHICAGO
+#pragma END_INIT
+#endif
diff --git a/private/ntos/tdi/tcpip/ip/info.h b/private/ntos/tdi/tcpip/ip/info.h
new file mode 100644
index 000000000..e8de293e0
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/info.h
@@ -0,0 +1,22 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+#include "ipinfo.h"
+
+extern IPSNMPInfo IPSInfo;
+extern ICMPStats ICMPInStats;
+extern ICMPStats ICMPOutStats;
+
+typedef struct RouteEntryContext {
+ uint rec_index;
+ struct RouteTableEntry *rec_rte;
+} RouteEntryContext;
+
+extern long IPQueryInfo(struct TDIObjectID *ID, PNDIS_BUFFER Buffer,
+ uint *Size, void *Context);
+extern long IPSetInfo(struct TDIObjectID *ID, void *Buffer, uint Size);
+extern long IPGetEList(struct TDIEntityID *Buffer, uint *Count);
+
diff --git a/private/ntos/tdi/tcpip/ip/init.c b/private/ntos/tdi/tcpip/ip/init.c
new file mode 100644
index 000000000..3f032a50a
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/init.c
@@ -0,0 +1,3509 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** Init.c - IP VxD init routines.
+//
+// All C init routines are located in this file. We get
+// config. information, allocate structures, and generally get things going.
+
+#include "oscfg.h"
+#include "cxport.h"
+#include "ndis.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "ipinit.h"
+#include "llipif.h"
+#include "arp.h"
+#include "info.h"
+#include "iproute.h"
+#include "iprtdef.h"
+#include "ipxmit.h"
+#include "igmp.h"
+#include "icmp.h"
+#include <tdiinfo.h>
+
+#ifdef NT
+#include <tdi.h>
+#include <tdikrnl.h>
+#endif
+
+
+#define NUM_IP_NONHDR_BUFFERS 50
+
+#define DEFAULT_RA_TIMEOUT 60
+
+#define DEFAULT_ICMP_BUFFERS 5
+
+extern IPConfigInfo *IPGetConfig(void);
+extern void IPFreeConfig(IPConfigInfo *);
+extern int IsIPBCast(IPAddr, uchar);
+
+extern uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE *Handle);
+extern void CloseIFConfig(NDIS_HANDLE Handle);
+
+// The IPRcv routine.
+extern void IPRcv(void *, void *, uint , uint , NDIS_HANDLE , uint , uint );
+// The transmit complete routine.
+extern void IPSendComplete(void *, PNDIS_PACKET , NDIS_STATUS );
+// Status indication routine.
+extern void IPStatus(void *, NDIS_STATUS, void *, uint);
+// Transfer data complete routine.
+extern void IPTDComplete(void *, PNDIS_PACKET , NDIS_STATUS , uint );
+
+extern void IPRcvComplete(void);
+
+extern void ICMPInit(uint);
+extern uint IGMPInit(void);
+extern void ICMPTimer(NetTableEntry *);
+extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong);
+extern void TDUserRcv(void *, PNDIS_PACKET, NDIS_STATUS, uint);
+extern void FreeRH(ReassemblyHeader *);
+extern PNDIS_PACKET GrowIPPacketList(void);
+extern PNDIS_BUFFER FreeIPPacket(PNDIS_PACKET Packet);
+
+extern ulong GetGMTDelta(void);
+extern ulong GetTime(void);
+extern ulong GetUnique32BitValue(void);
+
+extern void NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context,
+ ushort IPContext, PVOID *Handle, PNDIS_STRING ConfigName, uint Added);
+
+uint IPSetNTEAddr(ushort Context, IPAddr Addr, IPMask Mask, SetAddrControl *ControlBlock, SetAddrRtn Rtn);
+
+extern NDIS_HANDLE BufferPool;
+EXTERNAL_LOCK(HeaderLock)
+#ifdef NT
+extern SLIST_HEADER PacketList;
+extern SLIST_HEADER HdrBufList;
+#endif
+
+extern NetTableEntry *LoopNTE;
+
+extern uchar RouterConfigured;
+
+//NetTableEntry *NetTable; // Pointer to the net table.
+
+NetTableEntry *NetTableList; // List of NTEs.
+int NumNTE; // Number of NTEs.
+
+AddrTypeCache ATCache[ATC_SIZE];
+uint ATCIndex;
+
+uchar RATimeout; // Number of seconds to time out a
+ // reassembly.
+ushort NextNTEContext; // Next NTE context to use.
+
+#if 0
+DEFINE_LOCK_STRUCTURE(PILock)
+#endif
+
+ProtInfo IPProtInfo[MAX_IP_PROT]; // Protocol information table.
+ProtInfo *LastPI; // Last protinfo structure looked at.
+int NextPI; // Next PI field to be used.
+ProtInfo *RawPI = NULL; // Raw IP protinfo
+
+ulong TimeStamp;
+ulong TSFlag;
+
+uint DefaultTTL;
+uint DefaultTOS;
+uchar TrRii = TR_RII_ALL;
+
+// Interface *IFTable[MAX_IP_NETS];
+Interface *IFList; // List of interfaces active.
+Interface *FirstIF; // First 'real' IF.
+ulong NumIF;
+
+#ifdef _PNP_POWER
+#define BITS_PER_WORD 32
+ulong IFBitMask[(MAX_TDI_ENTITIES / BITS_PER_WORD) + 1];
+#endif // _PNP_POWER
+
+IPSNMPInfo IPSInfo;
+uint DHCPActivityCount = 0;
+uint IGMPLevel;
+
+#ifdef NT
+
+#ifndef _PNP_POWER
+
+extern NameMapping *AdptNameTable;
+extern DriverRegMapping *DriverNameTable;
+
+#endif // _PNP_POWER
+
+VOID
+SetPersistentRoutesForNTE(
+ IPAddr Address,
+ IPMask Mask,
+ ULONG IFIndex
+ );
+
+#else // NT
+
+#ifndef _PNP_POWER
+
+extern NameMapping AdptNameTable[];
+extern DriverRegMapping DriverNameTable[];
+
+#endif // _PNP_POWER
+
+#endif // NT
+
+#ifndef _PNP_POWER
+extern uint NumRegDrivers;
+uint MaxIPNets = 0;
+#endif // _PNP_POWER
+
+uint InterfaceSize; // Size of a net interface.
+NetTableEntry *DHCPNTE = NULL;
+
+
+#ifdef NT
+
+#ifdef ALLOC_PRAGMA
+//
+// Make init code disposable.
+//
+void InitTimestamp();
+int InitNTE(NetTableEntry *NTE);
+int InitInterface(NetTableEntry *NTE);
+LLIPRegRtn GetLLRegPtr(PNDIS_STRING Name);
+LLIPRegRtn FindRegPtr(PNDIS_STRING Name);
+uint IPRegisterDriver(PNDIS_STRING Name, LLIPRegRtn Ptr);
+void CleanAdaptTable();
+void OpenAdapters();
+int IPInit();
+
+#if 0 // BUGBUG: These can eventually be made init time only.
+
+#pragma alloc_text(INIT, IPGetInfo)
+#pragma alloc_text(INIT, IPTimeout)
+
+#endif // 0
+
+#pragma alloc_text(INIT, InitTimestamp)
+#ifndef _PNP_POWER
+#pragma alloc_text(INIT, InitNTE)
+#pragma alloc_text(INIT, InitInterface)
+#endif
+#pragma alloc_text(INIT, CleanAdaptTable)
+#pragma alloc_text(INIT, OpenAdapters)
+#pragma alloc_text(INIT, IPRegisterDriver)
+#pragma alloc_text(INIT, GetLLRegPtr)
+#pragma alloc_text(INIT, FindRegPtr)
+#pragma alloc_text(INIT, IPInit)
+
+
+//
+// Pagable code
+//
+uint
+IPAddDynamicNTE(ushort InterfaceContext, IPAddr NewAddr, IPMask NewMask,
+ ushort *NTEContext, ulong *NTEInstance);
+
+#pragma alloc_text(PAGE, IPAddDynamicNTE)
+
+#endif // ALLOC_PRAGMA
+
+extern PDRIVER_OBJECT IPDriverObject;
+
+NTSTATUS
+SetRegDWORDValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PULONG ValueData
+ );
+
+//
+// Debugging macros
+//
+#if DBG
+
+#define TCPTRACE(many_args) DbgPrint many_args
+
+#else // DBG
+
+#define TCPTRACE(many_args)
+
+#endif // DBG
+
+
+// SetIFContext - Set the context on a particular interface.
+//
+// A routine to set the filter context on a particular interface.
+//
+// Input: Index - Interface index of i/f to be set.
+// Context - Context to set.
+//
+// Returns: Status of attempt.
+//
+IP_STATUS
+SetIFContext(uint Index, INTERFACE_CONTEXT *Context)
+{
+ Interface *IF;
+
+ // Walk the list, looking for a matching index.
+ for (IF = IFList; IF != NULL; IF = IF->if_next) {
+ if (IF->if_index == Index) {
+ IF->if_filtercontext = Context;
+ break;
+ }
+ }
+
+ // If we found one, return success. Otherwise fail.
+ if (IF != NULL) {
+ return IP_SUCCESS;
+ } else {
+ return IP_GENERAL_FAILURE;
+ }
+}
+
+// SetFilterPtr - A routine to set the filter pointer.
+//
+// This routine sets the IP forwarding filter callout.
+//
+// Input: FilterPtr - Pointer to routine to call when filtering. May
+// be NULL.
+//
+// Returns: IP_SUCCESS.
+//
+IP_STATUS
+SetFilterPtr(IPPacketFilterPtr FilterPtr)
+{
+ Interface *IF;
+
+ //
+ // If the pointer is being set to NULL, means filtering is
+ // being turned off. Remove all the contexts we have
+ //
+
+ if(FilterPtr == NULL)
+ {
+
+ for (IF = IFList; IF != NULL; IF = IF->if_next)
+ {
+ IF->if_filtercontext = NULL;
+ }
+ }
+
+ ForwardFilterPtr = FilterPtr;
+
+ return IP_SUCCESS;
+}
+
+// SetMapRoutePtr - A routine to set the dial on demand callout pointer.
+//
+// This routine sets the IP dial on demand callout.
+//
+// Input: MapRoutePtr - Pointer to routine to call when we need to bring
+// up a link. May be NULL
+//
+// Returns: IP_SUCCESS.
+//
+IP_STATUS
+SetMapRoutePtr(IPMapRouteToInterfacePtr MapRoutePtr)
+{
+ DODCallout = MapRoutePtr;
+ return IP_SUCCESS;
+}
+
+#endif // NT
+
+
+//** SetDHCPNTE
+//
+// Routine to identify which NTE is currently being DHCP'ed. We take as input
+// an nte_context. If the context is less than the max NTE context, we look
+// for a matching NTE and if we find him we save a pointer. If we don't we
+// fail. If the context > max NTE context we're disabling DHCPing, and
+// we NULL out the save pointer.
+//
+// Input: Context - NTE context value.
+//
+// Returns: TRUE if we succeed, FALSE if we don't.
+//
+uint
+SetDHCPNTE(uint Context)
+{
+ CTELockHandle Handle;
+ NetTableEntry *NTE;
+ ushort NTEContext;
+ uint RetCode;
+
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ if (Context <= 0xffff) {
+ // We're setting the DHCP NTE. Look for one matching the context.
+
+ NTEContext = (ushort)Context;
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
+ if (NTE != LoopNTE && NTE->nte_context == NTEContext) {
+ // Found one. Save it and break out.
+ DHCPNTE = NTE;
+ break;
+ }
+ }
+
+ RetCode = (NTE != NULL);
+ } else {
+ // The context is invalid, so we're deleting the DHCP NTE.
+ DHCPNTE = NULL;
+ RetCode = TRUE;
+ }
+
+ CTEFreeLock(&RouteTableLock, Handle);
+
+ return RetCode;
+}
+
+
+//** SetDHCPNTE
+//
+// Routine for upper layers to call to check if the IPContext value passed
+// up to a RcvHandler identifies an interface that is currently being
+// DHCP'd.
+//
+// Input: Context - Pointer to an NTE
+//
+// Returns: TRUE if we succeed, FALSE if we don't.
+//
+uint
+IsDHCPInterface(void *IPContext)
+{
+// CTELockHandle Handle;
+ uint RetCode;
+ NetTableEntry *NTE = (NetTableEntry *) IPContext;
+
+
+// CTEGetLock(&RouteTableLock, &Handle);
+
+ if (DHCPNTE == NTE) {
+ RetCode = TRUE;
+ }
+ else {
+ RetCode = FALSE;
+ }
+
+// CTEFreeLock(&RouteTableLock, Handle);
+
+ return(RetCode);
+}
+
+
+//** CloseNets - Close active nets.
+//
+// Called when we need to close some lower layer interfaces.
+//
+// Entry: Nothing
+//
+// Returns: Nothing
+//
+void
+CloseNets(void)
+{
+ NetTableEntry *nt;
+
+ for (nt = NetTableList; nt != NULL; nt = nt->nte_next)
+ (*nt->nte_if->if_close)(nt->nte_if->if_lcontext); // Call close routine for this net.
+}
+
+//** IPRegisterProtocol - Register a protocol with IP.
+//
+// Called by upper layer software to register a protocol. The UL supplies
+// pointers to receive routines and a protocol value to be used on xmits/receives.
+//
+// Entry:
+// Protocol - Protocol value to be returned.
+// RcvHandler - Receive handler to be called when frames for Protocol are received.
+// XmitHandler - Xmit. complete handler to be called when frames from Protocol are completed.
+// StatusHandler - Handler to be called when status indication is to be delivered.
+//
+// Returns:
+// Pointer to ProtInfo,
+//
+void *
+IPRegisterProtocol(uchar Protocol, void *RcvHandler, void *XmitHandler,
+ void *StatusHandler, void *RcvCmpltHandler)
+{
+ ProtInfo *PI = (ProtInfo *)NULL;
+ int i;
+ int Incr;
+#if 0
+ CTELockHandle Handle;
+
+
+ CTEGetLock(&PILock, &Handle);
+#endif
+
+ // First check to see if it's already registered. If it is just replace it.
+ for (i = 0; i < NextPI; i++)
+ if (IPProtInfo[i].pi_protocol == Protocol) {
+ PI = &IPProtInfo[i];
+ Incr = 0;
+ break;
+ }
+
+ if (PI == (ProtInfo *)NULL) {
+ if (NextPI >= MAX_IP_PROT) {
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+ return NULL;
+ }
+
+ PI = &IPProtInfo[NextPI];
+ Incr = 1;
+
+ if (Protocol == PROTOCOL_ANY) {
+ RawPI = PI;
+ }
+ }
+
+ PI->pi_protocol = Protocol;
+ PI->pi_rcv = RcvHandler;
+ PI->pi_xmitdone = XmitHandler;
+ PI->pi_status = StatusHandler;
+ PI->pi_rcvcmplt = RcvCmpltHandler;
+ NextPI += Incr;
+
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+
+#ifndef _PNP_POWER
+#ifdef SECFLTR
+
+ //
+ // If this was a registration, call the status routine of each protocol
+ // to inform it of all existing interfaces. Yes, this is a hack, but
+ // it will work until PnP is turned on in NT.
+ //
+ // It is assumed that none of the upper layer status routines call back
+ // into IP.
+ //
+ // Note that we don't hold any locks here since no one manipulates the
+ // NTE list in a non-PNP build during the init phase.
+ //
+ if (StatusHandler != NULL) {
+ NetTableEntry *NTE;
+ NDIS_HANDLE ConfigHandle = NULL;
+ int i;
+
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
+ if ( !(IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) &&
+ !(IP_LOOPBACK_ADDR(NTE->nte_addr))
+ )
+ {
+ //
+ // Open a configuration key
+ //
+ if (!OpenIFConfig(&(NTE->nte_if->if_configname), &ConfigHandle))
+ {
+ //
+ // Not much we can do. The transports will have
+ // to handle this.
+ //
+ CTEAssert(ConfigHandle == NULL);
+ }
+
+ (* ((ULStatusProc) StatusHandler))(IP_HW_STATUS, IP_ADDR_ADDED,
+ NTE->nte_addr, NULL_IP_ADDR, NULL_IP_ADDR, 0, ConfigHandle );
+
+ if (ConfigHandle != NULL) {
+ CloseIFConfig(ConfigHandle);
+ ConfigHandle = NULL;
+ }
+ }
+ }
+ }
+
+#endif // SECFLTR
+#endif // _PNP_POWER
+
+ return PI;
+}
+
+//** IPSetMCastAddr - Set/Delete a multicast address.
+//
+// Called by an upper layer protocol or client to set or delete an IP multicast
+// address.
+//
+// Input: Address - Address to be set/deleted.
+// IF - IP Address of interface to set/delete on.
+// Action - TRUE if we're setting, FALSE if we're deleting.
+//
+// Returns: IP_STATUS of set/delete attempt.
+//
+IP_STATUS
+IPSetMCastAddr(IPAddr Address, IPAddr IF, uint Action)
+{
+ NetTableEntry *LocalNTE;
+
+ // Don't let him do this on the loopback address, since we don't have a
+ // route table entry for class D address on the loopback interface and
+ // we don't want a packet with a loopback source address to show up on
+ // the wire.
+ if (IP_LOOPBACK_ADDR(IF))
+ return IP_BAD_REQ;
+
+ for (LocalNTE = NetTableList; LocalNTE != NULL;
+ LocalNTE = LocalNTE->nte_next) {
+ if (LocalNTE != LoopNTE && ((LocalNTE->nte_flags & NTE_VALID) &&
+ (IP_ADDR_EQUAL(IF, NULL_IP_ADDR) ||
+ IP_ADDR_EQUAL(IF, LocalNTE->nte_addr))))
+ break;
+ }
+
+ if (LocalNTE == NULL) {
+ // Couldn't find a matching NTE.
+ return IP_BAD_REQ;
+ }
+
+ return IGMPAddrChange(LocalNTE, Address, Action ? IGMP_ADD : IGMP_DELETE);
+
+
+}
+
+//** IPGetAddrType - Return the type of a address.
+//
+// Called by the upper layer to determine the type of a remote address.
+//
+// Input: Address - The address in question.
+//
+// Returns: The DEST type of the address.
+//
+uchar
+IPGetAddrType(IPAddr Address)
+{
+ return GetAddrType(Address);
+}
+
+//** IPGetLocalMTU - Return the MTU for a local address
+//
+// Called by the upper layer to get the local MTU for a local address.
+//
+// Input: LocalAddr - Local address in question.
+// MTU - Where to return the local MTU.
+//
+// Returns: TRUE if we found the MTU, FALSE otherwise.
+//
+uchar
+IPGetLocalMTU(IPAddr LocalAddr, ushort *MTU)
+{
+ NetTableEntry *NTE;
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
+ if (IP_ADDR_EQUAL(NTE->nte_addr, LocalAddr) &&
+ (NTE->nte_flags & NTE_VALID)) {
+ *MTU = NTE->nte_mss;
+ return TRUE;
+ }
+ }
+
+ // Special case in case the local address is a loopback address other than
+ // 127.0.0.1.
+ if (IP_LOOPBACK_ADDR(LocalAddr)) {
+ *MTU = LoopNTE->nte_mss;
+ return TRUE;
+ }
+
+ return FALSE;
+
+}
+
+//** IPUpdateRcvdOptions - Update options for use in replying.
+//
+// A routine to update options for use in a reply. We reverse any source route options,
+// and optionally update the record route option. We also return the index into the
+// options of the record route options (if we find one). The options are assumed to be
+// correct - no validation is performed on them. We fill in the caller provided
+// IPOptInfo with the new option buffer.
+//
+// Input: Options - Pointer to option info structure with buffer to be reversed.
+// NewOptions - Pointer to option info structure to be filled in.
+// Src - Source address of datagram that generated the options.
+// LocalAddr - Local address responding. If this != NULL_IP_ADDR, then
+// record route and timestamp options will be updated with this
+// address.
+//
+//
+// Returns: Index into options of record route option, if any.
+//
+IP_STATUS
+IPUpdateRcvdOptions(IPOptInfo *OldOptions, IPOptInfo *NewOptions, IPAddr Src, IPAddr LocalAddr)
+{
+ uchar Length, Ptr;
+ uchar i; // Index variable
+ IPAddr UNALIGNED *LastAddr; // First address in route.
+ IPAddr UNALIGNED *FirstAddr; // Last address in route.
+ IPAddr TempAddr; // Temp used in exchange.
+ uchar *Options, OptLength;
+ OptIndex Index; // Optindex used by UpdateOptions.
+
+ Options = CTEAllocMem(OptLength = OldOptions->ioi_optlength);
+
+ if (!Options)
+ return IP_NO_RESOURCES;
+
+ CTEMemCopy(Options, OldOptions->ioi_options, OptLength);
+ Index.oi_srindex = MAX_OPT_SIZE;
+ Index.oi_rrindex = MAX_OPT_SIZE;
+ Index.oi_tsindex = MAX_OPT_SIZE;
+
+ NewOptions->ioi_flags &= ~IP_FLAG_SSRR;
+
+ i = 0;
+ while(i < OptLength) {
+ if (Options[i] == IP_OPT_EOL)
+ break;
+
+ if (Options[i] == IP_OPT_NOP) {
+ i++;
+ continue;
+ }
+
+ Length = Options[i+IP_OPT_LENGTH];
+ switch (Options[i]) {
+ case IP_OPT_SSRR:
+ NewOptions->ioi_flags |= IP_FLAG_SSRR;
+ case IP_OPT_LSRR:
+ // Have a source route. We save the last gateway we came through as
+ // the new address, reverse the list, shift the list forward one address,
+ // and set the Src address as the last gateway in the list.
+
+ // First, check for an empty source route. If the SR is empty
+ // we'll skip most of this.
+ if (Length != (MIN_RT_PTR - 1)) {
+ // A non empty source route.
+ // First reverse the list in place.
+ Ptr = Options[i+IP_OPT_PTR] - 1 - sizeof(IPAddr);
+ LastAddr = (IPAddr *)(&Options[i + Ptr]);
+ FirstAddr = (IPAddr *)(&Options[i + IP_OPT_PTR + 1]);
+ NewOptions->ioi_addr = *LastAddr; // Save Last address as
+ // first hop of new route.
+ while (LastAddr > FirstAddr) {
+ TempAddr = *LastAddr;
+ *LastAddr-- = *FirstAddr;
+ *FirstAddr++ = TempAddr;
+ }
+
+ // Shift the list forward one address. We'll copy all but
+ // one IP address.
+ CTEMemCopy(&Options[i + IP_OPT_PTR + 1],
+ &Options[i + IP_OPT_PTR + 1 + sizeof(IPAddr)],
+ Length - (sizeof(IPAddr) + (MIN_RT_PTR -1)));
+
+ // Set source as last address of route.
+ *(IPAddr UNALIGNED *)(&Options[i + Ptr]) = Src;
+ }
+
+ Options[i+IP_OPT_PTR] = MIN_RT_PTR; // Set pointer to min legal value.
+ i += Length;
+ break;
+ case IP_OPT_RR:
+ // Save the index in case LocalAddr is specified. If it isn't specified,
+ // reset the pointer and zero the option.
+ Index.oi_rrindex = i;
+ if (LocalAddr == NULL_IP_ADDR) {
+ CTEMemSet(&Options[i+MIN_RT_PTR-1], 0, Length - (MIN_RT_PTR-1));
+ Options[i+IP_OPT_PTR] = MIN_RT_PTR;
+ }
+ i += Length;
+ break;
+ case IP_OPT_TS:
+ Index.oi_tsindex = i;
+
+ // We have a timestamp option. If we're not going to update, reinitialize
+ // it for next time. For the 'unspecified' options, just zero the buffer.
+ // For the 'specified' options, we need to zero the timestamps without
+ // zeroing the specified addresses.
+ if (LocalAddr == NULL_IP_ADDR) { // Not going to update, reinitialize.
+ uchar Flags;
+
+ Options[i+IP_OPT_PTR] = MIN_TS_PTR; // Reinitialize pointer.
+ Flags = Options[i+IP_TS_OVFLAGS] & IP_TS_FLMASK; // Get option type.
+ Options[i+IP_TS_OVFLAGS] = Flags; // Clear overflow count.
+ switch (Flags) {
+ uchar j;
+ ulong UNALIGNED *TSPtr;
+
+ // The unspecified types. Just clear the buffer.
+ case TS_REC_TS:
+ case TS_REC_ADDR:
+ CTEMemSet(&Options[i+MIN_TS_PTR-1], 0, Length - (MIN_TS_PTR-1));
+ break;
+
+ // We have a list of addresses specified. Just clear the timestamps.
+ case TS_REC_SPEC:
+ // j starts off as the offset in bytes from start of buffer to
+ // first timestamp.
+ j = MIN_TS_PTR-1+sizeof(IPAddr);
+ // TSPtr points at timestamp.
+ TSPtr = (ulong UNALIGNED *)&Options[i+j];
+
+ // Now j is offset of end of timestamp being zeroed.
+ j += sizeof(ulong);
+ while (j <= Length) {
+ *TSPtr++ = 0;
+ j += sizeof(ulong);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ i += Length;
+ break;
+
+ default:
+ i += Length;
+ break;
+ }
+
+ }
+
+ if (LocalAddr != NULL_IP_ADDR) {
+ UpdateOptions(Options, &Index, LocalAddr);
+ }
+
+ NewOptions->ioi_optlength = OptLength;
+ NewOptions->ioi_options = Options;
+ return IP_SUCCESS;
+
+}
+
+//* ValidRouteOption - Validate a source or record route option.
+//
+// Called to validate that a user provided source or record route option is good.
+//
+// Entry: Option - Pointer to option to be checked.
+// NumAddr - NumAddr that need to fit in option.
+// BufSize - Maximum size of option.
+//
+// Returns: 1 if option is good, 0 if not.
+//
+uchar
+ValidRouteOption(uchar *Option, uint NumAddr, uint BufSize)
+{
+ if (Option[IP_OPT_LENGTH] < (3 + (sizeof(IPAddr)*NumAddr)) ||
+ Option[IP_OPT_LENGTH] > BufSize ||
+ ((Option[IP_OPT_LENGTH] - 3) % sizeof(IPAddr))) // Routing options is too small.
+ return 0;
+
+ if (Option[IP_OPT_PTR] != MIN_RT_PTR) // Pointer isn't correct.
+ return 0;
+
+ return 1;
+}
+
+//** IPInitOptions - Initialize an option buffer.
+//
+// Called by an upper layer routine to initialize an option buffer. We fill
+// in the default values for TTL, TOS, and flags, and NULL out the options
+// buffer and size.
+//
+// Input: Options - Pointer to IPOptInfo structure.
+//
+// Returns: Nothing.
+//
+void
+IPInitOptions(IPOptInfo *Options)
+{
+ Options->ioi_addr = NULL_IP_ADDR;
+
+ Options->ioi_ttl = (uchar)DefaultTTL;
+ Options->ioi_tos = (uchar)DefaultTOS;
+ Options->ioi_flags = 0;
+
+ Options->ioi_options = (uchar *)NULL;
+ Options->ioi_optlength = 0;
+
+}
+
+//** IPCopyOptions - Copy the user's options into IP header format.
+//
+// This routine takes an option buffer supplied by an IP client, validates it, and
+// creates an IPOptInfo structure that can be passed to the IP layer for transmission. This
+// includes allocating a buffer for the options, munging any source route
+// information into the real IP format.
+//
+// Note that we never lock this structure while we're using it. This may cause transitory
+// incosistencies while the structure is being updated if it is in use during the update.
+// This shouldn't be a problem - a packet or too might get misrouted, but it should
+// straighten itself out quickly. If this is a problem the client should make sure not
+// to call this routine while it's in the IPTransmit routine.
+//
+// Entry: Options - Pointer to buffer of user supplied options.
+// Size - Size in bytes of option buffer
+// OptInfoPtr - Pointer to IPOptInfo structure to be filled in.
+//
+// Returns: A status, indicating whether or not the options were valid and copied.
+//
+IP_STATUS
+IPCopyOptions(uchar *Options, uint Size, IPOptInfo *OptInfoPtr)
+{
+ uchar *TempOptions; // Buffer of options we'll build
+ uint TempSize; // Size of options.
+ IP_STATUS TempStatus; // Temporary status
+ uchar OptSeen = 0; // Indicates which options we've seen.
+
+
+ OptInfoPtr->ioi_addr = NULL_IP_ADDR;
+
+ OptInfoPtr->ioi_flags &= ~IP_FLAG_SSRR;
+
+ if (Size == 0) {
+ CTEAssert(FALSE);
+ OptInfoPtr->ioi_options = (uchar *)NULL;
+ OptInfoPtr->ioi_optlength = 0;
+ return IP_SUCCESS;
+ }
+
+
+ // Option size needs to be rounded to multiple of 4.
+ if ((TempOptions = CTEAllocMem(((Size & 3) ? (Size & ~3) + 4 : Size))) == (uchar *)NULL)
+ return IP_NO_RESOURCES; // Couldn't get a buffer, return error.
+
+ CTEMemSet(TempOptions, 0, ((Size & 3) ? (Size & ~3) + 4 : Size));
+
+ // OK, we have a buffer. Loop through the provided buffer, copying options.
+ TempSize = 0;
+ TempStatus = IP_PENDING;
+ while (Size && TempStatus == IP_PENDING) {
+ uint SRSize; // Size of a source route option.
+
+ switch (*Options) {
+ case IP_OPT_EOL:
+ TempStatus = IP_SUCCESS;
+ break;
+ case IP_OPT_NOP:
+ TempOptions[TempSize++] = *Options++;
+ Size--;
+ break;
+ case IP_OPT_SSRR:
+ if (OptSeen & (OPT_LSRR | OPT_SSRR)) {
+ TempStatus = IP_BAD_OPTION; // We've already seen a record route.
+ break;
+ }
+ OptInfoPtr->ioi_flags |= IP_FLAG_SSRR;
+ OptSeen |= OPT_SSRR; // Fall through to LSRR code.
+ case IP_OPT_LSRR:
+ if ( (*Options == IP_OPT_LSRR) &&
+ (OptSeen & (OPT_LSRR | OPT_SSRR))
+ ) {
+ TempStatus = IP_BAD_OPTION; // We've already seen a record route.
+ break;
+ }
+ if (*Options == IP_OPT_LSRR)
+ OptSeen |= OPT_LSRR;
+ if (!ValidRouteOption(Options, 2, Size)) {
+ TempStatus = IP_BAD_OPTION;
+ break;
+ }
+
+ // Option is valid. Copy the first hop address to NewAddr, and move all
+ // of the other addresses forward.
+ TempOptions[TempSize++] = *Options++; // Copy option type.
+ SRSize = *Options++;
+ Size -= SRSize;
+ SRSize -= sizeof(IPAddr);
+ TempOptions[TempSize++] = SRSize;
+ TempOptions[TempSize++] = *Options++; // Copy pointer.
+ OptInfoPtr->ioi_addr = *(IPAddr UNALIGNED *)Options;
+ Options += sizeof(IPAddr); // Point to address beyond first hop.
+ CTEMemCopy(&TempOptions[TempSize], Options, SRSize - 3);
+ TempSize += (SRSize - 3);
+ Options += (SRSize - 3);
+ break;
+ case IP_OPT_RR:
+ if (OptSeen & OPT_RR) {
+ TempStatus = IP_BAD_OPTION; // We've already seen a record route.
+ break;
+ }
+ OptSeen |= OPT_RR;
+ if (!ValidRouteOption(Options, 1, Size)) {
+ TempStatus = IP_BAD_OPTION;
+ break;
+ }
+ SRSize = Options[IP_OPT_LENGTH];
+ CTEMemCopy(&TempOptions[TempSize], Options, SRSize);
+ TempSize += SRSize;
+ Options += SRSize;
+ Size -= SRSize;
+ break;
+ case IP_OPT_TS:
+ {
+ uchar Overflow, Flags;
+
+ if (OptSeen & OPT_TS) {
+ TempStatus = IP_BAD_OPTION; // We've already seen a time stamp
+ break;
+ }
+ OptSeen |= OPT_TS;
+ Flags = Options[IP_TS_OVFLAGS] & IP_TS_FLMASK;
+ Overflow = (Options[IP_TS_OVFLAGS] & IP_TS_OVMASK) >> 4;
+
+ if (Overflow || (Flags != TS_REC_TS && Flags != TS_REC_ADDR &&
+ Flags != TS_REC_SPEC)) {
+ TempStatus = IP_BAD_OPTION; // Bad flags or overflow value.
+ break;
+ }
+
+ SRSize = Options[IP_OPT_LENGTH];
+ if (SRSize > Size || SRSize < 8 ||
+ Options[IP_OPT_PTR] != MIN_TS_PTR) {
+ TempStatus = IP_BAD_OPTION; // Option size isn't good.
+ break;
+ }
+ CTEMemCopy(&TempOptions[TempSize], Options, SRSize);
+ TempSize += SRSize;
+ Options += SRSize;
+ Size -= SRSize;
+ }
+ break;
+ default:
+ TempStatus = IP_BAD_OPTION; // Unknown option, error.
+ break;
+ }
+ }
+
+ if (TempStatus == IP_PENDING) // We broke because we hit the end of the buffer.
+ TempStatus = IP_SUCCESS; // that's OK.
+
+ if (TempStatus != IP_SUCCESS) { // We had some sort of an error.
+ CTEFreeMem(TempOptions);
+ return TempStatus;
+ }
+
+ // Check the option size here to see if it's too big. We check it here at the end
+ // instead of at the start because the option size may shrink if there are source route
+ // options, and we don't want to accidentally error out a valid option.
+ TempSize = (TempSize & 3 ? (TempSize & ~3) + 4 : TempSize);
+ if (TempSize > MAX_OPT_SIZE) {
+ CTEFreeMem(TempOptions);
+ return IP_OPTION_TOO_BIG;
+ }
+ OptInfoPtr->ioi_options = TempOptions;
+ OptInfoPtr->ioi_optlength = TempSize;
+
+ return IP_SUCCESS;
+
+}
+
+//** IPFreeOptions - Free options we're done with.
+//
+// Called by the upper layer when we're done with options. All we need to do is free
+// the options.
+//
+// Input: OptInfoPtr - Pointer to IPOptInfo structure to be freed.
+//
+// Returns: Status of attempt to free options.
+//
+IP_STATUS
+IPFreeOptions(IPOptInfo *OptInfoPtr)
+{
+ if (OptInfoPtr->ioi_options) {
+ // We have options to free. Save the pointer and zero the structure field before
+ // freeing the memory to try and present race conditions with it's use.
+ uchar *TempPtr = OptInfoPtr->ioi_options;
+
+ OptInfoPtr->ioi_options = (uchar *)NULL;
+ CTEFreeMem(TempPtr);
+ OptInfoPtr->ioi_optlength = 0;
+ OptInfoPtr->ioi_addr = NULL_IP_ADDR;
+ OptInfoPtr->ioi_flags &= ~IP_FLAG_SSRR;
+ }
+ return IP_SUCCESS;
+}
+
+
+//BUGBUG - After we're done testing, move BEGIN_INIT up here.
+
+//** ipgetinfo - Return pointers to our NetInfo structures.
+//
+// Called by upper layer software during init. time. The caller
+// passes a buffer, which we fill in with pointers to NetInfo
+// structures.
+//
+// Entry:
+// Buffer - Pointer to buffer to be filled in.
+// Size - Size in bytes of buffer.
+//
+// Returns:
+// Status of command.
+//
+IP_STATUS
+IPGetInfo(IPInfo *Buffer, int Size)
+{
+ if (Size < sizeof(IPInfo))
+ return IP_BUF_TOO_SMALL; // Not enough buffer space.
+
+ Buffer->ipi_version = IP_DRIVER_VERSION;
+ Buffer->ipi_hsize = sizeof(IPHeader);
+ Buffer->ipi_xmit = IPTransmit;
+ Buffer->ipi_protreg = IPRegisterProtocol;
+ Buffer->ipi_openrce = OpenRCE;
+ Buffer->ipi_closerce = CloseRCE;
+ Buffer->ipi_getaddrtype = IPGetAddrType;
+ Buffer->ipi_getlocalmtu = IPGetLocalMTU;
+ Buffer->ipi_getpinfo = IPGetPInfo;
+ Buffer->ipi_checkroute = IPCheckRoute;
+ Buffer->ipi_initopts = IPInitOptions;
+ Buffer->ipi_updateopts = IPUpdateRcvdOptions;
+ Buffer->ipi_copyopts = IPCopyOptions;
+ Buffer->ipi_freeopts = IPFreeOptions;
+ Buffer->ipi_qinfo = IPQueryInfo;
+ Buffer->ipi_setinfo = IPSetInfo;
+ Buffer->ipi_getelist = IPGetEList;
+ Buffer->ipi_setmcastaddr = IPSetMCastAddr;
+ Buffer->ipi_invalidsrc = InvalidSourceAddress;
+ Buffer->ipi_isdhcpinterface = IsDHCPInterface;
+
+ return IP_SUCCESS;
+
+}
+
+//** IPTimeout - IP timeout handler.
+//
+// The timeout routine called periodically to time out various things, such as entries
+// being reassembled and ICMP echo requests.
+//
+// Entry: Timer - Timer being fired.
+// Context - Pointer to NTE being time out.
+//
+// Returns: Nothing.
+//
+void
+IPTimeout(CTEEvent *Timer, void *Context)
+{
+ NetTableEntry *NTE = STRUCT_OF(NetTableEntry, Timer, nte_timer);
+ CTELockHandle NTEHandle;
+ ReassemblyHeader *PrevRH, *CurrentRH, *TempList = (ReassemblyHeader *)NULL;
+
+ ICMPTimer(NTE);
+ IGMPTimer(NTE);
+ if (Context) {
+ CTEGetLock(&NTE->nte_lock, &NTEHandle);
+ PrevRH = STRUCT_OF(ReassemblyHeader, &NTE->nte_ralist, rh_next);
+ CurrentRH = PrevRH->rh_next;
+ while (CurrentRH) {
+ if (--CurrentRH->rh_ttl == 0) { // This guy timed out.
+ PrevRH->rh_next = CurrentRH->rh_next; // Take him out.
+ CurrentRH->rh_next = TempList; // And save him for later.
+ TempList = CurrentRH;
+ IPSInfo.ipsi_reasmfails++;
+ } else
+ PrevRH = CurrentRH;
+
+ CurrentRH = PrevRH->rh_next;
+ }
+
+ // We've run the list. If we need to free anything, do it now. This may
+ // include sending an ICMP message.
+ CTEFreeLock(&NTE->nte_lock, NTEHandle);
+ while (TempList) {
+ CurrentRH = TempList;
+ TempList = CurrentRH->rh_next;
+ // If this wasn't sent to a bcast address and we already have the first fragment,
+ // send a time exceeded message.
+ if (CurrentRH->rh_headersize != 0)
+ SendICMPErr(NTE->nte_addr, (IPHeader *)CurrentRH->rh_header, ICMP_TIME_EXCEED,
+ TTL_IN_REASSEM, 0);
+ FreeRH(CurrentRH);
+ }
+
+ CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, NULL);
+ } else
+ CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, NTE);
+
+}
+
+//* IPpSetNTEAddr - Set the IP address of an NTE.
+//
+// Called by the DHCP client to set or delete the IP address of an NTE. We
+// make sure he's specifiying a valid NTE, then mark it up or down as needed,
+// notify the upper layers of the change if necessary, and then muck with
+// the routing tables.
+//
+// Input: Context - Context of NTE to alter.
+// Addr - IP address to set.
+// Mask - Subnet mask for Addr.
+//
+// Returns: TRUE if we changed the address, FALSE otherwise.
+//
+IP_STATUS
+IPpSetNTEAddr(NetTableEntry *NTE, IPAddr Addr, IPMask Mask,
+ CTELockHandle *RouteTableHandle, SetAddrControl *ControlBlock, SetAddrRtn Rtn)
+{
+ Interface *IF;
+ uint (*CallFunc)(struct RouteTableEntry *, void *, void *);
+
+ IF = NTE->nte_if;
+ DHCPActivityCount++;
+
+ if (IP_ADDR_EQUAL(Addr, NULL_IP_ADDR)) {
+ // We're deleting an address.
+ if (NTE->nte_flags & NTE_VALID) {
+ // The address is currently valid. Fix that.
+
+ NTE->nte_flags &= ~NTE_VALID;
+
+ //
+ // If the old address is in the ATCache, flush it out.
+ //
+ FlushATCache(NTE->nte_addr);
+
+ if (--(IF->if_ntecount) == 0) {
+ // This is the last one, so we'll need to delete relevant
+ // routes.
+ CallFunc = DeleteRTEOnIF;
+ } else
+ CallFunc = InvalidateRCEOnIF;
+
+ CTEFreeLock(&RouteTableLock, *RouteTableHandle);
+
+ StopIGMPForNTE(NTE);
+
+ // Now call the upper layers, and tell them that address is
+ // gone. We really need to do something about locking here.
+#ifdef _PNP_POWER
+
+ NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext,
+ NTE->nte_context, &NTE->nte_addrhandle, NULL, FALSE);
+
+#else // _PNP_POWER
+
+ NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL,
+ NTE->nte_context, NULL, NULL, FALSE);
+
+#endif // _PNP_POWER
+
+ // Call RTWalk to take the appropriate action on the RTEs.
+ RTWalk(CallFunc, IF, NULL);
+
+ // Delete the route to the address itself.
+ DeleteRoute(NTE->nte_addr, HOST_MASK, IPADDR_LOCAL,
+ LoopNTE->nte_if);
+
+ // Tell the lower interface this address is gone.
+ (*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_LOCAL, NTE->nte_addr,
+ NULL_IP_ADDR);
+
+ CTEGetLock(&RouteTableLock, RouteTableHandle);
+ }
+
+ DHCPActivityCount--;
+ CTEFreeLock(&RouteTableLock, *RouteTableHandle);
+ return IP_SUCCESS;
+ } else {
+ uint Status;
+
+ // We're not deleting, we're setting the address.
+ if (!(NTE->nte_flags & NTE_VALID)) {
+ uint index;
+
+ // The address is invalid. Save the info, mark him as valid,
+ // and add the routes.
+ NTE->nte_addr = Addr;
+ NTE->nte_mask = Mask;
+ NTE->nte_flags |= NTE_VALID;
+ IF->if_ntecount++;
+ index = IF->if_index;
+
+ //
+ // If the new address is in the ATCache, flush it out, otherwise
+ // TdiOpenAddress may fail.
+ //
+ FlushATCache(Addr);
+
+ CTEFreeLock(&RouteTableLock, *RouteTableHandle);
+
+ if (AddNTERoutes(NTE))
+ Status = TRUE;
+ else
+ Status = FALSE;
+
+ // Need to tell the lower layer about it.
+ if (Status) {
+ Interface *IF = NTE->nte_if;
+
+ ControlBlock->sac_rtn = Rtn;
+ Status = (*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_LOCAL,
+ Addr, Mask, ControlBlock );
+ }
+
+ if (Status == FALSE) {
+ // Couldn't add the routes. Recurively mark this NTE as down.
+ IPSetNTEAddr(NTE->nte_context, NULL_IP_ADDR, 0, NULL, NULL);
+ } else {
+ InitIGMPForNTE(NTE);
+
+ // Now call the upper layers, and tell them that address is
+ // is here. We really need to do something about locking here.
+#ifdef _PNP_POWER
+
+#ifdef SECFLTR
+ NotifyAddrChange(NTE->nte_addr, NTE->nte_mask,
+ NTE->nte_pnpcontext, NTE->nte_context, &NTE->nte_addrhandle,
+ &(IF->if_configname), TRUE);
+
+#else // SECFLTR
+
+ NotifyAddrChange(NTE->nte_addr, NTE->nte_mask,
+ NTE->nte_pnpcontext, NTE->nte_context, &NTE->nte_addrhandle,
+ NULL, TRUE);
+#endif // SECFLTR
+
+#else // _PNP_POWER
+
+#ifdef SECFLTR
+
+ NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL,
+ NTE->nte_context, NULL, &(IF->if_configname), TRUE);
+
+#else // SECFLTR
+
+ NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL,
+ NTE->nte_context, NULL, NULL, TRUE);
+
+#endif // SECFLTR
+#endif // _PNP_POWER
+
+#ifdef NT
+ if (!IP_ADDR_EQUAL(Addr, NULL_IP_ADDR)) {
+ SetPersistentRoutesForNTE(
+ net_long(Addr),
+ net_long(Mask),
+ index
+ );
+ }
+#endif // NT
+
+ if ( (Status != IP_PENDING) && (Rtn != NULL) ) {
+ (*Rtn)(ControlBlock, IP_SUCCESS);
+ }
+ }
+
+ CTEGetLock(&RouteTableLock, RouteTableHandle);
+ NTE->nte_rtrdisccount = MAX_SOLICITATION_DELAY;
+ NTE->nte_rtrdiscstate = NTE_RTRDISC_DELAYING;
+ } else
+ Status = FALSE;
+
+ DHCPActivityCount--;
+ CTEFreeLock(&RouteTableLock, *RouteTableHandle);
+ if (Status) {
+ return IP_PENDING;
+ } else {
+ return IP_GENERAL_FAILURE;
+ }
+ }
+}
+
+//* IPSetNTEAddr - Set the IP address of an NTE.
+//
+// Wrapper routine for IPpSetNTEAddr
+//
+// Input: Context - Context of NTE to alter.
+// Addr - IP address to set.
+// Mask - Subnet mask for Addr.
+//
+// Returns: TRUE if we changed the address, FALSE otherwise.
+//
+uint
+IPSetNTEAddr(ushort Context, IPAddr Addr, IPMask Mask, SetAddrControl *ControlBlock, SetAddrRtn Rtn)
+{
+ CTELockHandle Handle;
+ uint Status;
+ NetTableEntry *NTE;
+
+
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next)
+ if (NTE->nte_context == Context)
+ break;
+
+ if (NTE == NULL || NTE == LoopNTE) {
+ // Can't alter the loopback NTE, or one we didn't find.
+ CTEFreeLock(&RouteTableLock, Handle);
+ return IP_GENERAL_FAILURE;
+ }
+
+ Status = IPpSetNTEAddr(NTE, Addr, Mask, &Handle, ControlBlock, Rtn);
+
+ return(Status);
+}
+
+
+#pragma BEGIN_INIT
+
+extern NetTableEntry *InitLoopback(IPConfigInfo *);
+
+//** InitTimestamp - Intialize the timestamp for outgoing packets.
+//
+// Called at initialization time to setup our first timestamp. The timestamp we use
+// is the in ms since midnite GMT at which the system started.
+//
+// Input: Nothing.
+//
+// Returns: Nothing.
+//
+void
+InitTimestamp()
+{
+ ulong GMTDelta; // Delta in ms from GMT.
+ ulong Now; // Milliseconds since midnight.
+
+ TimeStamp = 0;
+
+ if ((GMTDelta = GetGMTDelta()) == 0xffffffff) { // Had some sort of error.
+ TSFlag = 0x80000000;
+ return;
+ }
+
+ if ((Now = GetTime()) > (24L*3600L*1000L)) { // Couldn't get time since midnight.
+ TSFlag = net_long(0x80000000);
+ return;
+ }
+
+ TimeStamp = Now + GMTDelta - CTESystemUpTime();
+ TSFlag = 0;
+
+}
+#pragma END_INIT
+
+#ifndef CHICAGO
+#pragma BEGIN_INIT
+#else
+#pragma code_seg("_LTEXT", "LCODE")
+#endif
+
+//** InitNTE - Initialize an NTE.
+//
+// This routine is called during initialization to initialize an NTE. We
+// allocate memory, NDIS resources, etc.
+//
+//
+// Entry: NTE - Pointer to NTE to be initalized.
+//
+// Returns: 0 if initialization failed, non-zero if it succeeds.
+//
+int
+InitNTE(NetTableEntry *NTE)
+{
+ Interface *IF;
+ NetTableEntry *PrevNTE;
+
+ NTE->nte_ralist = NULL;
+ NTE->nte_echolist = NULL;
+
+ //
+ // Taken together, the context and instance numbers uniquely identify
+ // a network entry, even across boots of the system. The instance number
+ // will have to become dynamic if contexts are ever reused.
+ //
+ NTE->nte_context = NextNTEContext++;
+ NTE->nte_rtrlist = NULL;
+ NTE->nte_instance = GetUnique32BitValue();
+
+ // Now link him on the IF chain, and bump the count.
+ IF = NTE->nte_if;
+ PrevNTE = STRUCT_OF(NetTableEntry, &IF->if_nte, nte_ifnext);
+ while (PrevNTE->nte_ifnext != NULL)
+ PrevNTE = PrevNTE->nte_ifnext;
+
+ PrevNTE->nte_ifnext = NTE;
+ NTE->nte_ifnext = NULL;
+
+ if (NTE->nte_flags & NTE_VALID) {
+ IF->if_ntecount++;
+ }
+
+ CTEInitTimer(&NTE->nte_timer);
+ CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, (void *)NULL);
+ return TRUE;
+}
+
+//** InitInterface - Initialize with an interface.
+//
+// Called when we need to initialize with an interface. We set the appropriate NTE
+// info, then register our local address and any appropriate broadcast addresses
+// with the interface. We assume the NTE being initialized already has an interface
+// pointer set up for it. We also allocate at least one TD buffer for use on the interface.
+//
+// Input: NTE - NTE to initialize with the interface.
+//
+// Returns: TRUE is we succeeded, FALSE if we fail.
+//
+int
+InitInterface(NetTableEntry *NTE)
+{
+ IPMask netmask = IPNetMask(NTE->nte_addr);
+ uchar *TDBuffer; // Pointer to tdbuffer
+ PNDIS_PACKET Packet;
+ NDIS_HANDLE TDbpool; // Handle for TD buffer pool.
+ NDIS_HANDLE TDppool;
+ PNDIS_BUFFER TDBufDesc; // Buffer descriptor for TDBuffer.
+ NDIS_STATUS Status;
+ Interface *IF; // Interface for this NTE.
+ CTELockHandle Handle;
+
+
+ IF = NTE->nte_if;
+
+ CTEAssert(NTE->nte_mss > sizeof(IPHeader));
+ CTEAssert(IF->if_mtu > 0);
+
+ NTE->nte_mss = MIN((NTE->nte_mss - sizeof(IPHeader)), IF->if_mtu);
+
+ CTERefillMem();
+
+ // Allocate resources needed for xfer data calls. The TD buffer has to be as large
+ // as any frame that can be received, even though our MSS may be smaller, because we
+ // can't control what might be sent at us.
+ TDBuffer = CTEAllocMem(IF->if_mtu);
+ if (TDBuffer == (uchar *)NULL)
+ return FALSE;
+
+ NdisAllocatePacketPool(&Status, &TDppool, 1, sizeof(TDContext));
+
+ if (Status != NDIS_STATUS_SUCCESS) {
+ CTEFreeMem(TDBuffer);
+ return FALSE;
+ }
+
+ NdisAllocatePacket(&Status, &Packet, TDppool);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ NdisFreePacketPool(TDppool);
+ CTEFreeMem(TDBuffer);
+ return FALSE;
+ }
+
+ CTEMemSet(Packet->ProtocolReserved, 0, sizeof(TDContext));
+
+ NdisAllocateBufferPool(&Status, &TDbpool, 1);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ NdisFreePacketPool(TDppool);
+ CTEFreeMem(TDBuffer);
+ return FALSE;
+ }
+
+ NdisAllocateBuffer(&Status,&TDBufDesc, TDbpool, TDBuffer,
+ (IF->if_mtu + sizeof(IPHeader)));
+ if (Status != NDIS_STATUS_SUCCESS) {
+ NdisFreeBufferPool(TDbpool);
+ NdisFreePacketPool(TDppool);
+ CTEFreeMem(TDBuffer);
+ return FALSE;
+ }
+
+ NdisChainBufferAtFront(Packet, TDBufDesc);
+
+ ((TDContext *)Packet->ProtocolReserved)->tdc_buffer = TDBuffer;
+
+
+ if (NTE->nte_flags & NTE_VALID) {
+
+ // Add our local IP address.
+ if (!(*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_LOCAL,
+ NTE->nte_addr, NTE->nte_mask, NULL)) {
+ NdisFreeBufferPool(TDbpool);
+ NdisFreePacketPool(TDppool);
+ CTEFreeMem(TDBuffer);
+ return FALSE; // Couldn't add local address.
+ }
+ }
+
+ // Set up the broadcast addresses for this interface, iff we're the
+ // 'primary' NTE on the interface.
+ if (NTE->nte_flags & NTE_PRIMARY) {
+
+ if (!(*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_BCAST,
+ NTE->nte_if->if_bcast, 0, NULL)) {
+ NdisFreeBufferPool(TDbpool);
+ NdisFreePacketPool(TDppool);
+ CTEFreeMem(TDBuffer);
+ return FALSE; // Couldn't add broadcast address.
+ }
+ }
+
+ if (IF->if_llipflags & LIP_COPY_FLAG) {
+ NTE->nte_flags |= NTE_COPY;
+ }
+
+ CTEGetLock(&IF->if_lock, &Handle);
+ ((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link = IF->if_tdpacket;
+ IF->if_tdpacket = Packet;
+ CTEFreeLock(&IF->if_lock, Handle);
+
+ return TRUE;
+}
+
+#ifndef _PNP_POWER
+//* CleanAdaptTable - Clean up the adapter name table.
+//
+//
+void
+CleanAdaptTable()
+{
+ int i = 0;
+
+ while (AdptNameTable[i].nm_arpinfo != NULL) {
+ CTEFreeMem(AdptNameTable[i].nm_arpinfo);
+ CTEFreeString(&AdptNameTable[i].nm_name);
+ if (AdptNameTable[i].nm_driver.Buffer != NULL)
+ CTEFreeString(&AdptNameTable[i].nm_driver);
+ i++;
+ }
+}
+
+
+//* OpenAdapters - Clean up the adapter name table.
+//
+// Used at the end of initialization. We loop through and 'open' all the adapters.
+//
+// Input: Nothing.
+//
+// Returns: Nothing.
+//
+void
+OpenAdapters()
+{
+ int i = 0;
+ LLIPBindInfo *ABI;
+
+ while ((ABI = AdptNameTable[i++].nm_arpinfo) != NULL) {
+ (*(ABI->lip_open))(ABI->lip_context);
+ }
+}
+
+
+//* IPRegisterDriver - Called during init time to register a driver.
+//
+// Called during init time when we have a non-LAN (or non-ARPable) driver
+// that wants to register with us. We try to find a free slot in the table
+// to register him.
+//
+// Input: Name - Pointer to the name of the driver to be registered.
+// Ptr - Pointer to driver's registration function.
+//
+// Returns: TRUE if we succeeded, FALSE if we fail.
+//
+uint
+IPRegisterDriver(PNDIS_STRING Name, LLIPRegRtn Ptr)
+{
+ uint i;
+
+ CTERefillMem();
+
+ // First, find a slot for him.
+ for (i = 0; i < MaxIPNets; i++) {
+ if (DriverNameTable[i].drm_driver.Buffer == NULL) {
+ // Found a slot. Try and allocate and copy a string for him.
+ if (!CTEAllocateString(&DriverNameTable[i].drm_driver,
+ CTELengthString(Name)))
+ return FALSE;
+ // Got the space. Copy the string and the pointer.
+ CTECopyString(&DriverNameTable[i].drm_driver, Name);
+ DriverNameTable[i].drm_regptr = Ptr;
+ NumRegDrivers++;
+ return TRUE;
+ }
+ }
+
+
+}
+
+
+#ifdef NT
+
+//* GetLLRegPtr - Called during init time to get a lower driver's registration
+// routine.
+//
+// Called during init time to locate the registration function of a
+// non-LAN (or non-ARPable) driver.
+//
+// Input: Name - Pointer to the name of the driver to be registered.
+//
+// Returns: A pointer to the driver's registration routine or NULL on failure.
+//
+LLIPRegRtn
+GetLLRegPtr(PNDIS_STRING Name)
+{
+ NTSTATUS status;
+ PFILE_OBJECT fileObject;
+ PDEVICE_OBJECT deviceObject;
+ LLIPIF_REGISTRATION_DATA registrationData;
+ IO_STATUS_BLOCK ioStatusBlock;
+ PIRP irp;
+ KEVENT ioctlEvent;
+extern POBJECT_TYPE *IoDeviceObjectType;
+
+
+ registrationData.RegistrationFunction = NULL;
+
+ KeInitializeEvent(&ioctlEvent, SynchronizationEvent, FALSE);
+
+ status = IoGetDeviceObjectPointer(
+ Name,
+ SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
+ &fileObject,
+ &deviceObject
+ );
+
+ if (status != STATUS_SUCCESS) {
+ CTEPrint("IP failed to open the lower layer driver\n");
+ return(NULL);
+ }
+
+ //
+ // Reference the device object.
+ //
+ ObReferenceObject(deviceObject);
+
+ //
+ // IoGetDeviceObjectPointer put a reference on the file object.
+ //
+ ObDereferenceObject(fileObject);
+
+ irp = IoBuildDeviceIoControlRequest(
+ IOCTL_LLIPIF_REGISTER,
+ deviceObject,
+ NULL, // input Buffer
+ 0, // input buffer length
+ &registrationData,
+ sizeof(LLIPIF_REGISTRATION_DATA),
+ FALSE, // not an InternalDeviceControl
+ &ioctlEvent,
+ &ioStatusBlock
+ );
+
+ if (irp == NULL) {
+ ObDereferenceObject(deviceObject);
+ return(NULL);
+ }
+
+ status = IoCallDriver(deviceObject, irp);
+
+ if (status == STATUS_PENDING) {
+ status = KeWaitForSingleObject(
+ &ioctlEvent,
+ Executive,
+ KernelMode,
+ FALSE, // not alertable
+ NULL // no timeout
+ );
+
+ }
+
+ ObDereferenceObject(deviceObject);
+
+ if (status != STATUS_SUCCESS) {
+ return(NULL);
+ }
+
+ if (registrationData.RegistrationFunction != NULL) {
+ //
+ // Cache the driver registration for future reference.
+ //
+ IPRegisterDriver(Name, registrationData.RegistrationFunction);
+ }
+
+ return(registrationData.RegistrationFunction);
+
+} // GetLLRegPtr
+
+#endif // NT
+#endif // _PNP_POWER
+
+
+#ifndef _PNP_POWER
+
+//* FindRegPtr - Find a driver's registration routine.
+//
+// Called during init time when we have a non-LAN (or non-ARPable) driver to
+// register with. We take in the driver name, and try to find a registration
+// pointer for the driver.
+//
+// Input: Name - Pointer to the name of the driver to be found.
+//
+// Returns: Pointer to the registration routine, or NULL if there is none.
+//
+LLIPRegRtn
+FindRegPtr(PNDIS_STRING Name)
+{
+ uint i;
+
+ for (i = 0; i < NumRegDrivers; i++) {
+ if (CTEEqualString(&(DriverNameTable[i].drm_driver), Name))
+ return (LLIPRegRtn)(DriverNameTable[i].drm_regptr);
+ }
+
+#ifdef NT
+ //
+ // For NT, we open the lower driver and issue an IOCTL to get a pointer to
+ // its registration function. We then cache this in the table for future
+ // reference.
+ //
+ return(GetLLRegPtr(Name));
+#else
+ return NULL;
+#endif // NT
+}
+
+#endif // _PNP_POWER
+
+#ifdef CHICAGO
+#pragma BEGIN_INIT
+#endif
+
+//* FreeNets - Free nets we have allocated.
+//
+// Called during init time if initialization fails. We walk down our list
+// of nets, and free them.
+//
+// Input: Nothing.
+//
+// Returns: Nothing.
+//
+void
+FreeNets(void)
+{
+ NetTableEntry *NTE;
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next)
+ CTEFreeMem(NTE);
+}
+
+#ifdef CHICAGO
+#pragma END_INIT
+#pragma code_seg("_LTEXT", "LCODE")
+#endif
+
+
+#ifdef _PNP_POWER
+
+extern uint GetGeneralIFConfig(IFGeneralConfig *GConfigInfo, NDIS_HANDLE Handle);
+extern IFAddrList *GetIFAddrList(uint *NumAddr, NDIS_HANDLE Handle);
+
+
+#ifdef CHICAGO
+
+extern void RequestDHCPAddr(ushort context);
+
+#define MAX_NOTIFY_CLIENTS 8
+
+typedef void (*AddrNotifyRtn)(IPAddr Addr, IPMask Mask, void *Context,
+ ushort IPContext, uint Added);
+
+AddrNotifyRtn AddrNotifyTable[MAX_NOTIFY_CLIENTS];
+
+typedef void (*InterfaceNotifyRtn)(ushort Context, uint Added);
+
+InterfaceNotifyRtn InterfaceNotifyTable[MAX_NOTIFY_CLIENTS];
+
+//* RegisterAddrNotify - Register an address notify routine.
+//
+// A routine called to register an address notify routine.
+//
+// Input: Rtn - Routine to register.
+// Register - True to register, False to deregister.
+//
+// Returns: TRUE if we succeed, FALSE if we don't/
+//
+uint
+RegisterAddrNotify(AddrNotifyRtn Rtn, uint Register)
+{
+ uint i;
+ AddrNotifyRtn NewRtn, OldRtn;
+
+ if (Register) {
+ NewRtn = Rtn;
+ OldRtn = NULL;
+ } else {
+ NewRtn = NULL;
+ OldRtn = Rtn;
+ }
+
+ for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) {
+ if (AddrNotifyTable[i] == OldRtn) {
+ AddrNotifyTable[i] = NewRtn;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+//* NotifyInterfaceChange - Notify clients of a change in an interface.
+//
+// Called when we want to notify registered clients that an interface has come
+// or gone. We loop through our InterfaceNotify table, calling each one.
+//
+// Input: Context - Context for interface that has changed.
+// Added - True if the interface is coming, False if it's
+// going.
+//
+// Returns: Nothing.
+//
+void
+NotifyInterfaceChange(ushort IPContext, uint Added)
+{
+ uint i;
+
+ for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) {
+ if (InterfaceNotifyTable[i] != NULL)
+ (*(InterfaceNotifyTable[i]))(IPContext, Added);
+ }
+}
+
+//* RegisterInterfaceNotify - Register an interface notify routine.
+//
+// A routine called to register an interface notify routine.
+//
+// Input: Rtn - Routine to register.
+// Register - True to register, False to deregister.
+//
+// Returns: TRUE if we succeed, FALSE if we don't/
+//
+uint
+RegisterInterfaceNotify(InterfaceNotifyRtn Rtn, uint Register)
+{
+ uint i;
+ InterfaceNotifyRtn NewRtn, OldRtn;
+
+ if (Register) {
+ NewRtn = Rtn;
+ OldRtn = NULL;
+ } else {
+ NewRtn = NULL;
+ OldRtn = Rtn;
+ }
+
+ for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) {
+ if (InterfaceNotifyTable[i] == OldRtn) {
+ InterfaceNotifyTable[i] = NewRtn;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+//* NotifyAddrChange - Notify clients of a change in addresses.
+//
+// Called when we want to notify registered clients that an address has come
+// or gone. We loop through our AddrNotify table, calling each one.
+//
+// Input: Addr - Addr that has changed.
+// Mask - Mask that has changed.
+// Context - PNP context for address
+// IPContext - NTE context for NTE
+// Handle - Pointer to where to get/set address registration
+// handle
+// ConfigName - Registry name to use to retrieve config info.
+// Added - True if the addr is coming, False if it's going.
+//
+// Returns: Nothing.
+//
+void
+NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext,
+ PVOID *Handle, PNDIS_STRING ConfigName, uint Added)
+{
+ uint i;
+
+ for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) {
+ if (AddrNotifyTable[i] != NULL)
+ (*(AddrNotifyTable[i]))(Addr, Mask, Context, IPContext, Added);
+ }
+}
+
+
+#else // CHICAGO
+
+//* NotifyAddrChange - Notify clients of a change in addresses.
+//
+// Called when we want to notify registered clients that an address has come
+// or gone. We call TDI to perform this function.
+//
+// Input: Addr - Addr that has changed.
+// Mask - Mask that has changed.
+// Context - PNP context for address
+// IPContext - NTE context for NTE
+// Handle - Pointer to where to get/set address registration
+// handle
+// ConfigName - Registry name to use to retrieve config info.
+// Added - True if the addr is coming, False if it's going.
+//
+// Returns: Nothing.
+//
+void
+NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext,
+ PVOID *Handle, PNDIS_STRING ConfigName, uint Added)
+{
+ uchar Address[sizeof(TA_ADDRESS) + sizeof(TDI_ADDRESS_IP)];
+ PTA_ADDRESS AddressPtr;
+ PTDI_ADDRESS_IP IPAddressPtr;
+ NTSTATUS Status;
+
+#ifdef SECFLTR
+
+ IP_STATUS StatusType;
+ NDIS_HANDLE ConfigHandle = NULL;
+ int i;
+ ULStatusProc StatProc;
+
+#endif // SECFLTR
+
+
+ AddressPtr = (PTA_ADDRESS)Address;
+
+ AddressPtr->AddressLength = sizeof(TDI_ADDRESS_IP);
+ AddressPtr->AddressType = TDI_ADDRESS_TYPE_IP;
+
+ IPAddressPtr = (PTDI_ADDRESS_IP)AddressPtr->Address;
+
+ CTEMemSet(IPAddressPtr, 0, sizeof(TDI_ADDRESS_IP));
+
+ IPAddressPtr->in_addr = Addr;
+
+#ifdef SECFLTR
+
+ //
+ // Call the status entrypoint of the transports so they can
+ // adjust their security filters.
+ //
+ if (Added) {
+ StatusType = IP_ADDR_ADDED;
+
+ //
+ // Open a configuration key
+ //
+ if (!OpenIFConfig(ConfigName, &ConfigHandle)) {
+ //
+ // Not much we can do. The transports will have
+ // to handle this.
+ //
+ CTEAssert(ConfigHandle == NULL);
+ }
+ }
+ else {
+ StatusType = IP_ADDR_DELETED;
+ }
+
+ for ( i = 0; i < NextPI; i++) {
+ StatProc = IPProtInfo[i].pi_status;
+ if (StatProc != NULL)
+ (*StatProc)(IP_HW_STATUS, StatusType, Addr, NULL_IP_ADDR,
+ NULL_IP_ADDR, 0, ConfigHandle );
+ }
+
+ if (ConfigHandle != NULL) {
+ CloseIFConfig(ConfigHandle);
+ }
+
+#endif // SECFLTR
+
+ //
+ // Notify any interested parties via TDI. The transports all register
+ // for this notification as well.
+ //
+ if (Added) {
+ Status = TdiRegisterNetAddress(AddressPtr, Handle);
+ if (Status != STATUS_SUCCESS) {
+ *Handle = NULL;
+ }
+ } else {
+ if (*Handle != NULL) {
+ TdiDeregisterNetAddress(*Handle);
+ *Handle = NULL;
+ }
+ }
+
+}
+
+#endif // CHICAGO
+
+
+//* IPAddNTE - Add a new NTE to an interface
+//
+// Called to create a new network entry on an interface.
+//
+// Input: GConfigInfo - Configuration information for the interface
+// PNPContext - The PNP context value associated with the interface
+// RegRtn - Routine to call to register with ARP.
+// BindInfo - Pointer to NDIS bind information.
+// IF - The interface on which to create the NTE.
+// NewAddr - The address of the new NTE.
+// NewMask - The subnet mask for the new NTE.
+// IsPrimary - TRUE if this NTE is the primary one on the interface
+// IsDynamic - TRUE if this NTE is being created on an
+// existing interface instead of a new one.
+//
+// Returns: A pointer to the new NTE if the operation succeeds.
+// NULL if the operation fails.
+//
+NetTableEntry *
+IPAddNTE(IFGeneralConfig *GConfigInfo, void * PNPContext, LLIPRegRtn RegRtn,
+ LLIPBindInfo *BindInfo, Interface *IF, IPAddr NewAddr, IPMask NewMask,
+ uint IsPrimary, uint IsDynamic)
+{
+ NetTableEntry *NTE, *PrevNTE;
+ CTELockHandle Handle;
+
+
+ // If the address is invalid we're done. Fail the request.
+ if (CLASSD_ADDR(NewAddr) || CLASSE_ADDR(NewAddr)) {
+ return NULL;
+ }
+
+ // See if we have an inactive NTE on the NetTableList. If we do, we'll
+ // just recycle that. We will pull him out of the list. This is not
+ // strictly MP safe, since other people could be walking the list while
+ // we're doing this without holding a lock, but it should be harmless.
+ // The removed NTE is marked as invalid, and his next pointer will
+ // be nulled, so anyone walking the list might hit the end too soon,
+ // but that's all. The memory is never freed, and the next pointer is
+ // never pointed at freed memory.
+
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ PrevNTE = STRUCT_OF(NetTableEntry, &NetTableList, nte_next);
+ for (NTE = NetTableList; NTE != NULL; PrevNTE = NTE, NTE = NTE->nte_next)
+ if (!(NTE->nte_flags & NTE_ACTIVE)) {
+ PrevNTE->nte_next = NTE->nte_next;
+ NTE->nte_next = NULL;
+ NumNTE--;
+ break;
+ }
+
+ CTEFreeLock(&RouteTableLock, Handle);
+
+ // See if we got one.
+ if (NTE == NULL) {
+ // Didn't get one. Try to allocate one.
+ NTE = CTEAllocMem(sizeof(NetTableEntry));
+ if (NTE == NULL)
+ return NULL;
+ CTEMemSet(NTE, 0, sizeof(NetTableEntry));
+ }
+
+ // Initialize the address and mask stuff
+ NTE->nte_addr = NewAddr;
+ NTE->nte_mask = NewMask;
+ NTE->nte_mss = MAX(GConfigInfo->igc_mtu, 68);
+ NTE->nte_rtrdiscaddr = GConfigInfo->igc_rtrdiscaddr;
+ NTE->nte_rtrdiscstate = NTE_RTRDISC_UNINIT;
+ NTE->nte_rtrdisccount = 0;
+ NTE->nte_rtrdiscovery = (uchar)GConfigInfo->igc_rtrdiscovery;
+ NTE->nte_rtrlist = NULL;
+ NTE->nte_pnpcontext = PNPContext;
+ NTE->nte_if = IF;
+ NTE->nte_flags = NTE_ACTIVE;
+
+ //
+ // If the new address is in the ATCache, flush it out, otherwise
+ // TdiOpenAddress may fail.
+ //
+ FlushATCache(NewAddr);
+
+ if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
+ NTE->nte_flags |= NTE_VALID;
+ NTE->nte_rtrdisccount = MAX_SOLICITATION_DELAY;
+ NTE->nte_rtrdiscstate = NTE_RTRDISC_DELAYING;
+ }
+
+ if (IsDynamic) {
+ NTE->nte_flags |= NTE_DYNAMIC;
+ }
+
+ NTE->nte_ralist = NULL;
+ NTE->nte_echolist = NULL;
+ NTE->nte_icmpseq = 0;
+ NTE->nte_igmplist = NULL;
+ CTEInitLock(&NTE->nte_lock);
+ CTEInitTimer(&NTE->nte_timer);
+
+ if (IsPrimary) {
+ //
+ // This is the first (primary) NTE on the interface.
+ //
+ NTE->nte_flags |= NTE_PRIMARY;
+
+ // Pass our information to the underlying code.
+ if (!(*RegRtn)(&(IF->if_configname), NTE, IPRcv, IPSendComplete,
+ IPStatus, IPTDComplete, IPRcvComplete, BindInfo,
+ IF->if_index)) {
+
+ // Couldn't register.
+ goto failure;
+ }
+ }
+
+ //
+ // Link the NTE onto the global NTE list.
+ //
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ NTE->nte_next = NetTableList;
+ NetTableList = NTE;
+ NumNTE++;
+
+ CTEFreeLock(&RouteTableLock, Handle);
+
+ if (!InitInterface(NTE)) {
+ goto failure;
+ }
+
+ if (!InitNTE(NTE)) {
+ goto failure;
+ }
+
+ if (!InitNTERouting(NTE, GConfigInfo->igc_numgws, GConfigInfo->igc_gw)) {
+ // Couldn't add the routes for this NTE. Mark him as not valid.
+ // Probably should log an event here.
+ if (NTE->nte_flags & NTE_VALID) {
+ NTE->nte_flags &= ~NTE_VALID;
+ NTE->nte_if->if_ntecount--;
+ }
+ }
+
+#ifdef NT
+
+ if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
+ SetPersistentRoutesForNTE(
+ net_long(NTE->nte_addr),
+ net_long(NTE->nte_mask),
+ NTE->nte_if->if_index
+ );
+ }
+
+#endif // NT
+
+ return(NTE);
+
+failure:
+
+ //
+ // BUGBUG - what should we do with the NTE here????
+ //
+
+ return(NULL);
+}
+
+
+//* IPAddDynamicNTE - Add a new "dynamic" NTE to an existing interface
+//
+// Called to dynamically create a new network entry on an existing interface.
+// This entry was not configured when the interaface was originally created
+// and will not persist if the interface is unbound.
+//
+// Input: InterfaceContext - The context value which identifies the
+// interface on which to create the NTE.
+// NewAddr - The address of the new NTE.
+// NewMask - The subnet mask for the new NTE.
+//
+// Output: NTEContext - The context identifying the new NTE.
+// NTEInstance - The instance number which (reasonably) uniquely
+// identifies this NTE in time.
+//
+// Returns: Nonzero if the operation succeeded. Zero if it failed.
+//
+uint
+IPAddDynamicNTE(ushort InterfaceContext, IPAddr NewAddr, IPMask NewMask,
+ ushort *NTEContext, ulong *NTEInstance)
+{
+ IFGeneralConfig GConfigInfo; // General config info structure.
+ NDIS_HANDLE Handle; // Configuration handle.
+ NetTableEntry *NTE;
+ Interface *IF;
+ ushort MTU;
+ uint Flags = 0;
+
+
+#ifdef NT
+ PAGED_CODE();
+#endif
+
+ for (IF = IFList; IF != NULL; IF = IF->if_next) {
+ if (IF->if_index == InterfaceContext) {
+ break;
+ }
+ }
+
+ //* Try to get the network configuration information.
+ if (!OpenIFConfig(&(IF->if_configname), &Handle))
+ return FALSE;
+
+ // Try to get our general config information.
+ if (!GetGeneralIFConfig(&GConfigInfo, Handle)) {
+ goto failure;
+ }
+
+ NTE = IPAddNTE(
+ &GConfigInfo,
+ NULL, // PNPContext - BUGBUG needed?
+ NULL, // RegRtn - not needed if not primary
+ NULL, // BindInfo - not needed if not primary
+ IF,
+ NewAddr,
+ NewMask,
+ FALSE, // not primary
+ TRUE // is dynamic
+ );
+
+ if (NTE == NULL) {
+ goto failure;
+ }
+
+ CloseIFConfig(Handle);
+
+ //
+ // Notify upper layers of the new address.
+ //
+#ifdef SECFLTR
+
+ NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext,
+ NTE->nte_context, &NTE->nte_addrhandle, &(IF->if_configname), TRUE);
+
+#else // SECFLTR
+
+ NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext,
+ NTE->nte_context, &NTE->nte_addrhandle, NULL, TRUE);
+
+#endif // SECFLTR
+
+ if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
+ InitIGMPForNTE(NTE);
+ }
+ else {
+#ifdef CHICAGO
+ // Call DHCP to get an address for this guy.
+
+ //
+ // BUGBUG (mikemas 8/28/96)
+ // we may not always want to do this!
+ //
+ RequestDHCPAddr(NTE->nte_context);
+#endif
+ }
+
+ //
+ // Fill in the out parameter value.
+ //
+ *NTEContext = NTE->nte_context;
+ *NTEInstance = NTE->nte_instance;
+
+ return(TRUE);
+
+failure:
+
+ CloseIFConfig(Handle);
+
+ return(IP_GENERAL_FAILURE);
+}
+
+
+//* IPAddInterface - Add an interface.
+//
+// Called when someone has an interface they want us to add. We read our
+// configuration information, and see if we have it listed. If we do,
+// we'll try to allocate memory for the structures we need. Then we'll
+// call back to the guy who called us to get things going. Finally, we'll
+// see if we have an address that needs to be DHCP'ed.
+//
+// Input: ConfigName - Name of config info we're to read.
+// Context - Context to pass to i/f on calls.
+// RegRtn - Routine to call to register.
+// BindInfo - Pointer to bind information.
+//
+// Returns: Status of attempt to add the interface.
+//
+IP_STATUS
+IPAddInterface(PNDIS_STRING ConfigName, void *PNPContext, void *Context,
+ LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo)
+{
+ IFGeneralConfig GConfigInfo; // General config info structure.
+ IFAddrList *AddrList; // List of addresses for this I/F.
+ uint NumAddr; // Number of IP addresses on this
+ // interface
+ NetTableEntry *NTE; // Current NTE being initialized.
+ uint i; // Index variable.
+ uint IndexMask; // Mask for searching IFBitMask.
+ Interface *IF; // Interface being added.
+ NDIS_HANDLE Handle; // Configuration handle.
+ NetTableEntry *PrimaryNTE; // The primary NTE for this I/F.
+ uint IFIndex; // Index to be assigned to this I/F.
+ NetTableEntry *LastNTE; // Last NTE created.
+
+
+ CTERefillMem();
+
+ PrimaryNTE = NULL;
+ AddrList = NULL;
+ IF = NULL;
+ LastNTE = NULL;
+
+ //* First, try to get the network configuration information.
+ if (!OpenIFConfig(ConfigName, &Handle))
+ return IP_GENERAL_FAILURE; // Couldn't get IFConfig.
+
+ // Try to get our general config information.
+ if (!GetGeneralIFConfig(&GConfigInfo, Handle)) {
+ goto failure;
+ }
+
+ // We got the general config info. Now allocate an interface.
+ IF = CTEAllocMem(InterfaceSize + ConfigName->MaximumLength);
+
+ if (IF == NULL) {
+ goto failure;
+ }
+
+ CTEMemSet(IF, 0, InterfaceSize);
+ CTEInitLock(&IF->if_lock);
+
+ // Initialize the broadcast we'll use.
+ if (GConfigInfo.igc_zerobcast)
+ IF->if_bcast = IP_ZERO_BCST;
+ else
+ IF->if_bcast = IP_LOCAL_BCST;
+
+ if (RouterConfigured) {
+ RouteInterface *RtIF = (RouteInterface *)IF;
+
+
+ RtIF->ri_q.rsq_qh.fq_next = &RtIF->ri_q.rsq_qh;
+ RtIF->ri_q.rsq_qh.fq_prev = &RtIF->ri_q.rsq_qh;
+ RtIF->ri_q.rsq_running = FALSE;
+ RtIF->ri_q.rsq_pending = 0;
+ RtIF->ri_q.rsq_maxpending = GConfigInfo.igc_maxpending;
+ RtIF->ri_q.rsq_qlength = 0;
+ CTEInitLock(&RtIF->ri_q.rsq_lock);
+ }
+
+ IF->if_xmit = BindInfo->lip_transmit;
+ IF->if_transfer = BindInfo->lip_transfer;
+ IF->if_close = BindInfo->lip_close;
+ IF->if_invalidate = BindInfo->lip_invalidate;
+ IF->if_lcontext = BindInfo->lip_context;
+ IF->if_addaddr = BindInfo->lip_addaddr;
+ IF->if_deladdr = BindInfo->lip_deladdr;
+ IF->if_qinfo = BindInfo->lip_qinfo;
+ IF->if_setinfo = BindInfo->lip_setinfo;
+ IF->if_getelist = BindInfo->lip_getelist;
+ IF->if_tdpacket = NULL;
+ CTEAssert(BindInfo->lip_mss > sizeof(IPHeader));
+ IF->if_mtu = BindInfo->lip_mss - sizeof(IPHeader);
+ IF->if_speed = BindInfo->lip_speed;
+ IF->if_flags = BindInfo->lip_flags & LIP_P2P_FLAG ? IF_FLAGS_P2P : 0;
+ IF->if_addrlen = BindInfo->lip_addrlen;
+ IF->if_addr = BindInfo->lip_addr;
+ IF->if_pnpcontext = PNPContext;
+ IF->if_llipflags = BindInfo->lip_flags;
+
+ // Initialize the reference count to 1, for the open.
+ IF->if_refcount = 1;
+
+#ifdef IGMPV2
+ IF->IgmpVersion = IGMPV2;
+#else
+ IF->IgmpVersion = IGMPV1;
+#endif
+
+
+ //
+ // No need to do the following since IF structure is inited to 0 through
+ // memset above
+ //
+ // IF->IgmpVer1Timeout = 0;
+
+ //
+ // Copy the config string for use later when DHCP enables an address
+ // on this interface or when an NTE is added dynamically.
+ //
+ IF->if_configname.Buffer = (PVOID) (((uchar *)IF) + InterfaceSize);
+ IF->if_configname.Length = 0;
+ IF->if_configname.MaximumLength = ConfigName->MaximumLength;
+
+ CTECopyString(
+ &(IF->if_configname),
+ ConfigName
+ );
+
+ // Find out how many addresses we have, and get the address list.
+ AddrList = GetIFAddrList(&NumAddr, Handle);
+
+ if (AddrList == NULL) {
+ CTEFreeMem(IF);
+ goto failure;
+ }
+
+ //
+ //Link this interface onto the global interface list
+ //
+ IF->if_next = IFList;
+ IFList = IF;
+
+ if (FirstIF == NULL)
+ FirstIF = IF;
+
+ NumIF++;
+ IndexMask = 1;
+
+ for (i = 0; i < MAX_TDI_ENTITIES; i++) {
+ if ((IFBitMask[i/BITS_PER_WORD] & IndexMask) == 0) {
+ IFIndex = i+ 1;
+ IFBitMask[i/BITS_PER_WORD] |= IndexMask;
+ break;
+ }
+ if (((i+1) % BITS_PER_WORD) == 0) {
+ IndexMask = 1;
+ } else {
+ IndexMask = IndexMask << 1;
+ }
+ }
+
+ if (i == MAX_TDI_ENTITIES) {
+ // Too many interfaces bound.
+ goto failure;
+ }
+
+ IF->if_index = IFIndex;
+
+ // Now loop through, initializing each NTE as we go. We don't hold any
+ // locks while we do this, since NDIS won't reenter us here and no one
+ // else manipulates the NetTableList.
+
+ for (i = 0;i < NumAddr;i++) {
+ NetTableEntry *PrevNTE;
+ IPAddr NewAddr;
+ uint isPrimary;
+
+ if (i == 0) {
+ isPrimary = TRUE;
+ }
+ else {
+ isPrimary = FALSE;
+ }
+
+ NTE = IPAddNTE(
+ &GConfigInfo,
+ PNPContext,
+ RegRtn,
+ BindInfo,
+ IF,
+ net_long(AddrList[i].ial_addr),
+ net_long(AddrList[i].ial_mask),
+ isPrimary,
+ FALSE // not dynamic
+ );
+
+ if (NTE == NULL) {
+ goto failure;
+ }
+
+ if (isPrimary) {
+ PrimaryNTE = NTE;
+
+#ifdef NT
+
+ //
+ // Write the context of the first interface to the registry.
+ //
+ if (isPrimary) {
+ NTSTATUS writeStatus;
+ ulong context = (ulong) NTE->nte_context;
+
+ writeStatus = SetRegDWORDValue(
+ Handle,
+ L"IPInterfaceContext",
+ &context
+ );
+
+ if (!NT_SUCCESS(writeStatus)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_DHCP_INIT_FAILED,
+ 2,
+ 1,
+ &(ConfigName->Buffer),
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to write IPInterfaceContext value for adapter %ws\n"
+ " (status %lx). DHCP will be unable to configure this \n"
+ " adapter.\n",
+ ConfigName->Buffer,
+ writeStatus
+ ));
+ }
+ }
+
+#endif // NT
+
+ }
+
+ LastNTE = NTE;
+ }
+
+#ifdef NT
+
+ if (LastNTE != NULL) {
+
+ NTSTATUS writeStatus;
+ ulong context = (ulong) LastNTE->nte_context;
+
+ writeStatus = SetRegDWORDValue(
+ Handle,
+ L"IPInterfaceContextMax",
+ &context
+ );
+
+ if (!NT_SUCCESS(writeStatus)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_DHCP_INIT_FAILED,
+ 3,
+ 1,
+ &(ConfigName->Buffer),
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to write IPInterfaceContextMax value for adapter %ws\n"
+ " (status %lx). DHCP will be unable to configure this \n"
+ " adapter.\n",
+ ConfigName->Buffer,
+ writeStatus
+ ));
+ }
+ }
+
+#endif // NT
+
+ CloseIFConfig(Handle);
+
+ // We've initialized our NTEs. Now get the adapter open, and go through
+ // again, calling DHCP if we need to.
+
+ (*(BindInfo->lip_open))(BindInfo->lip_context);
+
+ if (PrimaryNTE != NULL) {
+#ifdef CHICAGO
+ NotifyInterfaceChange(PrimaryNTE->nte_context, TRUE);
+#endif
+ }
+
+ // Now walk through the NTEs we've added, and get addresses for them (or
+ // tell clients about them). This code assumes that no one else has mucked
+ // with the list while we're here.
+ for (i = 0; i < NumAddr; i++, NTE = NTE->nte_next) {
+
+//
+// BUGBUG - Doesn't this send up a notification of zero for a DHCP'd
+// address on chicago??? (mikemas, 2/5/96)
+//
+#ifdef SECFLTR
+
+ NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext,
+ NTE->nte_context, &NTE->nte_addrhandle, &(IF->if_configname), TRUE);
+
+#else // SECFLTR
+
+ NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext,
+ NTE->nte_context, &NTE->nte_addrhandle, NULL, TRUE);
+
+#endif // SECFLTR
+
+ if (IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
+ // Call DHCP to get an address for this guy.
+#ifdef CHICAGO
+ RequestDHCPAddr(NTE->nte_context);
+#endif
+ } else {
+ InitIGMPForNTE(NTE);
+ }
+ }
+
+
+ CTEFreeMem(AddrList);
+ return IP_SUCCESS;
+
+failure:
+ CloseIFConfig(Handle);
+
+ if (AddrList != NULL)
+ CTEFreeMem(AddrList);
+
+ return IP_GENERAL_FAILURE;
+}
+
+extern uint BCastMinMTU;
+
+
+//* IPDelNTE - Delete an active NTE
+//
+// Called to delete an active NTE from the system. The RouteTableLock
+// must be acquired before calling this routine. It will be freed upon
+// return.
+//
+// Input: NTE - A pointer to the network entry to delete.
+// RouteTableHandle - A pointer to the lock handle for the
+// route table lock, which the caller has
+// acquired.
+//
+// Returns: Nothing
+//
+void
+IPDelNTE(NetTableEntry *NTE, CTELockHandle *RouteTableHandle)
+{
+ Interface *IF = NTE->nte_if;
+ ReassemblyHeader *RH, *RHNext;
+ EchoControl *EC, *ECNext;
+ EchoRtn Rtn;
+ CTELockHandle Handle;
+ PNDIS_PACKET Packet;
+ PNDIS_BUFFER Buffer;
+ uchar *TDBuffer;
+
+
+ if (NTE->nte_flags & NTE_VALID) {
+ (void) IPpSetNTEAddr(NTE, NULL_IP_ADDR, NULL_IP_ADDR, RouteTableHandle, NULL, NULL);
+
+ } else {
+ CTEFreeLock(&RouteTableLock, *RouteTableHandle);
+
+ NotifyAddrChange(NULL_IP_ADDR, NULL_IP_ADDR,
+ NTE->nte_pnpcontext, NTE->nte_context,
+ &NTE->nte_addrhandle, NULL, FALSE);
+ }
+
+ CTEGetLock(&RouteTableLock, RouteTableHandle);
+
+ if (DHCPNTE == NTE)
+ DHCPNTE = NULL;
+
+ NTE->nte_flags = 0;
+
+ CTEFreeLock(&RouteTableLock, *RouteTableHandle);
+
+ CTEStopTimer(&NTE->nte_timer);
+
+ CTEGetLock(&NTE->nte_lock, &Handle);
+
+ RH = NTE->nte_ralist;
+ NTE->nte_ralist = NULL;
+ EC = NTE->nte_echolist;
+ NTE->nte_echolist = NULL;
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ // Free any reassembly resources.
+ while (RH != NULL) {
+ RHNext = RH->rh_next;
+ FreeRH(RH);
+ RH = RHNext;
+ }
+
+ // Now free any pending echo requests.
+ while (EC != NULL) {
+ ECNext= EC->ec_next;
+ Rtn = (EchoRtn)EC->ec_rtn;
+ (*Rtn)(EC, IP_ADDR_DELETED, NULL, 0, NULL);
+ EC = ECNext;
+ }
+
+ //
+ // Free the TD resource allocated for this NTE.
+ //
+ CTEGetLock(&(IF->if_lock), &Handle);
+
+ Packet = IF->if_tdpacket;
+
+ if (Packet != NULL) {
+
+ IF->if_tdpacket =
+ ((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link;
+
+ CTEFreeLock(&(IF->if_lock), Handle);
+
+ Buffer = Packet->Private.Head;
+ TDBuffer = NdisBufferVirtualAddress(Buffer);
+ NdisFreePacketPool(Packet->Private.Pool);
+
+#ifdef CHICAGO
+ NdisFreeBufferPool(Buffer->Pool);
+#endif
+ CTEFreeMem(TDBuffer);
+ }
+ else {
+ CTEFreeLock(&(IF->if_lock), Handle);
+ }
+
+ return;
+}
+
+
+//* IPDeleteDynamicNTE - Deletes a "dynamic" NTE.
+//
+// Called to delete a network entry which was dynamically created on an
+// existing interface.
+//
+// Input: NTEContext - The context value identifying the NTE to delete.
+//
+// Returns: Nonzero if the operation succeeded. Zero if it failed.
+//
+uint
+IPDeleteDynamicNTE(ushort NTEContext)
+{
+ NetTableEntry *NTE;
+ Interface *IF;
+ CTELockHandle Handle;
+
+
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
+ if ( (NTE->nte_context == NTEContext) &&
+ (NTE->nte_flags & NTE_DYNAMIC) &&
+ (NTE->nte_flags & NTE_ACTIVE)
+ )
+ {
+ CTEAssert(NTE != LoopNTE);
+ CTEAssert(!(NTE->nte_flags & NTE_PRIMARY));
+
+ IPDelNTE(NTE, &Handle);
+
+ //
+ // Route table lock was freed by IPDelNTE
+ //
+
+ return(TRUE);
+ }
+ }
+
+ CTEFreeLock(&RouteTableLock, Handle);
+
+ return(FALSE);
+}
+
+
+//* IPGetNTEInfo - Retrieve information about a network entry.
+//
+// Called to retrieve context information about a network entry.
+//
+// Input: NTEContext - The context value which identifies the NTE to query.
+//
+// Output: NTEInstance - The instance number associated with the NTE.
+// Address - The address assigned to the NTE.
+// SubnetMask - The subnet mask assigned to the NTE.
+// NTEFlags - The flag values associated with the NTE.
+//
+// Returns: Nonzero if the operation succeeded. Zero if it failed.
+//
+uint
+IPGetNTEInfo(ushort NTEContext, ulong *NTEInstance, IPAddr *Address,
+ IPMask *SubnetMask, ushort *NTEFlags)
+{
+ NetTableEntry *NTE;
+ CTELockHandle Handle;
+ uint retval = FALSE;
+
+
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
+ if ((NTE->nte_context == NTEContext) &&
+ (NTE->nte_flags & NTE_ACTIVE)
+ )
+ {
+ *NTEInstance = NTE->nte_instance;
+
+ if (NTE->nte_flags & NTE_VALID) {
+ *Address = NTE->nte_addr;
+ *SubnetMask = NTE->nte_mask;
+ }
+ else {
+ *Address = NULL_IP_ADDR;
+ *SubnetMask = NULL_IP_ADDR;
+ }
+
+ *NTEFlags = NTE->nte_flags;
+ retval = TRUE;
+ }
+ }
+
+ CTEFreeLock(&RouteTableLock, Handle);
+
+ return(retval);
+}
+
+
+//* IPDelInterface - Delete an interface.
+//
+// Called when we need to delete an interface that's gone away. We'll walk
+// the NTE list, looking for NTEs that are on the interface that's going
+// away. For each of those, we'll invalidate the NTE, delete routes on it,
+// and notify the upper layers that it's gone. When that's done we'll pull
+// the interface out of the list and free the memory.
+//
+// Note that this code probably isn't MP safe. We'll need to fix that for
+// the port to NT.
+//
+// Input: Context - Pointer to primary NTE on the interface.
+//
+// Returns: Nothing.
+//
+void
+IPDelInterface(void *Context)
+{
+ NetTableEntry *NTE = (NetTableEntry *)Context;
+ NetTableEntry *FoundNTE = NULL;
+ Interface *IF, *PrevIF;
+ CTELockHandle Handle;
+ PNDIS_PACKET Packet;
+ PNDIS_BUFFER Buffer;
+ uchar *TDBuffer;
+ ReassemblyHeader *RH;
+ EchoControl *EC;
+ EchoRtn Rtn;
+ CTEBlockStruc Block;
+
+ IF = NTE->nte_if;
+
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ IF->if_flags |= IF_FLAGS_DELETING;
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
+ if (NTE->nte_if == IF) {
+
+ if (FoundNTE == NULL) {
+ FoundNTE = NTE;
+ }
+
+ // This guy is on the interface, and needs to be deleted.
+ IPDelNTE(NTE, &Handle);
+
+ CTEGetLock(&RouteTableLock, &Handle);
+ }
+ }
+
+ CTEFreeLock(&RouteTableLock, Handle);
+
+ // Clear this index from the IFBitMask.
+ CTEAssert(IFBitMask[(IF->if_index-1)/BITS_PER_WORD] & (1 << ((IF->if_index - 1)%BITS_PER_WORD)));
+
+ IFBitMask[(IF->if_index-1)/BITS_PER_WORD] &= ~(1 << ((IF->if_index - 1)%BITS_PER_WORD));
+
+ if (FoundNTE != NULL) {
+#ifdef CHICAGO
+ NotifyInterfaceChange(FoundNTE->nte_context, FALSE);
+#endif
+ }
+
+ //
+ // Free the TD resources on the IF.
+ //
+
+ while ((Packet = IF->if_tdpacket) != NULL) {
+
+ IF->if_tdpacket =
+ ((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link;
+
+ Buffer = Packet->Private.Head;
+ TDBuffer = NdisBufferVirtualAddress(Buffer);
+ NdisFreePacketPool(Packet->Private.Pool);
+
+#ifdef CHICAGO
+ NdisFreeBufferPool(Buffer->Pool);
+#endif
+ CTEFreeMem(TDBuffer);
+ }
+
+ // If this was the 'first' IF, set that to NULL and delete the broadcast
+ // route that goes through him.
+ if (FirstIF == IF) {
+ DeleteRoute(IP_LOCAL_BCST, HOST_MASK, IPADDR_LOCAL,
+ FirstIF);
+ DeleteRoute(IP_ZERO_BCST, HOST_MASK, IPADDR_LOCAL,
+ FirstIF);
+ FirstIF = NULL;
+ BCastMinMTU = 0xffff;
+ }
+
+ // OK, we've cleaned up all the routes through this guy.
+ // Get ready to block waiting for all reference to go
+ // away, then dereference our reference. After this, go
+ // ahead and try to block. Mostly likely our reference was
+ // the last one, so we won't block - we'll wake up immediately.
+ CTEInitBlockStruc(&Block);
+ IF->if_block = &Block;
+
+ DerefIF(IF);
+
+ (void)CTEBlock(&Block);
+
+ // OK, we've cleaned up all references, so there shouldn't be
+ // any more transmits pending through this interface. Close the
+ // adapter to force synchronization with any receives in process.
+
+
+ (*(IF->if_close))(IF->if_lcontext);
+
+ // Now walk the IFList, looking for this guy. When we find him, free him.
+ PrevIF = STRUCT_OF(Interface, &IFList, if_next);
+ while (PrevIF->if_next != IF && PrevIF->if_next != NULL)
+ PrevIF = PrevIF->if_next;
+
+ if (PrevIF->if_next != NULL) {
+ PrevIF->if_next = IF->if_next;
+ NumIF--;
+ CTEFreeMem(IF);
+ } else
+ CTEAssert(FALSE);
+
+ // If we've deleted the first interface but still have other valid
+ // interfaces, we need to create a new FirstIF and read broadcast routes
+ // through it. NumIF is always at least one because of the loopback
+ // interface.
+ if (FirstIF == NULL && NumIF != 1) {
+
+ FirstIF = IFList;
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
+ if ((NTE->nte_flags & NTE_VALID) && NTE != LoopNTE) {
+ BCastMinMTU = MIN(BCastMinMTU, NTE->nte_mss);
+ AddRoute(NTE->nte_if->if_bcast, HOST_MASK, IPADDR_LOCAL,
+ FirstIF, BCastMinMTU, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE,
+ NULL);
+ }
+ }
+ }
+
+}
+
+
+#else // _PNP_POWER
+
+
+//* NotifyAddrChange - Notify clients of a change in addresses.
+//
+// Called when we want to notify registered clients that an address has come
+// or gone. We call TDI to perform this function.
+//
+// Input: Addr - Addr that has changed.
+// Mask - Ignored - Mask that has changed.
+// Context - Ignored - PNP context for address
+// IPContext - NTE context for NTE
+// Handle - Pointer to where to get/set address registration
+// handle
+// ConfigName - Registry name to use to retrieve config info.
+// Added - True if the addr is coming, False if it's going.
+//
+// Returns: Nothing.
+//
+void
+NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext,
+ PVOID *Handle, PNDIS_STRING ConfigName, uint Added)
+
+{
+ IP_STATUS StatusType;
+ NDIS_HANDLE ConfigHandle = NULL;
+ int i;
+ ULStatusProc StatProc;
+
+
+ if (Added) {
+ StatusType = IP_ADDR_ADDED;
+
+#ifdef SECFLTR
+ //
+ // Open a configuration key
+ //
+ if (!OpenIFConfig(ConfigName, &ConfigHandle)) {
+ //
+ // Not much we can do. The transports will have
+ // to handle this.
+ //
+ CTEAssert(ConfigHandle == NULL);
+ }
+#endif // SECFLTR
+
+ }
+ else {
+ StatusType = IP_ADDR_DELETED;
+ }
+
+ for ( i = 0; i < NextPI; i++) {
+ StatProc = IPProtInfo[i].pi_status;
+ if (StatProc != NULL)
+ (*StatProc)(IP_HW_STATUS, StatusType, Addr, NULL_IP_ADDR,
+ NULL_IP_ADDR, 0, ConfigHandle );
+ }
+
+#ifdef SECFLTR
+
+ if (ConfigHandle != NULL) {
+ CloseIFConfig(ConfigHandle);
+ }
+
+#endif // SECFLTR
+
+}
+
+
+#endif // _PNP_POWER
+
+
+#pragma BEGIN_INIT
+
+//** ipinit - Initialize ourselves.
+//
+// This routine is called during initialization from the OS-specific
+// init code. We need to check for the presence of the common xport
+// environment first.
+//
+//
+// Entry: Nothing.
+//
+// Returns: 0 if initialization failed, non-zero if it succeeds.
+//
+int
+IPInit()
+{
+ IPConfigInfo *ci; // Pointer to our IP configuration info.
+ int numnets; // Number of nets active.
+ int i;
+ uint j; // Counter variables.
+ NetTableEntry *nt; // Pointer to current NTE.
+ LLIPBindInfo *ARPInfo; // Info. returned from ARP.
+ NDIS_STATUS Status;
+ Interface *NetInterface; // Interface for a particular net.
+ LLIPRegRtn RegPtr;
+ NetTableEntry *lastNTE;
+
+
+ if (!CTEInitialize())
+ return IP_INIT_FAILURE;
+
+ CTERefillMem();
+
+ if ((ci = IPGetConfig()) == NULL)
+ return IP_INIT_FAILURE;
+
+#ifndef _PNP_POWER
+ MaxIPNets = ci->ici_numnets + 1;
+#endif // _PNP_POWER
+
+ for (ATCIndex=0; ATCIndex < ATC_SIZE; ATCIndex++) {
+ ATCache[ATCIndex].atc_flags = 0;
+ }
+ ATCIndex = 0;
+
+ // First, initalize our loopback stuff.
+ NetTableList = InitLoopback(ci);
+ if (NetTableList == NULL)
+ return IP_INIT_FAILURE;
+
+ if (!ARPInit()) {
+ CTEFreeMem(NetTableList);
+ return IP_INIT_FAILURE; // Couldn't initialize ARP.
+ }
+
+ CTERefillMem();
+ if (!InitRouting(ci)) {
+ CTEFreeMem(NetTableList);
+ return IP_INIT_FAILURE;
+ }
+
+ RATimeout = DEFAULT_RA_TIMEOUT;
+#if 0
+ CTEInitLock(&PILock);
+#endif
+ LastPI = IPProtInfo;
+
+
+ if (!ci->ici_gateway)
+ InterfaceSize = sizeof(Interface);
+ else
+ InterfaceSize = sizeof(RouteInterface);
+
+ DeadGWDetect = ci->ici_deadgwdetect;
+ PMTUDiscovery = ci->ici_pmtudiscovery;
+ IGMPLevel = ci->ici_igmplevel;
+ DefaultTTL = MIN(ci->ici_ttl, 255);
+ DefaultTOS = ci->ici_tos & 0xfc;
+ if (IGMPLevel > 2)
+ IGMPLevel = 0;
+
+ InitTimestamp();
+
+#ifndef _PNP_POWER
+ numnets = ci->ici_numnets;
+
+ lastNTE = NetTableList; // loopback is only one on the list
+ CTEAssert(lastNTE != NULL);
+ CTEAssert(lastNTE->nte_next == NULL);
+
+ // Loop through the config. info, copying the addresses and masks.
+ for (i = 0; i < numnets; i++) {
+
+ CTERefillMem();
+ nt = CTEAllocMem(sizeof(NetTableEntry));
+ if (nt == NULL)
+ continue;
+
+ CTEMemSet(nt, 0, sizeof(NetTableEntry));
+
+ nt->nte_addr = net_long(ci->ici_netinfo[i].nci_addr);
+ nt->nte_mask = net_long(ci->ici_netinfo[i].nci_mask);
+ nt->nte_mss = MAX(ci->ici_netinfo[i].nci_mtu, 68);
+ nt->nte_flags = (IP_ADDR_EQUAL(nt->nte_addr, NULL_IP_ADDR) ? 0 :
+ NTE_VALID);
+ nt->nte_flags |= NTE_ACTIVE;
+
+ CTEInitLock(&nt->nte_lock);
+ // If the address is invalid, skip it.
+ if (CLASSD_ADDR(nt->nte_addr) || CLASSE_ADDR(nt->nte_addr)) {
+ CTEFreeMem(nt);
+ continue;
+ }
+
+ // See if we're already bound to this adapter. If we are, use the same
+ // interface. Otherwise assign a new one. We assume that the loopback
+ // interface is IF 1, so there is one less than NumIF in the table.
+ for (j = 0; j < NumIF - 1; j++) {
+ if (CTEEqualString(&(AdptNameTable[j].nm_name),
+ &(ci->ici_netinfo[i].nci_name))) {
+
+ // Names match. Now check driver/types.
+ if (((ci->ici_netinfo[i].nci_type == NET_TYPE_LAN) &&
+ (AdptNameTable[j].nm_driver.Buffer == NULL)) ||
+ (CTEEqualString(&(AdptNameTable[j].nm_driver),
+ &(ci->ici_netinfo[i].nci_driver))))
+ break; // Found a match
+ }
+ }
+
+ if (j < (NumIF - 1)) {
+
+ // Found a match above, so use that interface.
+ CTERefillMem();
+ nt->nte_if = AdptNameTable[j].nm_interface;
+ ARPInfo = AdptNameTable[j].nm_arpinfo;
+ // If the Init of the interface or the NTE fails, we don't want to
+ // close the interface, because another net is using it.
+
+ if (!InitInterface(nt)) {
+ CTEFreeMem(nt);
+ continue;
+ }
+ if (!InitNTE(nt)) {
+ CTEFreeMem(nt);
+ continue;
+ }
+
+ } else { // No match, create a new interface
+
+ CTEAssert(NumIF <= MaxIPNets);
+
+ if (NumIF == MaxIPNets) {
+ continue; // too many adapters
+ }
+
+ CTERefillMem();
+
+ ARPInfo = CTEAllocMem(sizeof(LLIPBindInfo));
+
+ if (ARPInfo == NULL) {
+ CTEFreeMem(nt);
+ continue;
+ }
+
+ NetInterface = CTEAllocMem(
+ InterfaceSize +
+ ci->ici_netinfo[i].nci_configname.MaximumLength
+ );
+
+ if (!NetInterface) {
+ CTEFreeMem(ARPInfo);
+ CTEFreeMem(nt);
+ continue;
+ }
+
+ CTEMemSet(NetInterface, 0, InterfaceSize);
+
+ nt->nte_if = NetInterface;
+ nt->nte_flags |= NTE_PRIMARY; // He is the primary NTE.
+
+ CTEInitLock(&NetInterface->if_lock);
+
+ if (ci->ici_gateway) {
+ // Hack in the max pending value here. Probably should be
+ // done in iproute.c, but it's easier to do it here.
+
+ RouteInterface *RtIF;
+
+ RtIF = (RouteInterface *)NetInterface;
+ RtIF->ri_q.rsq_maxpending = ci->ici_netinfo[i].nci_maxpending;
+ }
+
+ // If this is a LAN, register with ARP.
+ if (ci->ici_netinfo[i].nci_type == NET_TYPE_LAN)
+ RegPtr = ARPRegister;
+ else
+ RegPtr = FindRegPtr(&ci->ici_netinfo[i].nci_driver);
+
+ if (RegPtr == NULL || !((*RegPtr)(&ci->ici_netinfo[i].nci_name,
+ nt, IPRcv, IPSendComplete, IPStatus, IPTDComplete,
+ IPRcvComplete, ARPInfo, NumIF))) {
+ CTEFreeMem(ARPInfo);
+ CTEFreeMem(NetInterface);
+ CTEFreeMem(nt);
+ continue; // We're hosed, skip this net.
+ }
+ else {
+
+ if (ci->ici_netinfo[i].nci_zerobcast)
+ NetInterface->if_bcast = IP_ZERO_BCST;
+ else
+ NetInterface->if_bcast = IP_LOCAL_BCST;
+
+ NetInterface->if_xmit = ARPInfo->lip_transmit;
+ NetInterface->if_transfer = ARPInfo->lip_transfer;
+ NetInterface->if_close = ARPInfo->lip_close;
+ NetInterface->if_invalidate = ARPInfo->lip_invalidate;
+ NetInterface->if_lcontext = ARPInfo->lip_context;
+ NetInterface->if_addaddr = ARPInfo->lip_addaddr;
+ NetInterface->if_deladdr = ARPInfo->lip_deladdr;
+ NetInterface->if_qinfo = ARPInfo->lip_qinfo;
+ NetInterface->if_setinfo = ARPInfo->lip_setinfo;
+ NetInterface->if_getelist = ARPInfo->lip_getelist;
+ NetInterface->if_tdpacket = NULL;
+ NetInterface->if_index = ARPInfo->lip_index;
+ NetInterface->if_mtu = ARPInfo->lip_mss - sizeof(IPHeader);
+ NetInterface->if_speed = ARPInfo->lip_speed;
+ NetInterface->if_flags = ARPInfo->lip_flags & LIP_P2P_FLAG ?
+ IF_FLAGS_P2P : 0;
+ NetInterface->if_addrlen = ARPInfo->lip_addrlen;
+ NetInterface->if_addr = ARPInfo->lip_addr;
+ NetInterface->if_pnpcontext = PNPContext;
+ NetInterface->if_llipflags = ArpInfo->lip_flags
+
+ NetInterface->if_configname.Buffer =
+ (PVOID) (((uchar *)NetInterface) + InterfaceSize);
+
+ NetInterface->if_configname.Length = 0;
+ NetInterface->if_configname.MaximumLength =
+ ci->ici_netinfo[i].nci_configname.MaximumLength;
+
+ CTECopyString(
+ &(NetInterface->if_configname),
+ &(ci->ici_netinfo[i].nci_configname)
+ );
+
+ CTERefillMem();
+
+ if (!InitInterface(nt)) {
+ CTEFreeMem(ARPInfo);
+ CTEFreeMem(NetInterface);
+ CTEFreeMem(nt);
+ continue;
+ }
+
+ if (!InitNTE(nt)) {
+ CTEFreeMem(ARPInfo);
+ CTEFreeMem(NetInterface);
+ CTEFreeMem(nt);
+ continue;
+ }
+
+ CTERefillMem();
+ if (!CTEAllocateString(&AdptNameTable[j].nm_name,
+ CTELengthString(&ci->ici_netinfo[i].nci_name))) {
+ CTEFreeMem(ARPInfo);
+ CTEFreeMem(NetInterface);
+ CTEFreeMem(nt);
+ continue;
+ }
+
+ if (ci->ici_netinfo[i].nci_type != NET_TYPE_LAN) {
+ if (!CTEAllocateString(&AdptNameTable[j].nm_driver,
+ CTELengthString(&ci->ici_netinfo[i].nci_driver))) {
+ CTEFreeString(&AdptNameTable[j].nm_name);
+ CTEFreeMem(ARPInfo);
+ CTEFreeMem(NetInterface);
+ CTEFreeMem(nt);
+ continue;
+ }
+ CTECopyString(&(AdptNameTable[j].nm_driver),
+ &(ci->ici_netinfo[i].nci_driver));
+ }
+
+ CTECopyString(&(AdptNameTable[j].nm_name),
+ &(ci->ici_netinfo[i].nci_name));
+ AdptNameTable[j].nm_interface = NetInterface;
+ AdptNameTable[j].nm_arpinfo = ARPInfo;
+ NetInterface->if_next = IFList;
+ IFList = NetInterface;
+ if (FirstIF == NULL)
+ FirstIF = NetInterface;
+ NumIF++;
+
+#ifdef NT
+ //
+ // Write the interface context to the registry for DHCP et al
+ //
+ if (ci->ici_netinfo[i].nci_reghandle != NULL) {
+ NTSTATUS writeStatus;
+ ulong context = (ulong) nt->nte_context;
+
+ writeStatus = SetRegDWORDValue(
+ ci->ici_netinfo[i].nci_reghandle,
+ L"IPInterfaceContext",
+ &context
+ );
+
+ if (!NT_SUCCESS(writeStatus)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_DHCP_INIT_FAILED,
+ 2,
+ 1,
+ &(ci->ici_netinfo[i].nci_name.Buffer),
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to write IPInterfaceContext value for adapter %ws\n"
+ " (status %lx). DHCP will be unable to configure this \n"
+ " adapter.\n",
+ ci->ici_netinfo[i].nci_name.Buffer,
+ writeStatus
+ ));
+ }
+ }
+#endif // NT
+ }
+ }
+
+ nt->nte_next = NULL;
+ lastNTE->nte_next = nt;
+ lastNTE = nt;
+ NumNTE++;
+
+ if (!InitNTERouting(nt, ci->ici_netinfo[i].nci_numgws,
+ ci->ici_netinfo[i].nci_gw)) {
+ // Couldn't add the routes for this NTE. Mark has as not valid.
+ // Probably should log an event here.
+ if (nt->nte_flags & NTE_VALID) {
+ nt->nte_flags &= ~NTE_VALID;
+ nt->nte_if->if_ntecount--;
+ }
+ }
+
+#ifdef NT
+ if (!IP_ADDR_EQUAL(nt->nte_addr, NULL_IP_ADDR)) {
+ SetPersistentRoutesForNTE(
+ net_long(nt->nte_addr),
+ net_long(nt->nte_mask),
+ nt->nte_if->if_index
+ );
+ }
+#endif // NT
+
+ }
+
+#endif // ndef PNP_POWER
+
+ if (NumNTE != 0) { // We have an NTE, and loopback initialized.
+ PNDIS_PACKET Packet;
+
+#ifdef _PNP_POWER
+ for (i=0; i<MAX_TDI_ENTITIES; i++) {
+ IFBitMask[i/BITS_PER_WORD] = 0;
+ }
+ IFBitMask[0] = 1;
+#endif
+
+ IPSInfo.ipsi_forwarding = (ci->ici_gateway ? IP_FORWARDING :
+ IP_NOT_FORWARDING);
+ IPSInfo.ipsi_defaultttl = DefaultTTL;
+ IPSInfo.ipsi_reasmtimeout = DEFAULT_RA_TIMEOUT;
+
+ // Allocate our packet pools.
+ CTEInitLock(&HeaderLock);
+#ifdef NT
+ ExInitializeSListHead(&PacketList);
+ ExInitializeSListHead(&HdrBufList);
+#endif
+
+
+ Packet = GrowIPPacketList();
+
+ if (Packet == NULL) {
+ CloseNets();
+ FreeNets();
+ IPFreeConfig(ci);
+ return IP_INIT_FAILURE;
+ }
+
+ (void)FreeIPPacket(Packet);
+
+ NdisAllocateBufferPool(&Status, &BufferPool, NUM_IP_NONHDR_BUFFERS);
+ if (Status != NDIS_STATUS_SUCCESS) {
+#ifdef DEBUG
+ DEBUGCHK;
+#endif
+ }
+
+ CTERefillMem();
+
+ ICMPInit(DEFAULT_ICMP_BUFFERS);
+ if (!IGMPInit())
+ IGMPLevel = 1;
+
+ // Should check error code, and log an event here if this fails.
+ CTERefillMem();
+ InitGateway(ci);
+
+ IPFreeConfig(ci);
+ CTERefillMem();
+
+#ifndef _PNP_POWER
+ OpenAdapters();
+ CleanAdaptTable(); // Clean up the adapter info we don't need.
+#endif
+
+ CTERefillMem();
+
+ // Loop through, initialize IGMP for each NTE.
+ for (nt = NetTableList; nt != NULL; nt = nt->nte_next)
+ InitIGMPForNTE(nt);
+
+ return IP_INIT_SUCCESS;
+ }
+ else {
+ FreeNets();
+ IPFreeConfig(ci);
+ return IP_INIT_FAILURE; // Couldn't initialize anything.
+ }
+}
+
+#pragma END_INIT
+
+
diff --git a/private/ntos/tdi/tcpip/ip/ipdef.h b/private/ntos/tdi/tcpip/ip/ipdef.h
new file mode 100644
index 000000000..9d93d0616
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/ipdef.h
@@ -0,0 +1,371 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+#include "ipfilter.h"
+
+//** IPDEF.H - IP private definitions.
+//
+// This file contains all of the definitions for IP that
+// are private to IP, i.e. not visible to outside layers.
+
+// Internal error codes, not seen by IP uses.
+#define IP_OPTION_STRICT (MAX_IP_STATUS+1)
+
+#define CLASSA_ADDR(a) (( (*((uchar *)&(a))) & 0x80) == 0)
+#define CLASSB_ADDR(a) (( (*((uchar *)&(a))) & 0xc0) == 0x80)
+#define CLASSC_ADDR(a) (( (*((uchar *)&(a))) & 0xe0) == 0xc0)
+#define CLASSE_ADDR(a) ((( (*((uchar *)&(a))) & 0xf0) == 0xf0) && \
+ ((a) != 0xffffffff))
+
+#define CLASSA_MASK 0x000000ff
+#define CLASSB_MASK 0x0000ffff
+#define CLASSC_MASK 0x00ffffff
+#define CLASSD_MASK 0x000000e0
+#define CLASSE_MASK 0xffffffff
+
+#define IP_OPT_COPIED 0x80 // Bit indicating options is to be copied.
+#define IP_OPT_TYPE 0
+#define IP_OPT_LENGTH 1
+#define IP_OPT_DATA 2
+#define IP_OPT_PTR 2 // Pointer offset, for those options that have it.
+#define IP_TS_OVFLAGS 3 // Offset for overflow and flags.
+#define IP_TS_FLMASK 0xf // Mask for flags
+#define IP_TS_OVMASK 0xf0 // Mask for overflow field.
+#define IP_TS_MAXOV 0xf0 // Maximum value for the overflow field.
+#define IP_TS_INC 0x10 // Increment used on overflow field.
+
+#define MIN_RT_PTR 4
+#define MIN_TS_PTR 5
+
+#define TS_REC_TS 0 // Record TS option.
+#define TS_REC_ADDR 1 // Record TS and address.
+#define TS_REC_SPEC 3 // Only specified addresses record.
+
+#define OPT_SSRR 1 // We've seen a SSRR in this option buffer
+#define OPT_LSRR 2 // We've seen a LSRR in this option buffer
+#define OPT_RR 4 // We've seen a RR
+#define OPT_TS 8 // We've seen a TS.
+
+#define MAX_OPT_SIZE 40
+
+#define ALL_ROUTER_MCAST 0x020000E0
+
+// Received option index structure.
+struct OptIndex {
+ uchar oi_srindex;
+ uchar oi_rrindex;
+ uchar oi_tsindex;
+ uchar oi_srtype;
+}; /* OptIndex */
+
+typedef struct OptIndex OptIndex;
+
+#define MAX_HDR_SIZE (sizeof(IPHeader) + MAX_OPT_SIZE)
+
+#define DEFAULT_VERLEN 0x45 // Default version and length.
+
+#define IP_VERSION 0x40
+#define IP_VER_FLAG 0xF0
+
+#define IP_RSVD_FLAG 0x0080 // Reserved.
+#define IP_DF_FLAG 0x0040 // 'Don't fragment' flag
+#define IP_MF_FLAG 0x0020 // 'More fragments flag'
+
+
+#define IP_OFFSET_MASK ~0x00E0 // Mask for extracting offset field.
+
+typedef IP_STATUS (*ULRcvProc)(void *, IPAddr, IPAddr, IPAddr, IPAddr,
+ IPHeader UNALIGNED *, uint, IPRcvBuf *, uint,
+ uchar, uchar, IPOptInfo *);
+
+typedef uint (*ULStatusProc)(uchar, IP_STATUS, IPAddr, IPAddr, IPAddr, ulong, void *);
+
+//* Protocol information structure. These is one of there for each protocol bound
+// to an NTE.
+struct ProtInfo {
+ void (*pi_xmitdone)(void *, PNDIS_BUFFER); // Pointer to xmit done routine.
+ ULRcvProc pi_rcv; // Pointer to receive routine.
+ ULStatusProc pi_status; // Pointer to status handler.
+ void (*pi_rcvcmplt)(void); // Pointer to recv. cmplt handler.
+ uchar pi_protocol; // Protocol type.
+ uchar pi_pad[3]; // Pad to dword
+}; /* ProtInfo */
+
+typedef struct ProtInfo ProtInfo;
+
+//* Per-net information. We keep a variety of information for
+// each net, including the IP address, subnet mask, and reassembly
+// information.
+
+#define MAX_IP_PROT 5 // ICMP, IGMP, TCP, UDP, & Raw
+
+struct IPRtrEntry {
+ struct IPRtrEntry *ire_next;
+ IPAddr ire_addr;
+ long ire_preference;
+ ushort ire_lifetime;
+ ushort ire_pad;
+}; /* IPRtrEntry */
+
+typedef struct IPRtrEntry IPRtrEntry;
+
+struct NetTableEntry {
+ struct NetTableEntry *nte_next; // Next NTE of I/F.
+ IPAddr nte_addr; // IP address for this net.
+ IPMask nte_mask; // Subnet mask for this net.
+ struct Interface *nte_if; // Pointer to interface for this net.
+ struct NetTableEntry *nte_ifnext; // Linkage on if chain.
+ ushort nte_flags; // Flags for NTE.
+ ushort nte_context; // Context passed to upper layers.
+ ulong nte_instance; // Unique instance ID for this net
+ void *nte_pnpcontext; // PNP context.
+ DEFINE_LOCK_STRUCTURE(nte_lock)
+ struct ReassemblyHeader *nte_ralist; // Reassembly list.
+ struct EchoControl *nte_echolist; // List of pending echo control blocks
+ CTETimer nte_timer; // Timer for this net.
+ ushort nte_mss;
+ ushort nte_icmpseq; // ICMP seq. #
+ struct IGMPAddr *nte_igmplist; // List of mcast addresses.
+#ifdef _PNP_POWER
+ void *nte_addrhandle; // Handle for address registration.
+#endif
+ IPAddr nte_rtrdiscaddr; // Address used for Router Discovery
+ uchar nte_rtrdiscstate; // state of router solicitations
+ uchar nte_rtrdisccount; // router solicitation count
+ uchar nte_rtrdiscovery;
+ IPRtrEntry *nte_rtrlist;
+
+}; /* NetTableEntry */
+
+typedef struct NetTableEntry NetTableEntry;
+
+#define NTE_VALID 0x0001 // NTE is valid.
+#define NTE_COPY 0x0002 // For NDIS copy lookahead stuff.
+#define NTE_PRIMARY 0x0004 // This is the 'primary' NTE on the I/F.
+#define NTE_ACTIVE 0x0008 // NTE is active, i.e. interface is valid.
+#define NTE_DYNAMIC 0x0010 // NTE is was created dynamically
+
+#define IP_TIMEOUT 500
+
+#define NTE_RTRDISC_UNINIT 0
+#define NTE_RTRDISC_DELAYING 1
+#define NTE_RTRDISC_SOLICITING 2
+
+#define MAX_SOLICITATION_DELAY 2 // ticks to delay
+#define SOLICITATION_INTERVAL 6 // ticks between solicitations
+#define MAX_SOLICITATIONS 3 // number of solicitations
+
+struct AddrTypeCache {
+ IPAddr atc_addr; // IP Addr of cache entry
+ uchar atc_flags; // Valid flag
+ uchar atc_type; // Addr Type
+};
+
+typedef struct AddrTypeCache AddrTypeCache;
+
+#define ATC_SIZE 8
+#define ATC_MASK 7 // mask used to make sure indexes are less than ATC_SIZE
+
+//* Buffer reference structure. Used by broadcast and fragmentation code to
+// track multiple references to a single user buffer.
+struct BufferReference {
+ PNDIS_BUFFER br_buffer; // Pointer to uses buffer.
+ DEFINE_LOCK_STRUCTURE(br_lock)
+ int br_refcount; // Count of references to user's buffer.
+}; /* BufferReference */
+
+typedef struct BufferReference BufferReference;
+
+// Definitions of flags in pc_flags field
+#define PACKET_FLAG_OPTIONS 1 // Set if packet has an options buffer.
+#define PACKET_FLAG_IPBUF 2 // Set if packet is composed of IP buffers.
+#define PACKET_FLAG_RA 4 // Set if packet is being used for reassembly.
+#define PACKET_FLAG_FW 8 // Set if packet is a forwarding packet.
+#define PACKET_FLAG_IPHDR 0x10 // Packet uses an IP hdr buffer.
+
+//* Transfer data packet context. Used when TD'ing a packet - we store information for the
+// callback here.
+struct TDContext {
+ struct PCCommon tdc_common;
+ void *tdc_buffer; // Pointer to buffer containing data.
+ NetTableEntry *tdc_nte; // NTE to receive this on.
+ struct RABufDesc *tdc_rbd; // Pointer to RBD, if any.
+ uchar tdc_dtype; // Destination type of original address.
+ uchar tdc_hlength; // Length in bytes of header.
+ uchar tdc_pad[2];
+ uchar tdc_header[MAX_HDR_SIZE + 8];
+}; /* TDContext */
+
+typedef struct TDContext TDContext;
+
+//* Information about net interfaces. There can be multiple nets for each interface,
+// but there is exactly one interface per net.
+
+struct Interface {
+ struct Interface *if_next; // Next interface in chain.
+ void *if_lcontext; // Link layer context.
+ NDIS_STATUS (*if_xmit)(void *, PNDIS_PACKET, IPAddr, RouteCacheEntry *);
+ NDIS_STATUS (*if_transfer)(void *, NDIS_HANDLE, uint, uint, uint, PNDIS_PACKET,
+ uint *);
+ void (*if_close)(void *);
+ void (*if_invalidate)(void *, RouteCacheEntry *);
+ uint (*if_addaddr)(void *, uint, IPAddr, IPMask, void *);
+ void (*if_deladdr)(void *, uint, IPAddr, IPMask);
+ int (*if_qinfo)(void *, struct TDIObjectID *,
+ PNDIS_BUFFER, uint *, void *);
+ int (*if_setinfo)(void *, struct TDIObjectID *, void *,
+ uint);
+ int (*if_getelist)(void *, void *, uint *);
+ PNDIS_PACKET if_tdpacket; // Packet used for transferring data.
+ uint if_index; // Index of this interface.
+ uint if_ntecount; // Valid NTEs on this interface.
+ NetTableEntry *if_nte; // Pointer to list of NTE on interface.
+ IPAddr if_bcast; // Broadcast address for this interface.
+ uint if_mtu; // True maximum MTU for the interface.
+ uint if_speed; // Speed in bits/sec of this interface.
+ uint if_flags; // Flags for this interface.
+ INTERFACE_CONTEXT if_filtercontext; // Filter context for this i/f.
+ uint if_addrlen; // Length of i/f addr.
+ uchar *if_addr; // Pointer to addr.
+
+ uint IgmpVersion; //igmp version active on this interface
+ uint IgmpVer1Timeout; //Version 1 router present timeout
+#ifdef _PNP_POWER
+ uint if_refcount; // Reference count for this i/f.
+ CTEBlockStruc *if_block; // Block structure for PnP.
+ void *if_pnpcontext; // Context to pass to upper layers.
+#endif // _PNP_POWER
+
+ uint if_llipflags; // Lower layer flags
+#ifdef SECFLTR
+ NDIS_STRING if_configname; // Name of the i/f config section
+#endif //SECFLTR
+ DEFINE_LOCK_STRUCTURE(if_lock)
+}; /* Interface */
+
+typedef struct Interface Interface;
+
+/*NOINC*/
+extern void DerefIF(Interface *IF);
+/*INC*/
+
+#define IF_FLAGS_P2P 1 // Point to point interface
+#define IF_FLAGS_DELETING 2 // Interface is in the process of going
+ // away.
+
+// Structure of a reassembly buffer descriptor. Each RBD describes a fragment of the total
+// datagram
+struct RABufDesc {
+ IPRcvBuf rbd_buf; // IP receive buffer for this fragment.
+ ushort rbd_start; // Offset of first byte of this fragment.
+ ushort rbd_end; // Offset of last byte of this fragment.
+}; /* RABufDesc */
+
+typedef struct RABufDesc RABufDesc;
+
+// Reassembly header. The includes the information needed for the lookup, as well as space
+// for the received header and a chain of reassembly buffer descriptors.
+struct ReassemblyHeader {
+ struct ReassemblyHeader *rh_next; // Next header in chain.
+ IPAddr rh_dest; // Destination address of fragment.
+ IPAddr rh_src; // Source address of fragment.
+ ushort rh_id; // ID of datagram.
+ uchar rh_protocol; // Protocol of datagram.
+ uchar rh_ttl; // Remaining time of datagram.
+ RABufDesc *rh_rbd; // Chain of RBDs for this datagram.
+ ushort rh_datasize; // Total size of data.
+ ushort rh_datarcvd; // Amount of data received so far.
+ uint rh_headersize; // Size in bytes of header.
+ uchar rh_header[MAX_HDR_SIZE+8]; // Saved IP header of first fragment.
+}; /* ReassemblyHeader */
+
+typedef struct ReassemblyHeader ReassemblyHeader;
+
+// ICMP type and code definitions
+#define IP_DEST_UNREACH_BASE IP_DEST_NET_UNREACHABLE
+
+#define ICMP_REDIRECT 5 // Redirect
+#define ADDR_MASK_REQUEST 17 // Address mask request
+#define ADDR_MASK_REPLY 18
+#define ICMP_DEST_UNREACH 3 // Destination unreachable
+#define ICMP_TIME_EXCEED 11 // Time exceeded during reassembly
+#define ICMP_PARAM_PROBLEM 12 // Parameter problem
+#define ICMP_SOURCE_QUENCH 4 // Source quench
+#define ICMP_ROUTER_ADVERTISEMENT 9 // Router Advertisement
+#define ICMP_ROUTER_SOLICITATION 10 // Router Solicitation
+
+#define NET_UNREACH 0
+#define HOST_UNREACH 1
+#define PROT_UNREACH 2
+#define PORT_UNREACH 3
+#define FRAG_NEEDED 4
+#define SR_FAILED 5
+#define DEST_NET_UNKNOWN 6
+#define DEST_HOST_UNKNOWN 7
+#define SRC_ISOLATED 8
+#define DEST_NET_ADMIN 9
+#define DEST_HOST_ADMIN 10
+#define NET_UNREACH_TOS 11
+#define HOST_UNREACH_TOS 12
+
+#define TTL_IN_TRANSIT 0 // TTL expired in transit
+#define TTL_IN_REASSEM 1 // Time exceeded in reassembly
+
+
+#define PTR_VALID 0
+#define REQ_OPTION_MISSING 1
+
+#define REDIRECT_NET 0
+#define REDIRECT_HOST 1
+#define REDIRECT_NET_TOS 2
+#define REDIRECT_HOST_TOS 3
+
+extern uint DHCPActivityCount;
+
+extern IP_STATUS SetIFContext(uint Index, INTERFACE_CONTEXT *Context);
+
+extern IP_STATUS SetFilterPtr(IPPacketFilterPtr FilterPtr);
+
+extern IP_STATUS SetMapRoutePtr(IPMapRouteToInterfacePtr MapRoutePtr);
+
+#ifdef NT
+
+#ifdef POOL_TAGGING
+
+#ifdef ExAllocatePool
+#undef ExAllocatePool
+#endif
+
+#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'iPCT')
+
+#ifndef CTEAllocMem
+#error "CTEAllocMem is not already defined - will override tagging"
+#else
+#undef CTEAllocMem
+#endif
+
+#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'iPCT')
+
+#endif // POOL_TAGGING
+
+//
+// Use the TCP core checksum routine.
+//
+
+ULONG
+tcpxsum (
+ IN ULONG Checksum,
+ IN PUCHAR Source,
+ IN ULONG Length
+ );
+
+#define xsum(Buffer, Length) ((ushort) tcpxsum(0, (PUCHAR) (Buffer), (Length)))
+
+#else // NT
+
+extern ushort xsum(void *, int);
+
+#endif // NT
+
diff --git a/private/ntos/tdi/tcpip/ip/ipinit.h b/private/ntos/tdi/tcpip/ip/ipinit.h
new file mode 100644
index 000000000..d1d730915
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/ipinit.h
@@ -0,0 +1,155 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** IPINIT.H - IP initialization definitions.
+//
+// This file contains all of the definitions for IP that are
+// init. time specific.
+
+#define IP_INIT_FAILURE 0 // If we fail.
+#define IP_INIT_SUCCESS 1
+#define CFG_REQUIRED 1
+#define CFG_OPTIONAL 0
+
+
+#define NET_TYPE_LAN 0 // The local net interface is a LAN.
+#define NET_TYPE_WAN 1 // Point to point or other non-LAN network.
+#define DEFAULT_TTL 128
+#define DEFAULT_TOS 0
+
+#define MAX_DEFAULT_GWS 5 // Maximum number of default gateways per net.
+#define MAX_NAME_SIZE 32 // Maximum length of an adapter name.
+
+#define DEFAULT_FW_PACKETS 50 // Default number of packets for forwarding.
+#define DEFAULT_FW_BUFSIZE 74240 // Enough for 50 1480-byte Ethernet packets,
+ // rounded up to a multiple of 256.
+
+#define DEFAULT_MAX_FW_PACKETS 0xffffffff
+#define DEFAULT_MAX_FW_BUFSIZE 0xffffffff
+
+#define DEFAULT_MAX_PENDING 5000
+
+#define TR_RII_ALL 0x80
+#define TR_RII_SINGLE 0xC0
+
+#define DEFAULT_ARP_CACHE_LIFE (2L*60L) // 2 minutes
+
+#ifndef _PNP_POWER
+
+//* Per net config. information.
+struct NetConfigInfo {
+ IPAddr nci_addr; // IPAddr for this net.
+ IPMask nci_mask; // Net mask for this net.
+ uint nci_type; // Type of this net - Enet, TR, SLIP., etc.
+ NDIS_STRING nci_driver; // Device name for lower layer driver.
+ // Unused for NET_TYPE_LAN.
+ NDIS_STRING nci_name; // Name of adapter for this net.
+
+#ifdef SECFLTR
+ NDIS_STRING nci_configname; // Name of config section in registry.
+#endif // SECFLTR
+
+ uint nci_zerobcast; // Type of broadcast to be used on this net.
+
+#ifdef NT
+ HANDLE nci_reghandle; // Open handle to the registry key for
+ // this adapter.
+#endif // NT
+
+ uint nci_mtu; // Max MSS for this net.
+ uint nci_maxpending; // Max routing packets pending.
+ uint nci_numgws; // Number of default gateways for this interface.
+ IPAddr nci_gw[MAX_DEFAULT_GWS]; // Array of IPaddresses for gateways
+ uint nci_rtrdiscovery; // Router discovery enabled
+ IPAddr nci_rtrdiscaddr; // Multicast or BCast?
+}; /* NetConfigInfo */
+
+typedef struct NetConfigInfo NetConfigInfo;
+
+
+#else // _PNP_POWER
+
+/*NOINC*/
+
+
+// Per-net config structures for Chicago.
+typedef struct IFGeneralConfig {
+ uint igc_zerobcast; // Type of broadcast to be used on this net.
+ uint igc_mtu; // Max MSS for this net.
+ uint igc_maxpending; // Max FW pending on this IF.
+ uint igc_numgws; // Number of default gateways for this
+ // interface.
+ IPAddr igc_gw[MAX_DEFAULT_GWS]; // Array of IPaddresses for gateways
+ uint igc_rtrdiscovery; // Router discovery enabled
+ IPAddr igc_rtrdiscaddr; // Multicast or BCast?
+} IFGeneralConfig;
+
+typedef struct IFAddrList {
+ IPAddr ial_addr; // Address for this interface.
+ IPMask ial_mask; // Mask to go with this.
+} IFAddrList;
+
+
+/*INC*/
+
+#endif // _PNP_POWER
+
+//* Structure of configuration information. A pointer to this information
+// is returned from a system-specific config. information routine.
+struct IPConfigInfo {
+ uint ici_gateway; // 1 if we are a gateway, 0 otherwise
+ uint ici_fwbcast; // 1 if bcasts should be forwarded. Else 0.
+ uint ici_fwbufsize; // Total size of FW buf size.
+ uint ici_fwpackets; // Total number of FW packets to have.
+ uint ici_maxfwbufsize; // Maximum size of FW buffer.
+ uint ici_maxfwpackets; // Maximum number of FW packets.
+ uint ici_deadgwdetect; // True if we're doing dead GW detection.
+ uint ici_pmtudiscovery; // True if we're doing Path MTU discovery.
+ uint ici_igmplevel; // Level of IGMP we're doing.
+ uint ici_ttl; // Default TTL.
+ uint ici_tos; // Default TOS;
+
+#ifndef _PNP_POWER
+ int ici_numnets; // Number of nets present.
+ struct NetConfigInfo *ici_netinfo; // Per net config. info
+#endif // _PNP_POWER
+
+}; /* IPConfigInfo */
+
+typedef struct IPConfigInfo IPConfigInfo;
+
+
+#ifndef _PNP_POWER
+
+struct NameMapping {
+ NDIS_STRING nm_driver;
+ NDIS_STRING nm_name;
+ void *nm_interface;
+ void *nm_arpinfo;
+}; /* NameMapping */
+
+typedef struct NameMapping NameMapping;
+
+struct DriverRegMapping {
+ NDIS_STRING drm_driver;
+ void *drm_regptr;
+}; /* DriverRegMapping */
+
+typedef struct DriverRegMapping DriverRegMapping;
+
+#endif // _PNP_POWER
+extern uchar TrRii;
+
+
+struct SetAddrControl {
+ void *sac_rtn; // Pointer to routine to call when completing request.
+}; /* SetAddrControl */
+
+/*NOINC*/
+typedef struct SetAddrControl SetAddrControl;
+typedef void (*SetAddrRtn)(void *, IP_STATUS);
+/*INC*/
+
diff --git a/private/ntos/tdi/tcpip/ip/iploop.c b/private/ntos/tdi/tcpip/ip/iploop.c
new file mode 100644
index 000000000..b47676876
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/iploop.c
@@ -0,0 +1,665 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** iploop.c - IP loopback routines.
+//
+// This file contains all the routines related to loopback
+
+#include "oscfg.h"
+#include "cxport.h"
+#include "ndis.h"
+#include "ip.h"
+#include "tdistat.h"
+#include "ipdef.h"
+#include "ipinit.h"
+#include "llipif.h"
+#include "iprtdef.h"
+#include "iproute.h"
+#include "tdiinfo.h"
+#include "llinfo.h"
+
+#define LOOP_LOOKAHEAD MAX_HDR_SIZE + 8
+
+extern int NumNTE;
+
+extern Interface *IFList;
+extern uint NumIF;
+
+extern void IPRcv(void *, void *, uint, uint, NDIS_HANDLE, uint, uint);
+extern void IPSendComplete(void *, PNDIS_PACKET, NDIS_STATUS);
+extern void IPRcvComplete(void);
+extern PNDIS_BUFFER CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size,
+ uint *StartOffset);
+
+DEFINE_LOCK_STRUCTURE(LoopLock)
+PNDIS_PACKET LoopXmitHead = (PNDIS_PACKET)NULL;
+PNDIS_PACKET LoopXmitTail = (PNDIS_PACKET)NULL;
+CTEEvent LoopXmitEvent;
+RouteInterface LoopInterface; // Loopback interface.
+uint LoopXmitRtnRunning = 0;
+
+
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+//
+// Make init code disposable.
+//
+NetTableEntry *InitLoopback(IPConfigInfo *ConfigInfo);
+
+#pragma alloc_text(INIT, InitLoopback)
+
+#endif // ALLOC_PRAGMA
+#endif // NT
+
+
+uint LoopIndex; // Index of loop I/F.
+uint LoopInstance; // I/F instance of loopback I/F.
+NetTableEntry *LoopNTE; // Pointer to loopback NTE.
+
+IFEntry LoopIFE; // Loopback IF Entry.
+
+uchar LoopName[] = "MS TCP Loopback interface";
+
+uint LoopEntityType = IF_MIB;
+
+//* LoopXmitRtn - Loopback xmit event routine.
+//
+// This is the delayed event routine called for a loopback transmit.
+//
+// Entry: Event - Pointer to event structure.
+// Context - Pointer to loopback NTE
+//
+// Returns: Nothing.
+//
+void
+LoopXmitRtn(CTEEvent *Event, void *Context)
+{
+ PNDIS_PACKET Packet; // Pointer to packet being transmitted
+ CTELockHandle Handle;
+ PNDIS_BUFFER Buffer; // Current NDIS buffer being processed.
+ uint TotalLength; // Total length of send.
+ uint LookaheadLength; // Bytes in lookahead.
+ uint Copied; // Bytes copied so far.
+ uchar *CopyPtr; // Pointer to buffer being copied into.
+ uchar *SrcPtr; // Pointer to buffer being copied from.
+ uint SrcLength; // Length of src buffer.
+ uchar LookaheadBuffer[LOOP_LOOKAHEAD];
+ uchar Rcvd = FALSE;
+#ifdef NT
+ KIRQL OldIrql;
+
+
+ ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+ //
+ // Raise IRQL so we can acquire locks at DPC level in the receive code.
+ //
+ KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+#endif // NT
+
+ CTEGetLock(&LoopLock, &Handle);
+
+ if (LoopXmitRtnRunning) {
+ CTEFreeLock(&LoopLock, Handle);
+#ifdef NT
+ KeLowerIrql(OldIrql);
+#endif // NT
+ return;
+ }
+
+ LoopXmitRtnRunning = 1;
+
+ for (;;) {
+
+ Packet = LoopXmitHead; // Get the next packet from the list.
+ if (Packet != (PNDIS_PACKET)NULL) {
+ LoopXmitHead = *(PNDIS_PACKET *)Packet->MacReserved;
+ LoopIFE.if_outqlen--;
+ CTEFreeLock(&LoopLock, Handle);
+ } else { // Nothing left to do.
+ LoopXmitRtnRunning = 0;
+ CTEFreeLock(&LoopLock, Handle);
+ break;
+ }
+
+ NdisQueryPacket(Packet, NULL, NULL, &Buffer, &TotalLength);
+ LoopIFE.if_outoctets += TotalLength;
+ LoopIFE.if_inoctets += TotalLength;
+
+ // See if the interface is up. If it's not, we can't deliver it.
+ if (LoopIFE.if_adminstatus == IF_STATUS_UP) {
+ LookaheadLength = MIN(LOOP_LOOKAHEAD, TotalLength);
+ Copied = 0;
+ CopyPtr = LookaheadBuffer;
+ while (Copied < LookaheadLength) {
+ uint ThisCopy; // Bytes to copy this time.
+
+#ifdef DEBUG
+ if (!Buffer) {
+ DEBUGCHK;
+ CTEGetLock(&LoopLock, &Handle);
+ LoopXmitRtnRunning = 0;
+ CTEFreeLock(&LoopLock, Handle);
+#ifdef NT
+ KeLowerIrql(OldIrql);
+#endif // NT
+ return;
+ }
+#endif
+
+ NdisQueryBuffer(Buffer, &SrcPtr, &SrcLength);
+ ThisCopy = MIN(SrcLength, LookaheadLength - Copied);
+ CTEMemCopy(CopyPtr, SrcPtr, ThisCopy);
+ Copied += ThisCopy;
+ CopyPtr += ThisCopy;
+ NdisGetNextBuffer(Buffer, &Buffer);
+ }
+
+ Rcvd = TRUE;
+ LoopIFE.if_inucastpkts++;
+
+ IPRcv(Context, LookaheadBuffer, LookaheadLength, TotalLength,
+ (NDIS_HANDLE) Packet, 0, FALSE);
+
+ } else {
+ LoopIFE.if_indiscards++;
+ }
+
+ IPSendComplete(Context, Packet, NDIS_STATUS_SUCCESS);
+
+#ifdef NT
+ //
+ // Give other threads a chance to run.
+ //
+ KeLowerIrql(OldIrql);
+ KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+#endif // NT
+
+ CTEGetLock(&LoopLock, &Handle);
+ }
+
+ if (Rcvd) {
+ IPRcvComplete();
+ }
+
+#ifdef NT
+ KeLowerIrql(OldIrql);
+#endif // NT
+
+}
+
+//** LoopXmit - Transmit a loopback packet.
+//
+// This is the routine called when we need to transmit a packet to ourselves. We put
+// the packet on our loopback list, and schedule an event to deal with it.
+//
+// Entry: Context - Pointer to the loopback NTE.
+// Packet - Pointer to packet to be transmitted.
+// Dest - Destination addres of packet.
+// RCE - Pointer to RCE (should be NULL).
+//
+// Returns: NDIS_STATUS_PENDING
+//
+NDIS_STATUS
+LoopXmit(void *Context, PNDIS_PACKET Packet, IPAddr Dest, RouteCacheEntry *RCE)
+{
+ PNDIS_PACKET *PacketPtr;
+ CTELockHandle Handle;
+
+ LoopIFE.if_outucastpkts++;
+
+ if (LoopIFE.if_adminstatus == IF_STATUS_UP) {
+ PacketPtr = (PNDIS_PACKET *)Packet->MacReserved;
+ *PacketPtr = (PNDIS_PACKET)NULL;
+
+
+ CTEGetLock(&LoopLock, &Handle);
+ if (LoopXmitHead == (PNDIS_PACKET)NULL) { // Xmit. Q is empty
+ LoopXmitHead = Packet;
+ } else { // Xmit. Q is not empty
+ PacketPtr = (PNDIS_PACKET *)LoopXmitTail->MacReserved;
+ *PacketPtr = Packet;
+ }
+ LoopXmitTail = Packet;
+ LoopIFE.if_outqlen++;
+ if (!LoopXmitRtnRunning) {
+ CTEScheduleEvent(&LoopXmitEvent, Context);
+ }
+ CTEFreeLock(&LoopLock, Handle);
+ return NDIS_STATUS_PENDING;
+ } else {
+ LoopIFE.if_outdiscards++;
+ return NDIS_STATUS_SUCCESS;
+ }
+}
+
+//* LoopXfer - Loopback transfer data routine.
+//
+// Called when we need to transfer data for the loopback net. The input TDContext is
+// the original packet.
+//
+// Entry: Context - Pointer to loopback NTE.
+// TDContext - Original packet that was sent.
+// Dummy - Unused
+// Offset - Offset in frame from which to start copying.
+// BytesToCopy - Number of bytes to copy.
+// DestPacket - Packet describing buffer to copy into.
+// BytesCopied - Place to return bytes copied.
+//
+// Returns: NDIS_STATUS_SUCCESS
+//
+NDIS_STATUS
+LoopXfer(void *Context, NDIS_HANDLE TDContext, uint Dummy, uint Offset, uint BytesToCopy,
+ PNDIS_PACKET DestPacket, uint *BytesCopied)
+{
+ PNDIS_BUFFER SrcBuffer; // Current buffer we're copying from.
+ PNDIS_PACKET SrcPacket = (PNDIS_PACKET)TDContext;
+ uchar *SrcPtr; // Where we're copying from.
+ uint SrcLength; // Length of current src buffer.
+ PNDIS_BUFFER DestBuffer; // Buffer we're copying to.
+ uchar *DestPtr; // Where we're copying to.
+ uint DestLength; // Length of current dest. buffer.
+ uint Copied; // Length we've copied so far.
+
+ // First, skip over Offset bytes in the packet.
+ NdisQueryPacket(SrcPacket, NULL, NULL, &SrcBuffer, NULL);
+#ifdef DEBUG
+ if (!SrcBuffer)
+ DEBUGCHK;
+#endif
+ NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength);
+ while (Offset >= SrcLength) {
+ Offset -= SrcLength;
+ NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
+#ifdef DEBUG
+ if (!SrcBuffer)
+ DEBUGCHK;
+#endif
+ NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength);
+ }
+ // Update Src pointer and length.
+ SrcPtr += Offset;
+ SrcLength -= Offset;
+
+ // Set up the destination pointers and lengths.
+ NdisQueryPacket(DestPacket, NULL, NULL, &DestBuffer, NULL);
+ NdisQueryBuffer(DestBuffer, &DestPtr, &DestLength);
+ Copied = 0;
+
+ while (BytesToCopy) {
+ uint ThisCopy; // What we're copying this time.
+
+ ThisCopy = MIN(SrcLength, DestLength);
+ CTEMemCopy(DestPtr, SrcPtr, ThisCopy);
+ Copied += ThisCopy;
+ DestPtr += ThisCopy;
+ SrcPtr += ThisCopy;
+ BytesToCopy -= ThisCopy;
+ SrcLength -= ThisCopy;
+ DestLength -= ThisCopy;
+ if (!SrcLength) { // We've exhausted the source buffer.
+ NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
+ if (!SrcBuffer) {
+#ifdef DEBUG
+ if (BytesToCopy)
+ DEBUGCHK;
+#endif
+ break; // Copy is done.
+ }
+ NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength);
+ }
+ if (!DestLength) { // We've exhausted the destination buffer.
+ NdisGetNextBuffer(DestBuffer, &DestBuffer);
+ if (!DestBuffer) {
+#ifdef DEBUG
+ if (BytesToCopy)
+ DEBUGCHK;
+#endif
+ break; // Copy is done.
+ }
+ NdisQueryBuffer(DestBuffer, &DestPtr, &DestLength);
+ }
+ }
+
+ *BytesCopied = Copied;
+ return NDIS_STATUS_SUCCESS;
+
+
+}
+
+//* LoopClose - Loopback close routine.
+//
+// This is the loopback close routine. It does nothing but return.
+//
+// Entry: Context - Unused.
+//
+// Returns: Nothing.
+//
+void
+LoopClose(void *Context)
+{
+
+}
+
+//* LoopInvalidate - Invalidate an RCE.
+//
+// The loopback invalidate RCE routine. It also does nothing.
+//
+// Entry: Context - Unused.
+// RCE - Pointer to RCE to be invalidated.
+//
+// Returns: Nothing.
+//
+void
+LoopInvalidate(void *Context, RouteCacheEntry *RCE)
+{
+
+}
+
+//* LoopQInfo - Loopback query information handler.
+//
+// Called when the upper layer wants to query information about the loopback
+// interface.
+//
+// Input: IFContext - Interface context (unused).
+// ID - TDIObjectID for object.
+// Buffer - Buffer to put data into.
+// Size - Pointer to size of buffer. On return, filled with
+// bytes copied.
+// Context - Pointer to context block.
+//
+// Returns: Status of attempt to query information.
+//
+int
+LoopQInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size,
+ void *Context)
+{
+ uint Offset = 0;
+ uint BufferSize = *Size;
+ uint Entity;
+ uint Instance;
+
+
+ Entity = ID->toi_entity.tei_entity;
+ Instance = ID->toi_entity.tei_instance;
+
+ // First, make sure it's possibly an ID we can handle.
+ if (Entity != IF_ENTITY || Instance != LoopInstance) {
+ return TDI_INVALID_REQUEST;
+ }
+
+ *Size = 0; // In case of an error.
+
+ if (ID->toi_type != INFO_TYPE_PROVIDER)
+ return TDI_INVALID_PARAMETER;
+
+ if (ID->toi_class == INFO_CLASS_GENERIC) {
+ if (ID->toi_id == ENTITY_TYPE_ID) {
+ // He's trying to see what type we are.
+ if (BufferSize >= sizeof(uint)) {
+ (void)CopyToNdis(Buffer, (uchar *)&LoopEntityType, sizeof(uint),
+ &Offset);
+ return TDI_SUCCESS;
+ } else
+ return TDI_BUFFER_TOO_SMALL;
+ }
+ return TDI_INVALID_PARAMETER;
+ } else
+ if (ID->toi_class != INFO_CLASS_PROTOCOL)
+ return TDI_INVALID_PARAMETER;
+
+ // If he's asking for MIB statistics, then return them, otherwise fail
+ // the request.
+
+ if (ID->toi_id == IF_MIB_STATS_ID) {
+
+
+ // He's asking for statistics. Make sure his buffer is at least big
+ // enough to hold the fixed part.
+
+ if (BufferSize < IFE_FIXED_SIZE) {
+ return TDI_BUFFER_TOO_SMALL;
+ }
+
+ // He's got enough to hold the fixed part. Copy our IFE structure
+ // into his buffer.
+ Buffer = CopyToNdis(Buffer, (uchar *)&LoopIFE, IFE_FIXED_SIZE, &Offset);
+
+ // See if he has room for the descriptor string.
+ if (BufferSize >= (IFE_FIXED_SIZE + sizeof(LoopName))) {
+ // He has room. Copy it.
+ (void)CopyToNdis(Buffer, LoopName, sizeof(LoopName), &Offset);
+ *Size = IFE_FIXED_SIZE + sizeof(LoopName);
+ return TDI_SUCCESS;
+ } else {
+ // Not enough room to copy the desc. string.
+ *Size = IFE_FIXED_SIZE;
+ return TDI_BUFFER_OVERFLOW;
+ }
+
+ }
+
+ return TDI_INVALID_PARAMETER;
+
+}
+
+//* LoopSetInfo - Loopback set information handler.
+//
+// The loopback set information handler. We support setting of an I/F admin
+// status.
+//
+// Input: Context - Pointer to I/F to set on.
+// ID - The object ID
+// Buffer - Pointer to buffer containing value to set.
+// Size - Size in bytes of Buffer.
+//
+// Returns: Status of attempt to set information.
+//
+int
+LoopSetInfo(void *Context, TDIObjectID *ID, void *Buffer, uint Size)
+{
+ IFEntry *IFE = (IFEntry *)Buffer;
+ uint Entity, Instance, Status;
+
+ Entity = ID->toi_entity.tei_entity;
+ Instance = ID->toi_entity.tei_instance;
+
+ // First, make sure it's possibly an ID we can handle.
+ if (Entity != IF_ENTITY || Instance != LoopInstance) {
+ return TDI_INVALID_REQUEST;
+ }
+
+ if (ID->toi_class != INFO_CLASS_PROTOCOL ||
+ ID->toi_type != INFO_TYPE_PROVIDER) {
+ return TDI_INVALID_PARAMETER;
+ }
+
+ // It's for the I/F level, see if it's for the statistics.
+ if (ID->toi_id == IF_MIB_STATS_ID) {
+ // It's for the stats. Make sure it's a valid size.
+ if (Size >= IFE_FIXED_SIZE) {
+ // It's a valid size. See what he wants to do.
+ Status = IFE->if_adminstatus;
+ if (Status == IF_STATUS_UP || Status == IF_STATUS_DOWN)
+ LoopIFE.if_adminstatus = Status;
+ else
+ if (Status != IF_STATUS_TESTING)
+ return TDI_INVALID_PARAMETER;
+
+ return TDI_SUCCESS;
+
+ } else
+ return TDI_INVALID_PARAMETER;
+ }
+
+ return TDI_INVALID_PARAMETER;
+}
+
+//* LoopAddAddr - Dummy loopback add address routine.
+//
+// Called at init time when we need to initialize ourselves.
+//
+uint
+LoopAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2)
+{
+
+ return TRUE;
+}
+
+//* LoopDelAddr - Dummy loopback del address routine.
+//
+// Called at init time when we need to initialize ourselves.
+//
+uint
+LoopDelAddr(void *Context, uint Type, IPAddr Address, IPMask Mask)
+{
+
+ return TRUE;
+}
+
+#pragma BEGIN_INIT
+
+extern int InitNTE(NetTableEntry *);
+extern int InitInterface(NetTableEntry *);
+
+#ifdef CHICAGO
+#pragma END_INIT
+#pragma code_seg("_LTEXT", "LCODE")
+#endif
+
+//* LoopGetEList - Get the entity list.
+//
+// Called at init time to get an entity list. We fill our stuff in and return.
+//
+// Input: Context - Unused.
+// EntityList - Pointer to entity list to be filled in.
+// Count - Pointer to number of entries in the list.
+//
+// Returns Status of attempt to get the info.
+//
+int
+LoopGetEList(void *Context, TDIEntityID *EntityList, uint *Count)
+{
+ uint ECount;
+ uint MyIFBase;
+ uint i;
+
+ ECount = *Count;
+
+ // Walk down the list, looking for existing IF entities, and
+ // adjust our base instance accordingly.
+
+ MyIFBase = 0;
+ for (i = 0; i < ECount; i++, EntityList++) {
+ if (EntityList->tei_entity == IF_ENTITY)
+ MyIFBase = MAX(MyIFBase, EntityList->tei_instance + 1);
+ }
+
+ // EntityList points to the start of where we want to begin filling in.
+ // Make sure we have enough room.
+
+ if ((ECount + 1) > MAX_TDI_ENTITIES)
+ return FALSE;
+
+ // At this point we've figure out our base instance. Save for later use.
+ LoopInstance = MyIFBase;
+
+ // Now fill it in.
+ EntityList->tei_entity = IF_ENTITY;
+ EntityList->tei_instance = MyIFBase;
+ (*Count)++;
+
+ return TRUE;
+}
+
+#ifdef CHICAGO
+#pragma BEGIN_INIT
+#endif
+
+
+//** InitLoopback - Initialize the loopback NTE.
+//
+// This function initialized the loopback NTE. We set up the the MSS and pointer to
+// the various pseudo-link routines, then call InitNTE and return.
+//
+// Entry: ConfigInfo - Pointer to config. info structure.
+//
+// Returns: TRUE if we initialized, FALSE if we didn't.
+//
+NetTableEntry *
+InitLoopback(IPConfigInfo *ConfigInfo)
+{
+ LLIPBindInfo ARPInfo;
+
+ CTERefillMem();
+ LoopNTE = CTEAllocMem(sizeof(NetTableEntry));
+ if (LoopNTE == NULL)
+ return LoopNTE;
+
+ CTEMemSet(LoopNTE, 0, sizeof(NetTableEntry));
+
+ LoopNTE->nte_addr = LOOPBACK_ADDR;
+ LoopNTE->nte_mask = CLASSA_MASK;
+ LoopNTE->nte_icmpseq = 1;
+ LoopNTE->nte_flags = NTE_VALID | NTE_ACTIVE | NTE_PRIMARY;
+
+ CTEInitLock(&LoopNTE->nte_lock);
+ CTEInitLock(&LoopInterface.ri_if.if_lock);
+ LoopNTE->nte_mss = LOOPBACK_MSS;
+ LoopNTE->nte_if = (Interface *)&LoopInterface;
+ LoopInterface.ri_if.if_lcontext = LoopNTE;
+ LoopInterface.ri_if.if_xmit = LoopXmit;
+ LoopInterface.ri_if.if_transfer = LoopXfer;
+ LoopInterface.ri_if.if_close = LoopClose;
+ LoopInterface.ri_if.if_invalidate = LoopInvalidate;
+ LoopInterface.ri_if.if_qinfo = LoopQInfo;
+ LoopInterface.ri_if.if_setinfo = LoopSetInfo;
+ LoopInterface.ri_if.if_getelist = LoopGetEList;
+ LoopInterface.ri_if.if_addaddr = LoopAddAddr;
+ LoopInterface.ri_if.if_deladdr = LoopDelAddr;
+ LoopInterface.ri_if.if_bcast = IP_LOCAL_BCST;
+ LoopInterface.ri_if.if_speed = 10000000;
+ LoopInterface.ri_if.if_mtu = LOOPBACK_MSS;
+ LoopInterface.ri_if.if_llipflags = LIP_COPY_FLAG;
+#ifdef _PNP_POWER
+ LoopInterface.ri_if.if_refcount = 1;
+ LoopInterface.ri_if.if_pnpcontext = 0;
+#endif
+
+ ARPInfo.lip_mss = LOOPBACK_MSS + sizeof(IPHeader);
+ ARPInfo.lip_index = LoopIndex;
+ ARPInfo.lip_close = LoopClose;
+ ARPInfo.lip_addaddr = LoopAddAddr;
+ ARPInfo.lip_deladdr = LoopDelAddr;
+ ARPInfo.lip_flags = LIP_COPY_FLAG;
+ LoopIndex = NumIF + 1;
+ LoopInterface.ri_if.if_index = LoopIndex;
+ CTEInitEvent(&LoopXmitEvent, LoopXmitRtn);
+ CTEInitLock(&LoopLock);
+ LoopIFE.if_index = LoopIndex;
+ LoopIFE.if_type = IF_TYPE_LOOPBACK;
+ LoopIFE.if_mtu = ARPInfo.lip_mss;
+ LoopIFE.if_speed = 10000000;
+ LoopIFE.if_adminstatus = IF_STATUS_UP;
+ LoopIFE.if_operstatus = IF_STATUS_UP;
+ LoopIFE.if_descrlen = sizeof(LoopName);
+
+ IFList = (Interface *)&LoopInterface;
+ NumIF++;
+
+ NumNTE++;
+
+ if (!InitInterface(LoopNTE))
+ return NULL;
+
+ if (!InitNTE(LoopNTE))
+ return NULL;
+
+ return LoopNTE;
+}
+
+#pragma END_INIT
+
diff --git a/private/ntos/tdi/tcpip/ip/iprcv.c b/private/ntos/tdi/tcpip/ip/iprcv.c
new file mode 100644
index 000000000..791bc58f6
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/iprcv.c
@@ -0,0 +1,1145 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** iprcv.c - IP receive routines.
+//
+// This module contains all receive related IP routines.
+//
+
+
+#include "oscfg.h"
+#include "cxport.h"
+#include "ndis.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "info.h"
+#include "iproute.h"
+#include "ipfilter.h"
+
+extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong);
+
+extern uchar RATimeout;
+extern NDIS_HANDLE BufferPool;
+#if 0
+EXTERNAL_LOCK(PILock)
+#endif
+extern ProtInfo IPProtInfo[]; // Protocol information table.
+extern ProtInfo *LastPI; // Last protinfo structure looked at.
+extern int NextPI; // Next PI field to be used.
+extern ProtInfo *RawPI; // Raw IP protinfo
+extern NetTableEntry *NetTableList; // Pointer to the net table.
+
+DEBUGSTRING(RcvFile, "iprcv.c");
+
+#ifdef CHICAGO
+extern void RefillReasmMem(void);
+#endif
+
+//* FindUserRcv - Find the receive handler to be called for a particular protocol.
+//
+// This functions takes as input a protocol value, and returns a pointer to
+// the receive routine for that protocol.
+//
+// Input: NTE - Pointer to NetTableEntry to be searched
+// Protocol - Protocol to be searched for.
+// UContext - Place to returns UL Context value.
+//
+// Returns: Pointer to the receive routine.
+//
+ULRcvProc
+FindUserRcv(uchar Protocol)
+{
+ ULRcvProc RcvProc;
+ int i;
+#if 0
+ CTELockHandle Handle;
+
+
+ CTEGetLock(&PILock, &Handle);
+#endif
+
+ if (LastPI->pi_protocol == Protocol) {
+ RcvProc = LastPI->pi_rcv;
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+ return RcvProc;
+ }
+
+ RcvProc = (ULRcvProc)NULL;
+ for ( i = 0; i < NextPI; i++) {
+ if (IPProtInfo[i].pi_protocol == Protocol) {
+ LastPI = &IPProtInfo[i];
+ RcvProc = IPProtInfo[i].pi_rcv;
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+ return RcvProc;
+ }
+ }
+
+ //
+ // Didn't find a match. Use the raw protocol if it is registered.
+ //
+ if (RawPI != NULL) {
+ RcvProc = RawPI->pi_rcv;
+ }
+
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+ return RcvProc;
+
+}
+
+//* IPRcvComplete - Handle a receive complete.
+//
+// Called by the lower layer when receives are temporarily done.
+//
+// Entry: Nothing.
+//
+// Returns: Nothing.
+//
+void
+IPRcvComplete(void)
+{
+ void (*ULRcvCmpltProc)(void);
+ int i;
+#if 0
+ CTELockHandle Handle;
+
+
+ CTEGetLock(&PILock, &Handle);
+#endif
+ for (i = 0; i < NextPI; i++) {
+ if ((ULRcvCmpltProc = IPProtInfo[i].pi_rcvcmplt) != NULL) {
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+ (*ULRcvCmpltProc)();
+#if 0
+ CTEGetLock(&PILock, &Handle);
+#endif
+ }
+ }
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+
+}
+//* FindRH - Look up a reassembly header on an NTE.
+//
+// A utility function to look up a reassembly header. We assume the lock on the NTE
+// is taken when we are called. If we find a matching RH we'll take the lock on it.
+// We also return the predeccessor of the RH, for use in insertion or deletion.
+//
+// Input: PrevRH - Place to return pointer to previous RH
+// NTE - NTE to be searched.
+// Dest - Destination IP address
+// Src - Src IP address
+// ID - ID of RH
+// Protocol - Protocol of RH
+//
+// Returns: Pointer to RH, or NULL if none.
+//
+ReassemblyHeader *
+FindRH(ReassemblyHeader **PrevRH, NetTableEntry *NTE, IPAddr Dest, IPAddr Src, ushort Id,
+ uchar Protocol)
+{
+ ReassemblyHeader *TempPrev, *Current;
+
+ TempPrev = STRUCT_OF(ReassemblyHeader, &NTE->nte_ralist, rh_next);
+ Current = NTE->nte_ralist;
+ while (Current != (ReassemblyHeader *)NULL) {
+ if (Current->rh_dest == Dest && Current->rh_src == Src && Current->rh_id == Id &&
+ Current->rh_protocol == Protocol)
+ break;
+ TempPrev = Current;
+ Current = Current->rh_next;
+ }
+
+ *PrevRH = TempPrev;
+ return Current;
+
+}
+
+//* ParseRcvdOptions - Validate incoming options.
+//
+// Called during reception handling to validate incoming options. We make sure that everything
+// is OK as best we can, and find indices for any source route option.
+//
+// Input: OptInfo - Pointer to option info. structure.
+// Index - Pointer to optindex struct to be filled in.
+//
+//
+// Returns: Index of error if any, MAX_OPT_SIZE if no errors.
+//
+uchar
+ParseRcvdOptions(IPOptInfo *OptInfo, OptIndex *Index)
+{
+ uint i= 0; // Index variable.
+ uchar *Options = OptInfo->ioi_options;
+ uint OptLength = (uint)OptInfo->ioi_optlength;
+ uchar Length; // Length of option.
+ uchar Pointer; // Pointer field, for options that use it.
+
+ while(i < OptLength && *Options != IP_OPT_EOL) {
+ if (*Options == IP_OPT_NOP) {
+ i++;
+ Options++;
+ continue;
+ }
+ if (((Length = Options[IP_OPT_LENGTH]) + i) > OptLength) {
+ return (uchar)i + (uchar)IP_OPT_LENGTH; // Length exceeds options length.
+ }
+
+ Pointer = Options[IP_OPT_DATA] - 1;
+
+ if (*Options == IP_OPT_TS) {
+ if (Length < (MIN_TS_PTR - 1))
+ return (uchar)i + (uchar)IP_OPT_LENGTH;
+ Index->oi_tsindex = (uchar)i;
+ } else {
+ if (Length < (MIN_RT_PTR - 1))
+ return (uchar)i + (uchar)IP_OPT_LENGTH;
+
+ if (*Options == IP_OPT_LSRR || *Options == IP_OPT_SSRR) {
+ // A source route option
+ if (Pointer < Length) { // Route not complete
+
+ if ((Length - Pointer) < sizeof(IPAddr))
+ return (uchar)i + (uchar)IP_OPT_LENGTH;
+
+ Index->oi_srtype = *Options;
+ Index->oi_srindex = (uchar)i;
+ }
+ } else {
+ if (*Options == IP_OPT_RR) {
+ if (Pointer < Length)
+ Index->oi_rrindex = (uchar)i;
+ }
+ }
+ }
+
+ i += Length;
+ Options += Length;
+ }
+
+ return MAX_OPT_SIZE;
+}
+
+//* BCastRcv - Receive a broadcast or multicast packet.
+//
+// Called when we have to receive a broadcast packet. We loop through the NTE table,
+// calling the upper layer receive protocol for each net which matches the receive I/F
+// and for which the destination address is a broadcast.
+//
+// Input: RcvProc - The receive procedure to be called.
+// SrcNTE - NTE on which the packet was originally received.
+// DestAddr - Destination address.
+// SrcAddr - Source address of packet.
+// Data - Pointer to received data.
+// DataLength - Size in bytes of data
+// Protocol - Upper layer protocol being called.
+// OptInfo - Pointer to received IP option info.
+//
+// Returns: Nothing.
+//
+void
+BCastRcv(ULRcvProc RcvProc, NetTableEntry *SrcNTE, IPAddr DestAddr,
+ IPAddr SrcAddr, IPHeader UNALIGNED *Header, uint HeaderLength,
+ IPRcvBuf *Data, uint DataLength, uchar Protocol, IPOptInfo *OptInfo)
+{
+ NetTableEntry *CurrentNTE;
+ const Interface *SrcIF = SrcNTE->nte_if;
+ ulong Delivered = 0;
+
+
+ for (CurrentNTE = NetTableList;
+ CurrentNTE != NULL;
+ CurrentNTE = CurrentNTE->nte_next)
+ {
+ if ((CurrentNTE->nte_flags & NTE_ACTIVE) &&
+ (CurrentNTE->nte_if == SrcIF) &&
+ IS_BCAST_DEST(IsBCastOnNTE(DestAddr, CurrentNTE)))
+ {
+ Delivered = 1;
+
+ (*RcvProc)(CurrentNTE, DestAddr, SrcAddr, CurrentNTE->nte_addr,
+ SrcNTE->nte_addr, Header, HeaderLength, Data, DataLength,
+ TRUE, Protocol, OptInfo);
+ }
+ }
+
+ if (Delivered) {
+ IPSInfo.ipsi_indelivers++;
+ }
+}
+
+//* DeliverToUser - Deliver data to a user protocol.
+//
+// This procedure is called when we have determined that an incoming packet belongs
+// here, and any options have been processed. We accept it for upper layer processing,
+// which means looking up the receive procedure and calling it, or passing it to BCastRcv
+// if neccessary.
+//
+// Input: SrcNTE - Pointer to NTE on which packet arrived.
+// DestNTE - Pointer to NTE that is accepting packet.
+// Header - Pointer to IP header of packet.
+// HeaderLength - Length of Header in bytes.
+// Data - Pointer to IPRcvBuf chain.
+// DataLength - Length in bytes of upper layer data.
+// OptInfo - Pointer to Option information for this receive.
+// DestType - Type of destination - LOCAL, BCAST.
+//
+// Returns: Nothing.
+void
+DeliverToUser(NetTableEntry *SrcNTE, NetTableEntry *DestNTE,
+ IPHeader UNALIGNED *Header, uint HeaderLength, IPRcvBuf *Data,
+ uint DataLength, IPOptInfo *OptInfo, uchar DestType)
+{
+ ULRcvProc rcv;
+
+#ifdef DEBUG
+ if (DestType >= DEST_REMOTE)
+ DEBUGCHK;
+#endif
+
+ // Process this request right now. Look up the protocol. If we
+ // find it, copy the data if we need to, and call the protocol's
+ // receive handler. If we don't find it, send an ICMP
+ // 'protocol unreachable' message.
+ rcv = FindUserRcv(Header->iph_protocol);
+ if (rcv != NULL) {
+ IP_STATUS Status;
+
+ if (DestType == DEST_LOCAL) {
+ Status = (*rcv)(SrcNTE,Header->iph_dest, Header->iph_src,
+ DestNTE->nte_addr, SrcNTE->nte_addr, Header,
+ HeaderLength, Data, DataLength, FALSE,
+ Header->iph_protocol, OptInfo);
+
+ if (Status == IP_SUCCESS) {
+ IPSInfo.ipsi_indelivers++;
+ return;
+ }
+
+ if (Status == IP_DEST_PROT_UNREACHABLE) {
+ IPSInfo.ipsi_inunknownprotos++;
+ SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH,
+ PROT_UNREACH, 0);
+ }
+ else {
+ IPSInfo.ipsi_indelivers++;
+ SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH,
+ PORT_UNREACH, 0);
+ }
+
+ return; // Just return out of here now.
+ }
+ else {
+ BCastRcv(rcv, SrcNTE, Header->iph_dest, Header->iph_src,
+ Header, HeaderLength, Data, DataLength,
+ Header->iph_protocol, OptInfo);
+ }
+
+ } else {
+ IPSInfo.ipsi_inunknownprotos++;
+ // If we get here, we didn't find a matching protocol. Send an ICMP message.
+ SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH, PROT_UNREACH, 0);
+ }
+
+}
+
+//* FreeRH - Free a reassembly header.
+//
+// Called when we need to free a reassembly header, either because of a timeout or because
+// we're done with it.
+//
+// Input: RH - RH to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeRH(ReassemblyHeader *RH)
+{
+ RABufDesc *RBD, *TempRBD;
+
+ RBD = RH->rh_rbd;
+ while (RBD != NULL) {
+ TempRBD = RBD;
+ RBD = (RABufDesc *)RBD->rbd_buf.ipr_next;
+ CTEFreeMem(TempRBD);
+ }
+ CTEFreeMem(RH);
+
+}
+
+//* ReassembleFragment - Put a fragment into the reassembly list.
+//
+// This routine is called once we've put a fragment into the proper buffer. We look for
+// a reassembly header for the fragment. If we don't find one, we create one. Otherwise
+// we search the reassembly list, and insert the datagram in it's proper place.
+//
+// Input: NTE - NTE to reassemble on.
+// SrcNTE - NTE datagram arrived on.
+// NewRBD - New RBD to be inserted.
+// IPH - Pointer to header of datagram.
+// HeaderSize - Size in bytes of header.
+// DestType - Type of destination address.
+//
+// Returns: Nothing.
+//
+void
+ReassembleFragment(NetTableEntry *NTE, NetTableEntry *SrcNTE, RABufDesc *NewRBD,
+ IPHeader UNALIGNED *IPH, uint HeaderSize, uchar DestType)
+{
+ CTELockHandle NTEHandle; // Lock handle used for NTE
+ ReassemblyHeader *RH, *PrevRH; // Current and previous reassembly headers.
+ RABufDesc *PrevRBD; // Previous RBD in reassembly header list.
+ RABufDesc *CurrentRBD;
+ ushort DataLength = (ushort)NewRBD->rbd_buf.ipr_size, DataOffset;
+ ushort Offset; // Offset of this fragment.
+ ushort NewOffset; // Offset we'll copy from after checking RBD list.
+ ushort NewEnd; // End offset of fragment, after trimming (if any).
+
+ // If this is a broadcast, go ahead and forward it now.
+ if (IS_BCAST_DEST(DestType))
+ IPForward(SrcNTE, IPH, HeaderSize, NewRBD->rbd_buf.ipr_buffer,
+ NewRBD->rbd_buf.ipr_size, NULL, 0, DestType);
+
+
+ // We've got the buffer we need. Now get the reassembly header, if there is one. If
+ // there isn't, create one.
+ CTEGetLockAtDPC(&NTE->nte_lock, &NTEHandle);
+ RH = FindRH(&PrevRH, NTE, IPH->iph_dest, IPH->iph_src, IPH->iph_id, IPH->iph_protocol);
+ if (RH == (ReassemblyHeader *)NULL) { // Didn't find one, so create one.
+ ReassemblyHeader *NewRH;
+
+ CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle);
+ RH = CTEAllocMem(sizeof(ReassemblyHeader));
+ if (RH == (ReassemblyHeader *)NULL) { // Couldn't get a buffer.
+#ifdef CHICAGO
+ RefillReasmMem();
+#endif
+ IPSInfo.ipsi_reasmfails++;
+ CTEFreeMem(NewRBD);
+ return;
+ }
+
+ CTEGetLockAtDPC(&NTE->nte_lock, &NTEHandle);
+ // Need to look it up again - it could have changed during above call.
+ NewRH = FindRH(&PrevRH, NTE, IPH->iph_dest, IPH->iph_src, IPH->iph_id, IPH->iph_protocol);
+ if (NewRH != (ReassemblyHeader *)NULL) {
+ CTEFreeMem(RH);
+ RH = NewRH;
+ } else {
+
+ RH->rh_next = PrevRH->rh_next;
+ PrevRH->rh_next = RH;
+
+
+ // Initialize our new reassembly header.
+ RH->rh_dest = IPH->iph_dest;
+ RH->rh_src = IPH->iph_src;
+ RH->rh_id = IPH->iph_id;
+ RH->rh_protocol = IPH->iph_protocol;
+ RH->rh_ttl = RATimeout;
+ RH->rh_datasize = 0xffff; // Default datasize to maximum.
+ RH->rh_rbd = (RABufDesc *)NULL; // And nothing on chain.
+ RH->rh_datarcvd = 0; // Haven't received any data yet.
+ RH->rh_headersize = 0;
+
+ }
+ }
+
+ // When we reach here RH points to the reassembly header we want to use.
+ // and we hold locks on the NTE and the RH. If this is the first fragment we'll save
+ // the options and header information here.
+
+ Offset = IPH->iph_offset & IP_OFFSET_MASK;
+ Offset = net_short(Offset) * 8;
+
+ if (Offset == 0) { // First fragment.
+ RH->rh_headersize = HeaderSize;
+ CTEMemCopy(RH->rh_header, IPH, HeaderSize + 8);
+ }
+
+ // If this is the last fragment, update the amount of data we expect to received.
+ if (!(IPH->iph_offset & IP_MF_FLAG))
+ RH->rh_datasize = Offset + DataLength;
+
+ // Update the TTL value with the maximum of the current TTL and the incoming
+ // TTL (+1, to deal with rounding errors).
+ RH->rh_ttl = MAX(RH->rh_ttl, MIN(254, IPH->iph_ttl) + 1);
+
+ // Now we need to see where in the RBD list to put this.
+ //
+ // The idea is to go through the list of RBDs one at a time. The RBD currently
+ // being examined is CurrentRBD. If the start offset of the new fragment is less
+ // than (i.e. in front of) the offset of CurrentRBD, we need to insert the NewRBD
+ // in front of the CurrentRBD. If this is the case we need to check and see if the
+ // end of the new fragment overlaps some or all of the fragment described by
+ // CurrentRBD, and possibly subsequent fragment. If it overlaps part of a fragment
+ // we'll adjust our end down to be in front of the existing fragment. If it overlaps
+ // all of the fragment we'll free the old fragment.
+ //
+ // If the new fragment does not start in front of the current fragment we'll check
+ // to see if it starts somewhere in the middle of the current fragment. If this
+ // isn't the case, we move on the the next fragment. If this is the case, we check
+ // to see if the current fragment completely covers the new fragment. If not we
+ // move our start up and continue with the next fragment.
+
+ NewOffset = Offset;
+ NewEnd = Offset + DataLength - 1;
+ PrevRBD = STRUCT_OF(RABufDesc, STRUCT_OF(IPRcvBuf, &RH->rh_rbd, ipr_next), rbd_buf);
+ CurrentRBD = RH->rh_rbd;
+ for (; CurrentRBD != NULL; PrevRBD = CurrentRBD, CurrentRBD = (RABufDesc *)CurrentRBD->rbd_buf.ipr_next) {
+
+ // See if it starts in front of this fragment.
+ if (NewOffset < CurrentRBD->rbd_start) {
+ // It does start in front. Check to see if there's any overlap.
+
+ if (NewEnd < CurrentRBD->rbd_start)
+ break; // No overlap, so get out.
+ else {
+ // It does overlap. While we have overlap, walk down the list
+ // looking for RBDs we overlap completely. If we find one, put it
+ // on our deletion list. If we have overlap but not complete overlap,
+ // move our end down if front of the fragment we overlap.
+ do {
+ if (NewEnd > CurrentRBD->rbd_end) { // This overlaps completely.
+ RABufDesc *TempRBD;
+
+ RH->rh_datarcvd -= CurrentRBD->rbd_buf.ipr_size;
+ TempRBD = CurrentRBD;
+ CurrentRBD = (RABufDesc *)CurrentRBD->rbd_buf.ipr_next;
+ CTEFreeMem(TempRBD);
+ } else // Only partial ovelap.
+ NewEnd = CurrentRBD->rbd_start - 1;
+ // Update of NewEnd will force us out of loop.
+
+ } while (CurrentRBD != NULL && NewEnd >= CurrentRBD->rbd_start);
+ break;
+ }
+ } else {
+ // This fragment doesn't go in front of the current RBD. See if it is
+ // entirely beyond the end of the current fragment. If it is, just
+ // continue. Otherwise see if the current fragment completely subsumes
+ // us. If it does, get out, otherwise update our start offset and
+ // continue.
+
+ if (NewOffset > CurrentRBD->rbd_end)
+ continue; // No overlap at all.
+ else {
+ if (NewEnd <= CurrentRBD->rbd_end) {
+ // The current fragment overlaps the new fragment totally. Set
+ // our offsets so that we'll skip the copy below.
+ NewEnd = NewOffset - 1;
+ break;
+ } else // Only partial overlap.
+ NewOffset = CurrentRBD->rbd_end + 1;
+ }
+ }
+ } // End of for loop.
+
+ // Adjust the length and offset fields in the new RBD.
+ DataLength = NewEnd - NewOffset + 1;
+ DataOffset = NewOffset - Offset;
+ // Link him in chain.
+ NewRBD->rbd_buf.ipr_size = (uint)DataLength;
+ NewRBD->rbd_end = NewEnd;
+ NewRBD->rbd_start = NewOffset;
+ RH->rh_datarcvd += DataLength;
+ NewRBD->rbd_buf.ipr_buffer += DataOffset;
+ NewRBD->rbd_buf.ipr_next = (IPRcvBuf *)CurrentRBD;
+ PrevRBD->rbd_buf.ipr_next = &NewRBD->rbd_buf;
+
+ // If we've received all the data, deliver it to the user.
+ if (RH->rh_datarcvd == RH->rh_datasize) { // We have it all.
+ IPOptInfo OptInfo;
+ IPHeader *Header;
+ IPRcvBuf *FirstBuf;
+
+ PrevRH->rh_next = RH->rh_next;
+ CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle);
+ Header = (IPHeader *)RH->rh_header;
+ OptInfo.ioi_ttl = Header->iph_ttl;
+ OptInfo.ioi_tos = Header->iph_tos;
+ OptInfo.ioi_flags = 0; // Flags must be 0 - DF can't be set, this was reassembled.
+
+ if (RH->rh_headersize != sizeof(IPHeader)) { // We had options.
+ OptInfo.ioi_options = (uchar *)(Header + 1);
+ OptInfo.ioi_optlength = RH->rh_headersize - sizeof(IPHeader);
+ } else {
+ OptInfo.ioi_options = (uchar *)NULL;
+ OptInfo.ioi_optlength = 0;
+ }
+
+ // Make sure that the first buffer contains enough data.
+ FirstBuf = (IPRcvBuf *)RH->rh_rbd;
+ while (FirstBuf->ipr_size < MIN_FIRST_SIZE) {
+ IPRcvBuf *NextBuf = FirstBuf->ipr_next;
+ uint CopyLength;
+
+ if (NextBuf == NULL)
+ break;
+
+ CopyLength = MIN(MIN_FIRST_SIZE - FirstBuf->ipr_size,
+ NextBuf->ipr_size);
+ CTEMemCopy(FirstBuf->ipr_buffer + FirstBuf->ipr_size,
+ NextBuf->ipr_buffer, CopyLength);
+ FirstBuf->ipr_size += CopyLength;
+ NextBuf->ipr_buffer += CopyLength;
+ NextBuf->ipr_size -= CopyLength;
+ if (NextBuf->ipr_size == 0) {
+ FirstBuf->ipr_next = NextBuf->ipr_next;
+ CTEFreeMem(NextBuf);
+ }
+ }
+
+ IPSInfo.ipsi_reasmoks++;
+ DeliverToUser(SrcNTE, NTE, Header, RH->rh_headersize, FirstBuf,
+ RH->rh_datasize, &OptInfo, DestType);
+ FreeRH(RH);
+ } else
+ CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle);
+
+
+}
+
+//* RATDComplete - Completion routing for a reassembly transfer data.
+//
+// This is the completion handle for TDs invoked because we are reassembling a fragment.
+//
+// Input: NetContext - Pointer to the net table entry on which we received this.
+// Packet - Packet we received into.
+// Status - Final status of copy.
+// DataSize - Size in bytes of data transferred.
+//
+// Returns: Nothing
+//
+void
+RATDComplete(void *NetContext, PNDIS_PACKET Packet, NDIS_STATUS Status, uint DataSize)
+{
+ NetTableEntry *NTE = (NetTableEntry *)NetContext;
+ Interface *SrcIF;
+ TDContext *Context = (TDContext *)Packet->ProtocolReserved;
+ CTELockHandle Handle;
+ PNDIS_BUFFER Buffer;
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+ Context->tdc_rbd->rbd_buf.ipr_size = DataSize;
+ ReassembleFragment(Context->tdc_nte, NTE, Context->tdc_rbd,
+ (IPHeader *)Context->tdc_header, Context->tdc_hlength, Context->tdc_dtype);
+ }
+
+ NdisUnchainBufferAtFront(Packet, &Buffer);
+ NdisFreeBuffer(Buffer);
+ Context->tdc_common.pc_flags &= ~PACKET_FLAG_RA;
+ SrcIF = NTE->nte_if;
+ CTEGetLockAtDPC(&SrcIF->if_lock, &Handle);
+
+ Context->tdc_common.pc_link = SrcIF->if_tdpacket;
+ SrcIF->if_tdpacket = Packet;
+ CTEFreeLockFromDPC(&SrcIF->if_lock, Handle);
+
+ return;
+
+}
+
+//* IPReassemble - Reassemble an incoming datagram.
+//
+// Called when we receive an incoming fragment. The first thing we do is get a buffer
+// to put the fragment in. If we can't we'll exit. Then we copy the data, either via
+// transfer data or directly if it all fits.
+//
+// Input: SrcNTE - Pointer to NTE that received the datagram.
+// NTE - Pointer to NTE on which to reassemble.
+// IPH - Pointer to header of packet.
+// HeaderSize - Size in bytes of header.
+// Data - Pointer to data part of fragment.
+// BufferLength - Length in bytes of user data available in the buffer.
+// DataLength - Length in bytes of the (upper-layer) data.
+// DestType - Type of destination
+// LContext1, LContext2 - Link layer context values.
+//
+// Returns: Nothing.
+//
+void
+IPReassemble(NetTableEntry *SrcNTE, NetTableEntry *NTE, IPHeader UNALIGNED *IPH,
+ uint HeaderSize,
+ uchar *Data, uint BufferLength, uint DataLength, uchar DestType, NDIS_HANDLE LContext1,
+ uint LContext2)
+{
+ Interface *RcvIF;
+ PNDIS_PACKET TDPacket; // NDIS packet used for TD.
+ TDContext *TDC = (TDContext *)NULL; // Transfer data context.
+ NDIS_STATUS Status;
+ PNDIS_BUFFER Buffer;
+ RABufDesc *NewRBD; // Pointer to new RBD to hold arriving fragment.
+ CTELockHandle Handle;
+ uint AllocSize;
+
+ IPSInfo.ipsi_reasmreqds++;
+
+ // First, get a new RBD to hold the arriving fragment. If we can't, then just skip
+ // the rest. The RBD has the buffer implicitly at the end of it. The buffer for the
+ // first fragment must be at least MIN_FIRST_SIZE bytes.
+ if ((IPH->iph_offset & IP_OFFSET_MASK) == 0)
+ AllocSize = MAX(MIN_FIRST_SIZE, DataLength);
+ else
+ AllocSize = DataLength;
+
+ NewRBD = CTEAllocMem(sizeof(RABufDesc) + AllocSize);
+
+ if (NewRBD != (RABufDesc *)NULL) {
+
+ NewRBD->rbd_buf.ipr_buffer = (uchar *)(NewRBD + 1);
+ NewRBD->rbd_buf.ipr_size = DataLength;
+ NewRBD->rbd_buf.ipr_owner = IPR_OWNER_IP;
+
+ // Copy the data into the buffer. If we need to call transfer data do so now.
+ if (DataLength > BufferLength) { // Need to call transfer data.
+ NdisAllocateBuffer(&Status, &Buffer, BufferPool, NewRBD + 1, DataLength);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ IPSInfo.ipsi_reasmfails++;
+ CTEFreeMem(NewRBD);
+ return;
+ }
+
+ // Now get a packet for transferring the frame.
+ RcvIF = SrcNTE->nte_if;
+ CTEGetLockAtDPC(&RcvIF->if_lock, &Handle);
+ TDPacket = RcvIF->if_tdpacket;
+
+ if (TDPacket != (PNDIS_PACKET)NULL) {
+
+ TDC = (TDContext *)TDPacket->ProtocolReserved;
+ RcvIF->if_tdpacket = TDC->tdc_common.pc_link;
+ CTEFreeLockFromDPC(&RcvIF->if_lock, Handle);
+
+ TDC->tdc_common.pc_flags |= PACKET_FLAG_RA;
+ TDC->tdc_nte = NTE;
+ TDC->tdc_dtype = DestType;
+ TDC->tdc_hlength = (uchar)HeaderSize;
+ TDC->tdc_rbd = NewRBD;
+ CTEMemCopy(TDC->tdc_header, IPH, HeaderSize + 8);
+ NdisChainBufferAtFront(TDPacket, Buffer);
+ Status = (*(RcvIF->if_transfer))(RcvIF->if_lcontext, LContext1, LContext2,
+ HeaderSize, DataLength, TDPacket, &DataLength);
+ if (Status != NDIS_STATUS_PENDING)
+ RATDComplete(SrcNTE, TDPacket, Status, DataLength);
+ else
+ return;
+ } else { // Couldn't get a TD packet.
+ CTEFreeLockFromDPC(&RcvIF->if_lock, Handle);
+ CTEFreeMem(NewRBD);
+ IPSInfo.ipsi_reasmfails++;
+ return;
+ }
+ } else { // It all fits, copy it.
+ CTEMemCopy(NewRBD + 1, Data, DataLength);
+ ReassembleFragment(NTE, SrcNTE, NewRBD, IPH, HeaderSize, DestType);
+ }
+ } else {
+#ifdef CHICAGO
+ RefillReasmMem();
+#endif
+
+ IPSInfo.ipsi_reasmfails++;
+ }
+
+ return;
+}
+
+
+
+//* CheckLocalOptions - Check the options received with a packet.
+//
+// A routine called when we've received a packet for this host and want to examine
+// it for options. We process the options, and return TRUE or FALSE depending on whether
+// or not it's for us.
+//
+// Input: SrcNTE - Pointer to NTE this came in on.
+// Header - Pointer to incoming header.
+// OptInfo - Place to put opt info.
+// DestType - Type of incoming packet.
+//
+// Returns: DestType - Local or remote.
+//
+uchar
+CheckLocalOptions(NetTableEntry *SrcNTE, IPHeader UNALIGNED *Header,
+ IPOptInfo *OptInfo, uchar DestType)
+{
+ uint HeaderLength; // Length in bytes of header.
+ OptIndex Index;
+ uchar ErrIndex;
+
+#ifdef DEBUG
+ if (DestType >= DEST_REMOTE)
+ DEBUGCHK;
+#endif
+
+
+ HeaderLength = (Header->iph_verlen & (uchar)~IP_VER_FLAG) << 2;
+
+#ifdef DEBUG
+ if (HeaderLength <= sizeof(IPHeader))
+ DEBUGCHK;
+#endif
+
+ OptInfo->ioi_options = (uchar *)(Header + 1);
+ OptInfo->ioi_optlength = HeaderLength - sizeof(IPHeader);
+
+
+ // We have options of some sort. The packet may or may not be bound for us.
+ Index.oi_srindex = MAX_OPT_SIZE;
+ if ((ErrIndex = ParseRcvdOptions(OptInfo, &Index)) < MAX_OPT_SIZE) {
+ SendICMPErr(SrcNTE->nte_addr, Header, ICMP_PARAM_PROBLEM, PTR_VALID,
+ ((ulong)ErrIndex + sizeof(IPHeader)));
+ return DEST_INVALID; // Parameter error.
+ }
+
+ // If there's no source route, or if the destination is a broadcast, we'll take
+ // it. If it is a broadcast DeliverToUser will forward it when it's done, and
+ // the forwarding code will reprocess the options.
+ if (Index.oi_srindex == MAX_OPT_SIZE || IS_BCAST_DEST(DestType))
+ return DEST_LOCAL;
+ else
+ return DEST_REMOTE;
+
+}
+
+//* TDUserRcv - Completion routing for a user transfer data.
+//
+// This is the completion handle for TDs invoked because we need to give data to a
+// upper layer client. All we really do is call the upper layer handler with
+// the data.
+//
+// Input: NetContext - Pointer to the net table entry on which we received this.
+// Packet - Packet we received into.
+// Status - Final status of copy.
+// DataSize - Size in bytes of data transferred.
+//
+// Returns: Nothing
+//
+void
+TDUserRcv(void *NetContext, PNDIS_PACKET Packet, NDIS_STATUS Status,
+ uint DataSize)
+{
+ NetTableEntry *NTE = (NetTableEntry *)NetContext;
+ Interface *SrcIF;
+ TDContext *Context = (TDContext *)Packet->ProtocolReserved;
+ CTELockHandle Handle;
+ uchar DestType;
+ IPRcvBuf RcvBuf;
+ IPOptInfo OptInfo;
+ IPHeader *Header;
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+ Header = (IPHeader *)Context->tdc_header;
+ OptInfo.ioi_ttl = Header->iph_ttl;
+ OptInfo.ioi_tos = Header->iph_tos;
+ OptInfo.ioi_flags = (net_short(Header->iph_offset) >> 13) & IP_FLAG_DF;
+ if (Context->tdc_hlength != sizeof(IPHeader)) {
+ OptInfo.ioi_options = (uchar *)(Header + 1);
+ OptInfo.ioi_optlength = Context->tdc_hlength - sizeof(IPHeader);
+ } else {
+ OptInfo.ioi_options = (uchar *)NULL;
+ OptInfo.ioi_optlength = 0;
+ }
+
+ DestType = Context->tdc_dtype;
+ RcvBuf.ipr_next = NULL;
+ RcvBuf.ipr_owner = IPR_OWNER_IP;
+ RcvBuf.ipr_buffer = (uchar *)Context->tdc_buffer;
+ RcvBuf.ipr_size = DataSize;
+
+ DeliverToUser(NTE, Context->tdc_nte, Header, Context->tdc_hlength,
+ &RcvBuf, DataSize, &OptInfo, DestType);
+ // If it's a broadcast packet forward it on.
+ if (IS_BCAST_DEST(DestType))
+ IPForward(NTE, Header, Context->tdc_hlength, RcvBuf.ipr_buffer, DataSize,
+ NULL, 0, DestType);
+ }
+
+ SrcIF = NTE->nte_if;
+ CTEGetLockAtDPC(&SrcIF->if_lock, &Handle);
+
+ Context->tdc_common.pc_link = SrcIF->if_tdpacket;
+ SrcIF->if_tdpacket = Packet;
+ CTEFreeLockFromDPC(&SrcIF->if_lock, Handle);
+
+ return;
+
+}
+
+
+//* IPRcv - Receive an incoming IP datagram.
+//
+// This is the routine called by the link layer module when an incoming IP
+// datagram is to be processed. We validate the datagram (including doing
+// the xsum), copy and process incoming options, and decide what to do with it.
+//
+// Entry: MyContext - The context valued we gave to the link layer.
+// Data - Pointer to the data buffer.
+// DataSize - Size in bytes of the data buffer.
+// TotalSize - Total size in bytes available.
+// LContext1 - 1st link context.
+// LContext2 - 2nd link context.
+// BCast - Indicates whether or not packet was received on bcast address.
+//
+// Returns: Nothing.
+//
+void
+IPRcv(void *MyContext, void *Data, uint DataSize, uint TotalSize, NDIS_HANDLE LContext1,
+ uint LContext2, uint BCast)
+{
+ IPHeader UNALIGNED *IPH = (IPHeader UNALIGNED *)Data;
+ NetTableEntry *NTE = (NetTableEntry *)MyContext; // Local NTE received on
+ NetTableEntry *DestNTE; // NTE to receive on.
+ Interface *RcvIF; // Interface corresponding to NTE.
+ CTELockHandle Handle;
+ PNDIS_PACKET TDPacket; // NDIS packet used for TD.
+ TDContext *TDC = (TDContext *)NULL; // Transfer data context.
+ NDIS_STATUS Status;
+ IPAddr DAddr; // Dest. IP addr. of received packet.
+ uint HeaderLength; // Size in bytes of received header.
+ uint IPDataLength; // Length in bytes of IP (including UL) data in packet.
+ IPOptInfo OptInfo; // Incoming header information.
+ uchar DestType; // Type (LOCAL, REMOTE, SR) of Daddr.
+ IPRcvBuf RcvBuf;
+
+#if 0
+ CTECheckMem(RcvFile); // Check heap status.
+#endif
+
+ IPSInfo.ipsi_inreceives++;
+
+ // Make sure we actually have data.
+ if (DataSize) {
+
+ // Check the header length, the xsum and the version. If any of these
+ // checks fail silently discard the packet.
+ HeaderLength = ((IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2);
+ if (HeaderLength >= sizeof(IPHeader) && HeaderLength <= DataSize &&
+ xsum(Data, HeaderLength) == (ushort)0xffff) {
+
+ // Check the version, and sanity check the total length.
+ IPDataLength = (uint)net_short(IPH->iph_length);
+ if ((IPH->iph_verlen & IP_VER_FLAG) == IP_VERSION &&
+ IPDataLength > sizeof(IPHeader) && IPDataLength <= TotalSize) {
+
+ IPDataLength -= HeaderLength;
+ Data = (uchar *)Data + HeaderLength;
+ DataSize -= HeaderLength;
+
+ DAddr = IPH->iph_dest;
+ DestNTE = NTE;
+
+ // Find local NTE, if any.
+ DestType = GetLocalNTE(DAddr, &DestNTE);
+
+ // Check to see if this is a non-broadcast IP address that
+ // came in as a link layer broadcast. If it is, throw it out.
+ // This is an important check for DHCP, since if we're
+ // DHCPing an interface all otherwise unknown addresses will
+ // come in as DEST_LOCAL. This check here will throw them out
+ // if they didn't come in as unicast.
+
+ if (BCast && !IS_BCAST_DEST(DestType)) {
+ IPSInfo.ipsi_inhdrerrors++;
+ return; // Non bcast packet on bcast address.
+ }
+
+ OptInfo.ioi_ttl = IPH->iph_ttl;
+ OptInfo.ioi_tos = IPH->iph_tos;
+ OptInfo.ioi_flags = (net_short(IPH->iph_offset) >> 13) &
+ IP_FLAG_DF;
+ OptInfo.ioi_options = (uchar *)NULL;
+ OptInfo.ioi_optlength = 0;
+
+ if (DestType < DEST_REMOTE) {
+ // It's either local or some sort of broadcast.
+
+ // The data probably belongs at this station. If there
+ // aren't any options, it definetly belongs here, and we'll
+ // dispatch it either to our reasssmbly code or to the
+ // deliver to user code. If there are options, we'll check
+ // them and then either handle the packet locally or pass it
+ // to our forwarding code.
+
+ if (HeaderLength != sizeof(IPHeader)) {
+ // We have options.
+
+ uchar NewDType;
+ NewDType = CheckLocalOptions(NTE, IPH, &OptInfo,
+ DestType);
+ if (NewDType != DEST_LOCAL) {
+ if (NewDType == DEST_REMOTE)
+ goto forward;
+ else {
+ IPSInfo.ipsi_inhdrerrors++;
+ return; // Bad Options.
+ }
+ }
+ }
+
+ // Before we go further, if we have a filter installed
+ // call it to see if we should take this.
+
+ if (ForwardFilterPtr != NULL) {
+ FORWARD_ACTION Action;
+
+ Action = (*ForwardFilterPtr)(IPH,
+ Data,
+ DataSize,
+ NTE->nte_if->if_filtercontext,
+ NULL);
+
+ if (Action != FORWARD) {
+ IPSInfo.ipsi_indiscards++;
+ return;
+ }
+ }
+
+ // No options. See if it's a fragment. If it is, call our
+ // reassembly handler.
+ if ((IPH->iph_offset & ~(IP_DF_FLAG | IP_RSVD_FLAG)) == 0) {
+
+ // We don't have a fragment. If the data all fits,
+ // handle it here. Otherwise transfer data it.
+
+#ifdef VXD
+ if (IPDataLength > DataSize) {
+ // Data isn't all in the buffer.
+#else
+ // Make sure data is all in buffer, and directly
+ // accesible.
+ if ((IPDataLength > DataSize) ||
+ !(NTE->nte_flags & NTE_COPY)) {
+#endif
+ // The data isn't all here. Transfer data it.
+ RcvIF = NTE->nte_if;
+ CTEGetLockAtDPC(&RcvIF->if_lock, &Handle);
+ TDPacket = RcvIF->if_tdpacket;
+
+ if (TDPacket != (PNDIS_PACKET)NULL) {
+
+ TDC = (TDContext *)TDPacket->ProtocolReserved;
+ RcvIF->if_tdpacket = TDC->tdc_common.pc_link;
+ CTEFreeLockFromDPC(&RcvIF->if_lock, Handle);
+
+ TDC->tdc_nte = DestNTE;
+ TDC->tdc_dtype = DestType;
+ TDC->tdc_hlength = (uchar)HeaderLength;
+ CTEMemCopy(TDC->tdc_header, IPH,
+ HeaderLength + 8);
+ Status = (*(RcvIF->if_transfer))(
+ RcvIF->if_lcontext, LContext1,
+ LContext2, HeaderLength, IPDataLength,
+ TDPacket, &IPDataLength);
+
+ // Check the status. If it's success, call the
+ // receive procedure. Otherwise, if it's pending
+ // wait for the callback.
+ Data = TDC->tdc_buffer;
+ if (Status != NDIS_STATUS_PENDING) {
+ if (Status != NDIS_STATUS_SUCCESS) {
+ IPSInfo.ipsi_indiscards++;
+ CTEGetLockAtDPC(&RcvIF->if_lock, &Handle);
+ TDC->tdc_common.pc_link =
+ RcvIF->if_tdpacket;
+ RcvIF->if_tdpacket = TDPacket;
+ CTEFreeLockFromDPC(&RcvIF->if_lock,
+ Handle);
+ return;
+ }
+ } else
+ return; // Status is pending.
+ } else { // Couldn't get a packet.
+ IPSInfo.ipsi_indiscards++;
+ CTEFreeLockFromDPC(&RcvIF->if_lock, Handle);
+ return;
+ }
+ }
+
+ RcvBuf.ipr_next = NULL;
+ RcvBuf.ipr_owner = IPR_OWNER_IP;
+ RcvBuf.ipr_buffer = (uchar *)Data;
+ RcvBuf.ipr_size = IPDataLength;
+ // When we get here, we have the whole packet. Deliver
+ // it.
+ DeliverToUser(NTE, DestNTE, IPH, HeaderLength, &RcvBuf,
+ IPDataLength, &OptInfo, DestType);
+ // When we're here, we're through with the packet
+ // locally. If it's a broadcast packet forward it on.
+ if (IS_BCAST_DEST(DestType)) {
+ IPForward(NTE, IPH, HeaderLength, Data, IPDataLength,
+ NULL, 0, DestType);
+ }
+ if (TDC != NULL) {
+ CTEGetLockAtDPC(&RcvIF->if_lock, &Handle);
+ TDC->tdc_common.pc_link = RcvIF->if_tdpacket;
+ RcvIF->if_tdpacket = TDPacket;
+ CTEFreeLockFromDPC(&RcvIF->if_lock, Handle);
+ }
+ return;
+ } else {
+ // This is a fragment. Reassemble it.
+ IPReassemble(NTE, DestNTE, IPH, HeaderLength, Data,
+ DataSize, IPDataLength, DestType, LContext1,
+ LContext2);
+ return;
+ }
+
+ }
+
+ // Not for us, may need to be forwarded. It might be an outgoing
+ // broadcast that came in through a source route, so we need to
+ // check that.
+forward:
+ if (DestType != DEST_INVALID)
+ IPForward(NTE, IPH, HeaderLength, Data, DataSize,
+ LContext1, LContext2, DestType);
+ else
+ IPSInfo.ipsi_inaddrerrors++;
+ return;
+ } // Bad version
+ } // Bad checksum
+
+ } // No data
+
+ IPSInfo.ipsi_inhdrerrors++;
+}
+
+//* IPTDComplete - IP Transfer data complete handler.
+//
+// This is the routine called by the link layer when a transfer data completes.
+//
+// Entry: MyContext - Context value we gave to the link layer.
+// Packet - Packet we originally gave to transfer data.
+// Status - Final status of command.
+// BytesCopied - Number of bytes copied.
+//
+// Exit: Nothing
+//
+void
+IPTDComplete(void *MyContext, PNDIS_PACKET Packet, NDIS_STATUS Status, uint BytesCopied)
+{
+ TDContext *TDC = (TDContext *)Packet->ProtocolReserved;
+
+ if (!(TDC->tdc_common.pc_flags & PACKET_FLAG_FW))
+ if (!(TDC->tdc_common.pc_flags & PACKET_FLAG_RA))
+ TDUserRcv(MyContext, Packet, Status, BytesCopied);
+ else
+ RATDComplete(MyContext, Packet, Status, BytesCopied);
+ else
+ SendFWPacket(Packet, Status, BytesCopied);
+
+
+}
diff --git a/private/ntos/tdi/tcpip/ip/iproute.c b/private/ntos/tdi/tcpip/ip/iproute.c
new file mode 100644
index 000000000..3a0bb109d
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/iproute.c
@@ -0,0 +1,4703 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1995 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** iproute.c - IP routing routines.
+//
+// This file contains all the routines related to IP routing, including
+// routing table lookup and management routines.
+
+#include "oscfg.h"
+#include "cxport.h"
+#include "ndis.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "ipinit.h"
+#include "info.h"
+#include "tdistat.h"
+#include "iproute.h"
+#include "iprtdef.h"
+#include "ipxmit.h"
+#include "igmp.h"
+#include "tdiinfo.h"
+#include "ipfilter.h"
+
+extern NetTableEntry *NetTableList; // Pointer to the net table.
+extern NetTableEntry *DHCPNTE; // Pointer to NTE being DHCP'd.
+
+extern NetTableEntry *LoopNTE; // Pointer to loopback NTE.
+extern Interface LoopInterface; // Pointer to loopback interface.
+
+extern AddrTypeCache ATCache[];
+extern int ATCIndex;
+
+extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong);
+extern uchar ParseRcvdOptions(IPOptInfo *, OptIndex *);
+extern void ULMTUNotify(IPAddr Dest, IPAddr Src, uchar Prot, void *Ptr,
+ uint NewMTU);
+
+extern Interface *IFList;
+extern Interface *FirstIF;
+
+#define ROUTE_TABLE_SIZE 32 // Size of the route table.
+DEFINE_LOCK_STRUCTURE(RouteTableLock)
+
+#define FWPACKET_GROW_AMOUNT 20
+
+#define FW_BUF_SIZE 256 // Size of a forwarding buffer.
+
+#define FW_BUF_GROW_AMOUNT 30720 // Enough for 20 Ethernet packets.
+
+#define NO_SR 0
+
+RouteTableEntry *RouteTable[ROUTE_TABLE_SIZE];
+
+DEFINE_LOCK_STRUCTURE(FWPacketFreeLock)
+DEFINE_LOCK_STRUCTURE(FWBufFreeLock)
+
+PNDIS_PACKET FWPacketFree; // Free list of forwarding packets.
+PNDIS_BUFFER FWBufFree; // Free list of forwarding buffers.
+
+uint MaxFWPackets; // Maximum number of forward packets allowed.
+uint CurrentFWPackets; // Number of forwarding packets currently
+ // allocated.
+uint MaxFWBufferSize; // Maximum number of forwarding buffers allowed.
+uint CurrentFWBufferSize; // Number of forwarding buffers allocated.
+
+uchar ForwardPackets; // Flag indicating whether we should forward.
+uchar RouterConfigured; // TRUE if we were initially
+ // configured as a router.
+uchar ForwardBCast; // Flag indicating if we should forward bcasts.
+RouteSendQ *BCastRSQ;
+
+uint DefGWConfigured; // Number of default gateways configed.
+uint DefGWActive; // Number of def. gateways active.
+
+uint DeadGWDetect;
+uint PMTUDiscovery;
+
+ProtInfo *RtPI = NULL;
+
+IPMask IPMaskTable[] = {
+ CLASSA_MASK,
+ CLASSA_MASK,
+ CLASSA_MASK,
+ CLASSA_MASK,
+ CLASSA_MASK,
+ CLASSA_MASK,
+ CLASSA_MASK,
+ CLASSA_MASK,
+ CLASSB_MASK,
+ CLASSB_MASK,
+ CLASSB_MASK,
+ CLASSB_MASK,
+ CLASSC_MASK,
+ CLASSC_MASK,
+ CLASSD_MASK,
+ CLASSE_MASK };
+
+extern void TransmitFWPacket(PNDIS_PACKET, uint);
+
+uint MTUTable[] = {
+
+ 65535 - sizeof(IPHeader),
+ 32000 - sizeof(IPHeader),
+ 17914 - sizeof(IPHeader),
+ 8166 - sizeof(IPHeader),
+ 4352 - sizeof(IPHeader),
+ 2002 - sizeof(IPHeader),
+ 1492 - sizeof(IPHeader),
+ 1006 - sizeof(IPHeader),
+ 508 - sizeof(IPHeader),
+ 296 - sizeof(IPHeader),
+ MIN_VALID_MTU - sizeof(IPHeader)
+};
+
+CTETimer IPRouteTimer;
+
+// Pointer to callout routine for dial on demand.
+IPMapRouteToInterfacePtr DODCallout;
+
+// Pointer to packet filter handler.
+IPPacketFilterPtr ForwardFilterPtr;
+
+RouteInterface DummyInterface; // Dummy interface.
+
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+//
+// Make init code disposable.
+//
+int InitRouting(IPConfigInfo *ci);
+
+#pragma alloc_text(INIT, InitRouting)
+
+#endif // ALLOC_PRAGMA
+#endif // NT
+
+
+#define InsertAfterRTE(P, R) (R)->rte_next = (P)->rte_next;\
+ (P)->rte_next = (R)
+
+#define InsertRTE(R) {\
+ RouteTableEntry *__P__; \
+ __P__ = FindInsertPoint((R)); \
+ InsertAfterRTE(__P__, (R)); \
+ }
+
+#define RemoveRTE(P, R) (P)->rte_next = (R)->rte_next;
+
+//** DuumyXmit - Dummy interface transmit handler.
+//
+// A dummy routine that should never be called.
+//
+// Entry: Context - NULL.
+// Packet - Pointer to packet to be transmitted.
+// Dest - Destination addres of packet.
+// RCE - Pointer to RCE (should be NULL).
+//
+// Returns: NDIS_STATUS_PENDING
+//
+NDIS_STATUS
+DummyXmit(void *Context, PNDIS_PACKET Packet, IPAddr Dest, RouteCacheEntry *RCE)
+{
+ DbgPrint("TCPIP: Dummy Xmit called - NOT GOOD\n");
+ CTEAssert(FALSE);
+ return NDIS_STATUS_SUCCESS;
+}
+
+//* DummyXfer - Dummy interface transfer data routine.
+//
+// A dummy routine that should never be called.
+//
+// Entry: Context - NULL.
+// TDContext - Original packet that was sent.
+// Dummy - Unused
+// Offset - Offset in frame from which to start copying.
+// BytesToCopy - Number of bytes to copy.
+// DestPacket - Packet describing buffer to copy into.
+// BytesCopied - Place to return bytes copied.
+//
+// Returns: NDIS_STATUS_SUCCESS
+//
+NDIS_STATUS
+DummyXfer(void *Context, NDIS_HANDLE TDContext, uint Dummy, uint Offset, uint BytesToCopy,
+ PNDIS_PACKET DestPacket, uint *BytesCopied)
+{
+ DbgPrint("TCPIP: DummyXfer called - NOT GOOD\n");
+
+ CTEAssert(FALSE);
+
+ return NDIS_STATUS_FAILURE;
+}
+
+//* DummyClose - Dummy close routine.
+//
+// A dummy routine that should never be called.
+//
+// Entry: Context - Unused.
+//
+// Returns: Nothing.
+//
+void
+DummyClose(void *Context)
+{
+ DbgPrint("TCPIP: Dummy Close called - NOT GOOD\n");
+
+ CTEAssert(FALSE);
+}
+
+//* DummyInvalidate - .
+//
+// A dummy routine that should never be called.
+//
+// Entry: Context - Unused.
+// RCE - Pointer to RCE to be invalidated.
+//
+// Returns: Nothing.
+//
+void
+DummyInvalidate(void *Context, RouteCacheEntry *RCE)
+{
+ DbgPrint("TCPIP: Dummy Invalidate called - NOT GOOD\n");
+
+ CTEAssert(FALSE);
+
+}
+
+//* DummyQInfo - Dummy query information handler.
+//
+// A dummy routine that should never be called.
+//
+// Input: IFContext - Interface context (unused).
+// ID - TDIObjectID for object.
+// Buffer - Buffer to put data into.
+// Size - Pointer to size of buffer. On return, filled with
+// bytes copied.
+// Context - Pointer to context block.
+//
+// Returns: Status of attempt to query information.
+//
+int
+DummyQInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size,
+ void *Context)
+{
+ DbgPrint("TCPIP: DummyQInfo called - NOT GOOD\n");
+
+ CTEAssert(FALSE);
+
+ return TDI_INVALID_REQUEST;
+}
+
+//* DummySetInfo - Dummy query information handler.
+//
+// A dummy routine that should never be called.
+//
+// Input: IFContext - Interface context (unused).
+// ID - TDIObjectID for object.
+// Buffer - Buffer to put data into.
+// Size - Pointer to size of buffer. On return, filled with
+// bytes copied.
+//
+// Returns: Status of attempt to query information.
+//
+int
+DummySetInfo(void *IFContext, TDIObjectID *ID, void *Buffer, uint Size)
+{
+ DbgPrint("TCPIP: DummySetInfo called - NOT GOOD\n");
+
+ CTEAssert(FALSE);
+
+ return TDI_INVALID_REQUEST;
+}
+
+//* DummyAddAddr - Dummy add address routine.
+//
+// Called at init time when we need to initialize ourselves.
+//
+uint
+DummyAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2)
+{
+ CTEAssert(FALSE);
+
+ return TRUE;
+}
+
+//* DummyDelAddr - Dummy del address routine.
+//
+// Called at init time when we need to initialize ourselves.
+//
+uint
+DummyDelAddr(void *Context, uint Type, IPAddr Address, IPMask Mask)
+{
+ DbgPrint("TCPIP: DummyAddAddr called - NOT GOOD\n");
+
+ CTEAssert(FALSE);
+
+ return TRUE;
+}
+
+//* DummyGetEList - Dummy get entity list.
+//
+// A dummy routine that should never be called.
+//
+// Input: Context - Unused.
+// EntityList - Pointer to entity list to be filled in.
+// Count - Pointer to number of entries in the list.
+//
+// Returns Status of attempt to get the info.
+//
+int
+DummyGetEList(void *Context, TDIEntityID *EntityList, uint *Count)
+{
+ DbgPrint("TCPIP: DummyGetEList called - NOT GOOD\n");
+
+ CTEAssert(FALSE);
+
+ return FALSE;
+}
+
+#ifdef _PNP_POWER
+//* DerefIF - Dereference an interface.
+//
+// Called when we need to dereference an interface. We decrement the
+// refcount, and if it goes to zero we signal whoever is blocked on
+// it.
+//
+// Input: IF - Interfaec to be dereferenced.
+//
+// Returns: Nothing.
+//
+void
+DerefIF(Interface *IF)
+{
+ uint Original;
+
+ Original = CTEInterlockedAddUlong(
+ &IF->if_refcount,
+ (ULONG)-1,
+ &RouteTableLock
+ );
+ if (Original != 1) {
+ return;
+ } else {
+ // We just decremented the last reference. Wake whoever is
+ // blocked on it.
+ CTEAssert(IF->if_block != NULL);
+ CTESignal(IF->if_block, NDIS_STATUS_SUCCESS);
+ }
+}
+
+//* LockedDerefIF - Dereference an interface w/RouteTableLock held.
+//
+// Called when we need to dereference an interface. We decrement the
+// refcount, and if it goes to zero we signal whoever is blocked on
+// it. The difference here is that we assume the caller already holds
+// the RouteTableLock.
+//
+// Input: IF - Interfaec to be dereferenced.
+//
+// Returns: Nothing.
+//
+void
+LockedDerefIF(Interface *IF)
+{
+ uint Original;
+
+ IF->if_refcount--;
+
+ if (IF->if_refcount != 0) {
+ return;
+ } else {
+ // We just decremented the last reference. Wake whoever is
+ // blocked on it.
+ CTEAssert(IF->if_block != NULL);
+ CTESignal(IF->if_block, NDIS_STATUS_SUCCESS);
+ }
+}
+#endif
+
+//* GetHashMask - Get mask to use with address when hashing.
+//
+// Called when we need to decide on the mask to use when hashing. If the
+// supplied mask is the host mask or the default mask, we'll use that. Else
+// if the supplied mask is at least as specific as the net mask, we'll use the
+// net mask. Otherwise we drop back to the default mask.
+//
+// Input: Destination - Destination we'll be hashing on.
+// Mask - Caller supplied mask.
+//
+// Returns: Mask to use.
+//
+IPMask
+GetHashMask(IPAddr Destination, IPMask Mask)
+{
+ IPMask NetMask;
+
+ if (Mask == HOST_MASK || Mask == DEFAULT_MASK)
+ return Mask;
+
+ NetMask = IPNetMask(Destination);
+
+ if ((NetMask & Mask) == NetMask)
+ return NetMask;
+
+ return DEFAULT_MASK;
+
+}
+
+//** AddrOnIF - Check to see if a given address is local to an IF
+//
+// Called when we want to see if a given address is a valid local address
+// for an interface. We walk down the chain of NTEs in the interface, and
+// see if we get a match. We assume the caller holds the RouteTableLock
+// at this point.
+//
+// Input: IF - Interface to check.
+// Addr - Address to check.
+//
+// Returns: TRUE if Addr is an address for IF, FALSE otherwise.
+//
+uint
+AddrOnIF(Interface *IF, IPAddr Addr)
+{
+ NetTableEntry *NTE;
+
+ NTE = IF->if_nte;
+ while (NTE != NULL) {
+ if ((NTE->nte_flags & NTE_VALID) && IP_ADDR_EQUAL(NTE->nte_addr, Addr))
+ return TRUE;
+ else
+ NTE = NTE->nte_ifnext;
+ }
+
+ return FALSE;
+}
+
+//** BestNTEForIF - Find the 'best match' NTE on a given interface.
+//
+// This is a utility function that takes an address and tries to find the
+// 'best match' NTE on a given interface. This is really only useful when we
+// have multiple IP addresses on a single interface.
+//
+// Input: Address - Source address of packet.
+// IF - Pointer to IF to be searched.
+//
+// Returns: The 'best match' NTE.
+//
+NetTableEntry *
+BestNTEForIF(IPAddr Address, Interface *IF)
+{
+ NetTableEntry *CurrentNTE, *FoundNTE;
+
+ if (IF->if_nte != NULL) {
+ // Walk the list of NTEs, looking for a valid one.
+ CurrentNTE = IF->if_nte;
+ FoundNTE = NULL;
+ do {
+ if (CurrentNTE->nte_flags & NTE_VALID) {
+ if (IP_ADDR_EQUAL(Address & CurrentNTE->nte_mask,
+ CurrentNTE->nte_addr & CurrentNTE->nte_mask))
+ return CurrentNTE;
+ else
+ if (FoundNTE == NULL)
+ FoundNTE = CurrentNTE;
+
+ }
+
+ CurrentNTE = CurrentNTE->nte_ifnext;
+ } while (CurrentNTE != NULL);
+
+ // If we found a match, or we didn't and the destination is not
+ // a broadcast, return the result. We have special case code to
+ // handle broadcasts, since the interface doesn't really matter there.
+ if (FoundNTE != NULL || (!IP_ADDR_EQUAL(Address, IP_LOCAL_BCST) &&
+ !IP_ADDR_EQUAL(Address, IP_ZERO_BCST)))
+ return FoundNTE;
+
+ }
+
+ // An 'anonymous' I/F, or the address we're reaching is a broadcast and the
+ // first interface has no address. Find a valid (non-loopback) address.
+ for (CurrentNTE = NetTableList; CurrentNTE != NULL;
+ CurrentNTE = CurrentNTE->nte_next) {
+ if (CurrentNTE != LoopNTE && (CurrentNTE->nte_flags & NTE_VALID))
+ return CurrentNTE;
+
+ }
+
+ return NULL;
+
+}
+
+//** IsBCastonNTE - Determine if the specified addr. is a bcast on a spec. NTE.
+//
+// This routine is called when we need to know if an address is a broadcast
+// on a particular net. We check in the order we expect to be most common - a
+// subnet bcast, an all ones broadcast, and then an all subnets broadcast. We
+// return the type of broadcast it is, or return DEST_LOCAL if it's not a
+// broadcast.
+//
+// Entry: Address - Address in question.
+// NTE - NetTableEntry to check Address against.
+//
+// Returns: Type of broadcast.
+//
+uchar
+IsBCastOnNTE(IPAddr Address, NetTableEntry *NTE)
+{
+ IPMask Mask;
+ IPAddr BCastAddr;
+
+ BCastAddr = NTE->nte_if->if_bcast;
+
+ if (NTE->nte_flags & NTE_VALID) {
+
+ Mask = NTE->nte_mask;
+
+ if(Mask != 0xFFFFFFFF)
+ {
+ if (IP_ADDR_EQUAL(Address, (NTE->nte_addr & Mask) | (BCastAddr & ~Mask)))
+ return DEST_SN_BCAST;
+ }
+
+ // See if it's an all subnet's broadcast.
+ if (!CLASSD_ADDR(Address)) {
+ Mask = IPNetMask(Address);
+
+ if (IP_ADDR_EQUAL(Address,
+ (NTE->nte_addr & Mask) | (BCastAddr & ~Mask)))
+ return DEST_BCAST;
+ } else {
+ // This is a class D address. If we're allowed to receive
+ // mcast datagrams, check our list.
+
+ if (IGMPLevel == 2) {
+ IGMPAddr *AddrPtr;
+ CTELockHandle Handle;
+
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ AddrPtr = NTE->nte_igmplist;
+ while (AddrPtr != NULL) {
+ if (IP_ADDR_EQUAL(Address, AddrPtr->iga_addr))
+ break;
+ else
+ AddrPtr = AddrPtr->iga_next;
+ }
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ if (AddrPtr != NULL)
+ return DEST_MCAST;
+ }
+ }
+ }
+
+ // A global bcast is certainly a bcast on this net.
+ if (IP_ADDR_EQUAL(Address, BCastAddr))
+ return DEST_BCAST;
+
+ return DEST_LOCAL;
+
+}
+
+//** InvalidSourceAddress - Check to see if a source address is invalid.
+//
+// This function takes an input address and checks to see if it is valid
+// if used as the source address of an incoming packet. An address is invalid
+// if it's 0, -1, a Class D or Class E address, is a net or subnet broadcast,
+// or has a 0 subnet or host part.
+//
+// Input: Address - Address to be check.
+//
+// Returns: FALSE if the address is not invalid, TRUE if it is invalid.
+//
+uint
+InvalidSourceAddress(IPAddr Address)
+{
+ NetTableEntry *NTE; // Pointer to current NTE.
+ IPMask Mask; // Mask for address.
+ uchar Result; // Result of broadcast check.
+ IPMask SNMask;
+ IPAddr MaskedAddress;
+ IPAddr LocalAddress;
+
+
+ if ( !CLASSD_ADDR(Address) &&
+ !CLASSE_ADDR(Address) &&
+ !IP_ADDR_EQUAL(Address, IP_ZERO_BCST) &&
+ !IP_ADDR_EQUAL(Address, IP_LOCAL_BCST)
+ ) {
+ // It's not an obvious broadcast. See if it's an all subnets
+ // broadcast, or has a zero host part.
+ Mask = IPNetMask(Address);
+ MaskedAddress = Address & Mask;
+
+ if (!IP_ADDR_EQUAL(Address, MaskedAddress) &&
+ !IP_ADDR_EQUAL(Address, (MaskedAddress | ~Mask))
+ ) {
+ // It's not an all subnet's broadcast, and it has a non-zero
+ // host/subnet part. Walk our local IP addresses, and see if it's
+ // a subnet broadcast.
+ NTE = NetTableList;
+ do {
+
+ LocalAddress = NTE->nte_addr;
+
+ if ((NTE->nte_flags & NTE_VALID) &&
+ !IP_LOOPBACK(LocalAddress)) {
+
+ Mask = NTE->nte_mask;
+ MaskedAddress = LocalAddress & Mask;
+
+ if (IP_ADDR_EQUAL(Address, MaskedAddress) ||
+ IP_ADDR_EQUAL(Address,
+ (MaskedAddress |
+ (NTE->nte_if->if_bcast & ~Mask)))) {
+ return TRUE;
+ }
+ }
+
+
+ NTE = NTE->nte_next;
+ } while (NTE != NULL);
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+//** FlushATCache - Flush an address from the ATCache
+//
+// This function takes an input address, and removes it from the ATCache,
+// if it is present.
+//
+// Input: Address - Address to be check.
+//
+// Returns: Destination type.
+//
+void
+FlushATCache(IPAddr Address)
+{
+ uint i;
+
+
+ for (i=0; i<ATC_SIZE; i++) {
+ if (ATCache[i].atc_flags & (ATCache[i].atc_addr == Address)) {
+ ATCache[i].atc_flags = 0;
+ }
+ }
+}
+
+//** GetAddrType - Return the type of a specified address.
+//
+// This function takes an input address, and determines what type it is. An
+// address can be local, bcast, remote, or remote bcast.
+//
+// Input: Address - Address to be check.
+//
+// Returns: Destination type.
+//
+uchar
+GetAddrType(IPAddr Address)
+{
+ NetTableEntry *NTE; // Pointer to current NTE.
+ IPMask Mask; // Mask for address.
+ uchar Result; // Result of broadcast check.
+ IPMask SNMask;
+ uint saveATCIndex;
+ uint i;
+
+ saveATCIndex = ATCIndex & ATC_MASK;
+ i = saveATCIndex;
+
+ do {
+ if (ATCache[i].atc_flags && (ATCache[i].atc_addr == Address)) {
+ Result = ATCache[i].atc_type;
+ if (ATCache[i].atc_flags && (ATCache[i].atc_addr == Address)) {
+ return(Result);
+ }
+ }
+ i = (--i) & ATC_MASK;
+ } while (i != saveATCIndex );
+
+
+
+ if (!CLASSE_ADDR(Address)) {
+ // See if it's one of our local addresses, or a broadcast
+ // on a local address.
+ NTE = NetTableList;
+ do {
+
+ if (IP_ADDR_EQUAL(NTE->nte_addr, Address) &&
+ (NTE->nte_flags & NTE_VALID)) {
+ Result = DEST_LOCAL;
+ goto gat_exit;
+ }
+
+ if ((Result = IsBCastOnNTE(Address, NTE)) != DEST_LOCAL) {
+ goto gat_exit;
+ }
+
+ // See if the destination has a valid host part.
+ if (NTE->nte_flags & NTE_VALID) {
+ SNMask = NTE->nte_mask;
+ if (IP_ADDR_EQUAL(Address & SNMask, NTE->nte_addr & SNMask)) {
+ // On this subnet. See if the host part is invalid.
+
+ if (IP_ADDR_EQUAL(Address & SNMask, Address)) {
+ Result = DEST_INVALID; // Invalid 0 host part.
+ goto gat_exit;
+ }
+ }
+ }
+ NTE = NTE->nte_next;
+ } while (NTE != NULL);
+
+ // It's not a local address, see if it's loopback.
+ if (IP_LOOPBACK(Address)) {
+ Result = DEST_LOCAL;
+ goto gat_exit;
+ }
+
+ // If we're doing IGMP, see if it's a Class D address. If it it,
+ // return that.
+ if (CLASSD_ADDR(Address)) {
+ if (IGMPLevel != 0) {
+ Result = DEST_REM_MCAST;
+ goto gat_exit;
+ }
+ else {
+ Result = DEST_INVALID;
+ goto gat_exit;
+ }
+ }
+
+ Mask = IPNetMask(Address);
+
+ // Now check remote broadcast. When we get here we know that the
+ // address is not a global broadcast, a subnet broadcast for a subnet
+ // of which we're a member, or an all-subnets broadcast for a net of
+ // which we're a member. Since we're avoiding making assumptions about
+ // all subnet of a net having the same mask, we can't really check for
+ // a remote subnet broadcast. We'll use the net mask and see if it's
+ // a remote all-subnet's broadcast.
+ if (IP_ADDR_EQUAL(Address, (Address & Mask) | (IP_LOCAL_BCST & ~Mask))) {
+ Result = DEST_REM_BCAST;
+ goto gat_exit;
+ }
+
+ // Check for invalid 0 parts. All we can do from here is see if he's
+ // sending to a remote net with all zero subnet and host parts. We
+ // can't check to see if he's sending to a remote subnet with an all
+ // zero host part.
+ if (IP_ADDR_EQUAL(Address, Address & Mask) ||
+ IP_ADDR_EQUAL(Address, NULL_IP_ADDR)) {
+ Result = DEST_INVALID;
+ goto gat_exit;
+ }
+
+ // Must be remote.
+ Result = DEST_REMOTE;
+ goto gat_exit;
+ }
+
+ Result = DEST_INVALID;
+
+gat_exit:
+
+ ++ATCIndex;
+
+ i = ATCIndex & ATC_MASK;
+
+ ATCache[i].atc_addr = Address;
+ ATCache[i].atc_type = Result;
+ ATCache[i].atc_flags = 1;
+ return(Result);
+
+}
+
+//** IPHash - IP hash function.
+//
+// This is the function to compute the hash index from a masked address.
+//
+// Input: Address - Masked address to be hashed.
+//
+// Returns: Hashed value.
+//
+uint
+IPHash(IPAddr Address)
+{
+ uchar *i = (uchar *)&Address;
+ return (i[0] + i[1] + i[2] + i[3]) & (ROUTE_TABLE_SIZE-1);
+}
+
+//** GetLocalNTE - Get the local NTE for an incoming packet.
+//
+// Called during receive processing to find a matching NTE for a packet.
+// First we check against the NTE we received it on, then against any NTE.
+//
+// Input: Address - The dest. address of the packet.
+// NTE - Pointer to NTE packet was received on - filled in on
+// exit w/correct NTE.
+//
+// Returns: DEST_LOCAL if the packet is destined for this host, DEST_REMOTE if it needs to
+// be routed, DEST_SN_BCAST or DEST_BCAST if it's some sort of a broadcast.
+uchar
+GetLocalNTE(IPAddr Address, NetTableEntry **NTE)
+{
+ NetTableEntry *LocalNTE = *NTE;
+ IPMask Mask;
+ uchar Result;
+ int i;
+ Interface *LocalIF;
+ NetTableEntry *OriginalNTE;
+
+ // Quick check to see if it is for the NTE it came in on (the common case).
+ if (IP_ADDR_EQUAL(Address, LocalNTE->nte_addr) &&
+ (LocalNTE->nte_flags & NTE_VALID))
+ return DEST_LOCAL; // For us, just return.
+
+ // Now check to see if it's a broadcast of some sort on the interface it
+ // came in on.
+ if ((Result = IsBCastOnNTE(Address, LocalNTE)) != DEST_LOCAL)
+ return Result;
+
+ // The common cases failed us. Loop through the NetTable and see if
+ // it is either a valid local address or is a broadcast on one of the NTEs
+ // on the incoming interface. We won't check the NTE we've already looked
+ // at. We look at all NTEs, including the loopback NTE, because a loopback
+ // frame could come through here. Also, frames from ourselves to ourselves
+ // will come in on the loopback NTE.
+
+ i = 0;
+ LocalIF = LocalNTE->nte_if;
+ OriginalNTE = LocalNTE;
+ LocalNTE = NetTableList;
+ do {
+ if (LocalNTE != OriginalNTE) {
+ if (IP_ADDR_EQUAL(Address, LocalNTE->nte_addr) &&
+ (LocalNTE->nte_flags & NTE_VALID)) {
+ *NTE = LocalNTE;
+ return DEST_LOCAL; // For us, just return.
+ }
+
+ // If this NTE is on the same interface as the NTE it arrived on,
+ // see if it's a broadcast.
+ if (LocalIF == LocalNTE->nte_if)
+ if ((Result = IsBCastOnNTE(Address, LocalNTE)) != DEST_LOCAL) {
+ *NTE = LocalNTE;
+ return Result;
+ }
+
+ }
+
+ LocalNTE = LocalNTE->nte_next;
+
+ } while (LocalNTE != NULL);
+
+ // It's not a local address, see if it's loopback.
+ if (IP_LOOPBACK(Address)) {
+ *NTE = LoopNTE;
+ return DEST_LOCAL;
+ }
+
+ // If it's a class D address and we're receiveing multicasts, handle it
+ // here.
+ if (CLASSD_ADDR(Address)) {
+ if (IGMPLevel != 0)
+ return DEST_REM_MCAST;
+ else
+ return DEST_INVALID;
+ }
+
+ // It's not local. Check to see if maybe it's a net broadcast for a net
+ // of which we're not a member. If so, return remote bcast. We can't check
+ // for subnet broadcast of subnets for which we're not a member, since we're
+ // not making assumptions about all subnets of a single net having the
+ // same mask. If we're here it's not a subnet broadcast for a net of which
+ // we're a member, so we don't know a subnet mask for it. We'll just use
+ // the net mask.
+ Mask = IPNetMask(Address);
+ if (IP_ADDR_EQUAL(Address, (Address & Mask) |
+ ((*NTE)->nte_if->if_bcast & ~Mask)))
+ return DEST_REM_BCAST;
+
+ // If it's to the 0 address, or a Class E address, or has an all-zero
+ // subnet and net part, it's invalid.
+
+
+ if (IP_ADDR_EQUAL(Address, IP_ZERO_BCST) ||
+ IP_ADDR_EQUAL(Address, (Address & Mask)) ||
+ CLASSE_ADDR(Address))
+ return DEST_INVALID;
+
+ // If we're DHCPing the interface on which this came in we'll accept this.
+ // If it came in as a broadcast a check in IPRcv() will reject it. If it's
+ // a unicast to us we'll pass it up.
+ if (DHCPNTE != NULL && DHCPNTE == *NTE) {
+ return DEST_LOCAL;
+ }
+
+ return DEST_REMOTE;
+}
+
+
+//** FindSpecificRTE - Look for a particular RTE.
+//
+// Called when we're adding a route and want to find a particular RTE.
+// We take in the destination, mask, first hop, and src addr, and search
+// the appropriate routeing table chain. We assume the caller has the
+// RouteTableLock held. If we find the match, we'll return a pointer to the
+// RTE with the RTE lock held, as well as a pointer to the previous RTE in
+// the chain.
+//
+// Input: Dest - Destination to search for.
+// Mask - Mask for destination.
+// FirstHop - FirstHop to Dest.
+// OutIF - Pointer to outgoing interface structure.
+// PrevRTE - Place to put PrevRTE, if found.
+//
+// Returns: Pointer to matching RTE if found, or NULL if not.
+//
+RouteTableEntry *
+FindSpecificRTE(IPAddr Dest, IPMask Mask, IPAddr FirstHop, Interface *OutIF,
+ RouteTableEntry **PrevRTE)
+{
+ uint Index;
+ IPMask HashMask;
+ RouteTableEntry *TempRTE, *CurrentRTE;
+
+ HashMask = GetHashMask(Dest, Mask);
+
+ Index = IPHash(Dest & HashMask);
+ TempRTE = STRUCT_OF(RouteTableEntry, &RouteTable[Index], rte_next);
+ CurrentRTE = TempRTE->rte_next;
+
+ //
+ // If this has been called because user mode was trying to set the route
+ // to INVALID, then the OUTIF will be DummyInterface, but we want to match
+ // any interface, since the IF will have already been plumbed by DODCallOut
+ //
+
+ if(OutIF == (Interface *)&DummyInterface)
+ {
+ //
+ // Match everything but the interface
+ //
+
+ while (CurrentRTE != NULL)
+ {
+ if (IP_ADDR_EQUAL(CurrentRTE->rte_dest,Dest) &&
+ CurrentRTE->rte_mask == Mask &&
+ IP_ADDR_EQUAL(CurrentRTE->rte_addr, FirstHop))
+ {
+ break;
+ }
+
+ TempRTE = CurrentRTE;
+ CurrentRTE = CurrentRTE->rte_next;
+ }
+ }
+ else
+ {
+ // Walk the table, looking for a match.
+ while (CurrentRTE != NULL) {
+ // See if everything matches.
+ if (IP_ADDR_EQUAL(CurrentRTE->rte_dest,Dest) &&
+ CurrentRTE->rte_mask == Mask &&
+ IP_ADDR_EQUAL(CurrentRTE->rte_addr, FirstHop) &&
+ CurrentRTE->rte_if == OutIF)
+ break;
+
+ TempRTE = CurrentRTE;
+ CurrentRTE = CurrentRTE->rte_next;
+ }
+ }
+
+ *PrevRTE = TempRTE;
+ return CurrentRTE;
+
+}
+
+//** IsRouteICMP - This function is used by Router Discovery to determine
+// how we learned about the route. We are not allowed to update or timeout
+// routes that were not learned about via icmp. If the route is new then
+// we treat it as icmp and add a new entry.
+// Input: Dest - Destination to search for.
+// Mask - Mask for destination.
+// FirstHop - FirstHop to Dest.
+// OutIF - Pointer to outgoing interface structure.
+//
+// Returns: TRUE if learned via ICMP, FALSE otherwise.
+//
+uint
+IsRouteICMP(IPAddr Dest, IPMask Mask, IPAddr FirstHop, Interface *OutIF)
+{
+ RouteTableEntry *RTE;
+ RouteTableEntry *TempRTE;
+
+ RTE = FindSpecificRTE(Dest, Mask, FirstHop, OutIF, &TempRTE);
+
+ if (RTE == NULL)
+ return(TRUE);
+
+ if (RTE->rte_proto == IRE_PROTO_ICMP) {
+ return(TRUE);
+ } else {
+ return(FALSE);
+ }
+}
+
+
+//** FindRTE - Find a matching RTE in a hash table chain.
+//
+// Called when we want to find a matching RTE. We take in a destination,
+// a source, a hash index, and a maximum priority, and walk down the
+// chain specified by the index looking for a matching RTE. If we can find
+// one, we'll keep looking hoping for a match on the source address.
+//
+// The caller must hold the RouteTableLock before calling this function.
+//
+// Input: Dest - Destination we're trying to reach.
+// Source - Source address to match.
+// Index - Index of chain to search.
+// MaxPri - Maximum acceptable priority.
+// MinPri - Minimum acceptable priority.
+//
+// Returns: Pointer to RTE if found, or NULL if not.
+//
+RouteTableEntry *
+FindRTE(IPAddr Dest, IPAddr Source, uint Index, uint MaxPri, uint MinPri)
+{
+ RouteTableEntry *CurrentRTE;
+ uint RTEPri;
+ uint Metric;
+ RouteTableEntry *FoundRTE;
+
+ // First walk down the chain, skipping those RTEs that have a
+ // a priority greater than what we want.
+
+ CurrentRTE = RouteTable[Index];
+
+ for (;;) {
+ if (CurrentRTE == NULL)
+ return NULL; // Hit end of chain, bounce out.
+
+ if (CurrentRTE->rte_priority <= MaxPri)
+ break; // He's a possible match.
+
+ // Priority is too big. Try the next one.
+ CurrentRTE = CurrentRTE->rte_next;
+ }
+
+ FoundRTE = NULL;
+
+ // When we get here, we have a locked RTE with a priority less than or
+ // equal to what was specifed.
+ // Examine it, and if it doesn't match try the next one. If it does match
+ // we'll stash it temporarily and keep looking for one that matches the
+ // specifed source.
+ for (;;) {
+
+ // The invariant at the top of this loop is that CurrentRTE points to
+ // a candidate RTE, locked with the handle in CurrentHandle.
+
+ if (CurrentRTE->rte_flags & RTE_VALID) {
+ // He's valid. Make sure he's at least the priority we need. If
+ // he is, see if he matches. Otherwise we're done.
+
+ if (CurrentRTE->rte_priority < MinPri) {
+ // His priority is too small. Since the list is in sorted order,
+ // all following routes must have too low a priority, so we're
+ // done.
+ return NULL;
+ }
+
+ if (IP_ADDR_EQUAL(Dest & CurrentRTE->rte_mask, CurrentRTE->rte_dest)) {
+ // He's valid for this route. Save the current information,
+ // and look for a matching source.
+ FoundRTE = CurrentRTE;
+
+ if (!IP_ADDR_EQUAL(Source, NULL_IP_ADDR) &&
+ !AddrOnIF(CurrentRTE->rte_if, Source)) {
+ RTEPri = CurrentRTE->rte_priority;
+ Metric = CurrentRTE->rte_metric;
+
+ CurrentRTE = CurrentRTE->rte_next;
+
+ // We've save the info. Starting at the next RTE, look for
+ // an RTE that matches both the mask criteria and the source
+ // address. The search will terminate when we hit the end
+ // of the list, or the RTE we're examing has a different
+ // (presumably lesser) priority (or greater metric), or we
+ // find a match.
+ while (CurrentRTE != NULL &&
+ CurrentRTE->rte_priority == RTEPri &&
+ CurrentRTE->rte_metric == Metric) {
+
+
+ // Skip invalid route types.
+ if (CurrentRTE->rte_flags & RTE_VALID) {
+ if (IP_ADDR_EQUAL(Dest & CurrentRTE->rte_mask,
+ CurrentRTE->rte_dest)) {
+ if (AddrOnIF(CurrentRTE->rte_if, Source)) {
+ // He matches the source. Free the old lock,
+ // and break out.
+ FoundRTE = CurrentRTE;
+ break;
+ }
+ }
+
+ }
+ CurrentRTE = CurrentRTE->rte_next;
+ }
+ }
+
+ // At this point, FoundRTE points to the RTE we want to return,
+ // and *Handle has the lock handle for the RTE. Break out.
+ break;
+ }
+
+ }
+
+ CurrentRTE = CurrentRTE->rte_next;
+
+ if (CurrentRTE != NULL) {
+ continue;
+ } else
+ break;
+ }
+
+ return FoundRTE;
+}
+
+//* ValidateDefaultGWs - Mark all default gateways as valid.
+//
+// Called to one or all of our default gateways as up. The caller specifies
+// the IP address of the one to mark as up, or NULL_IP_ADDR if they're all
+// supposed to be marked up. We return a count of how many we marked as
+// valid.
+//
+// Input: IP address of G/W to mark as up.
+//
+// Returns: Count of gateways marked as up.
+//
+uint
+ValidateDefaultGWs(IPAddr Addr)
+{
+ RouteTableEntry *RTE;
+ uint Count = 0;
+ uint Now = CTESystemUpTime() / 1000L;
+
+ RTE = RouteTable[IPHash(0)];
+
+ while (RTE != NULL) {
+ if (RTE->rte_mask == DEFAULT_MASK && !(RTE->rte_flags & RTE_VALID) &&
+ (IP_ADDR_EQUAL(Addr, NULL_IP_ADDR) ||
+ IP_ADDR_EQUAL(Addr, RTE->rte_addr))) {
+ RTE->rte_flags |= RTE_VALID;
+ RTE->rte_valid = Now;
+ Count++;
+ }
+ RTE = RTE->rte_next;
+ }
+
+ DefGWActive += Count;
+ return Count;
+}
+
+//* InvalidateRCEChain - Invalidate the RCEs on an RCE.
+//
+// Called to invalidate the RCE chain on an RTE. We assume the caller holds
+// the route table lock.
+//
+// Input: RTE - RTE on which to invalidate RCEs.
+//
+// Returns: Nothing.
+//
+void
+InvalidateRCEChain(RouteTableEntry *RTE)
+{
+ CTELockHandle RCEHandle; // Lock handle for RCE being updated.
+ RouteCacheEntry *TempRCE, *CurrentRCE;
+ Interface *OutIF;
+
+ OutIF = RTE->rte_if;
+
+ // If there is an RCE chain on this RCE, invalidate the RCEs on it. We still
+ // hold the RouteTableLock, so RCE closes can't happen.
+
+
+ CurrentRCE = RTE->rte_rcelist;
+ RTE->rte_rcelist = NULL;
+
+ // Walk down the list, nuking each RCE.
+ while (CurrentRCE != NULL) {
+
+ CTEGetLock(&CurrentRCE->rce_lock, &RCEHandle);
+
+ if (CurrentRCE->rce_flags & RCE_VALID) {
+ CTEAssert(CurrentRCE->rce_rte == RTE);
+ CurrentRCE->rce_flags &= ~RCE_VALID;
+ CurrentRCE->rce_rte = (RouteTableEntry *)OutIF;
+ if ((CurrentRCE->rce_flags & RCE_CONNECTED) &&
+ CurrentRCE->rce_usecnt == 0) {
+
+ (*(OutIF->if_invalidate))(OutIF->if_lcontext, CurrentRCE);
+#ifdef _PNP_POWER
+ if (CurrentRCE->rce_flags & RCE_REFERENCED) {
+ LockedDerefIF(OutIF);
+ CurrentRCE->rce_flags &= ~RCE_REFERENCED;
+ }
+#endif
+ }
+ } else
+ CTEAssert(FALSE);
+
+ TempRCE = CurrentRCE->rce_next;
+ CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle);
+ CurrentRCE = TempRCE;
+ }
+
+}
+
+//** FindValidIFForRTE - Find a valid inteface for an RTE.
+//
+// Called when we're going to send a packet out a route that currently marked
+// as disconnected. If we have a valid callout routine we'll call it to find
+// the outgoing interface index, and set up the RTE to point at that interface.
+// This routine is called with the RouteTableLock held.
+//
+// Input: RTE - A pointer to the RTE for the route being used.
+// Destination - Destination IP address we're trying to reach.
+// Source - Source IP address we're sending from.
+// Protocol - Protocol type of packet that caused send.
+// Buffer - Pointer to first part of packet that caused send.
+// Length - Length of buffer.
+//
+// Returns: A pointer to the RTE, or NULL if that RTE couldn't be connected.
+//
+RouteTableEntry *
+FindValidIFForRTE(RouteTableEntry *RTE, IPAddr Destination, IPAddr Source,
+ uchar Protocol, uchar *Buffer, uint Length)
+{
+ uint NewIFIndex;
+ Interface *NewIF;
+ NetTableEntry *NewNTE;
+
+ if (DODCallout != NULL) {
+ // There is a callout. See if it can help us.
+ NewIFIndex = (*DODCallout)(RTE->rte_context, Destination, Source,
+ Protocol, Buffer, Length);
+ if (NewIFIndex != INVALID_IF_INDEX) {
+ // We got what should be a valid index. Walk our interface table list
+ // and see if we can find a matching interface structure.
+ for (NewIF = IFList; NewIF != NULL; NewIF = NewIF->if_next) {
+ if (NewIF->if_index == NewIFIndex) {
+ // Found one.
+ break;
+ }
+ }
+ if (NewIF != NULL) {
+ // We found a matching structure. Set the RTE interface to point
+ // to this, and mark as connected.
+ if (RTE->rte_addr != IPADDR_LOCAL) {
+ // See if the first hop of the route is a local address on this
+ // new interface. If it is, mark it as local.
+ for (NewNTE = NewIF->if_nte; NewNTE != NULL;
+ NewNTE = NewNTE->nte_ifnext) {
+
+ // Don't look at him if he's not valid.
+ if (!(NewNTE->nte_flags & NTE_VALID)) {
+ continue;
+ }
+
+ // See if the first hop in the RTE is equal to this IP
+ // address.
+ if (IP_ADDR_EQUAL(NewNTE->nte_addr, RTE->rte_addr)) {
+ // It is, so mark as local and quit looking.
+ RTE->rte_addr = IPADDR_LOCAL;
+ RTE->rte_type = IRE_TYPE_DIRECT;
+ break;
+ }
+ }
+ }
+
+ // Set the RTE to the new interface, and mark him as valid.
+ RTE->rte_if = NewIF;
+ RTE->rte_flags |= RTE_IF_VALID;
+ RTE->rte_mtu = NewIF->if_mtu - sizeof(IPHeader);
+ return RTE;
+ } else
+ CTEAssert(FALSE);
+ }
+ }
+
+ // Either the callout is NULL, or the callout couldn't map a inteface index.
+ return NULL;
+}
+
+//** LookupRTE - Lookup a routing table entry.
+//
+// This routine looks up a routing table entry, and returns with the entry
+// locked if it finds one. If it doesn't find one, it returns NULL. This
+// routine assumes that the routing table is locked when it is called.
+//
+// The routeing table is organized as an open hash table. The table contains
+// routes to hosts, subnets, and nets. Host routes are hashed on the host
+// address, other non-default routes on the destination anded with the net
+// mask, and default routes wind up in bucket 0. Within each bucket chain
+// the routes are sorted with the greatest priority (i.e. number of bits in the
+// route mask) first, and within each priority class the routes are sorted
+// with the lowest metric first. The caller may specify a maximum priority
+// for the route to be found. We look for routes in order of most specific to
+// least specifc, i.e. first host routes, then other non-default routes, and
+// finally default routes. We give preference to routes that are going out
+// on an interface with an address that matches the input source address.
+//
+// It might be worthile in the future to split this up into multiple tables,
+// so that we have a table for host routes, a table for non-default routes,
+// and a list of default routes.
+//
+// Entry: Address - Address for which a route is to be found.
+// Src - IPAddr of source (may be 0).
+// MaxPri - Maximum priority of route to find.
+//
+// Returns: A pointer to the locked RTE if we find one, or NULL if we don't
+//
+RouteTableEntry *
+LookupRTE(IPAddr Address, IPAddr Src, uint MaxPri)
+{
+ RouteTableEntry *RTE;
+
+ // First try to find a host route, if we're allowed to.
+ if (MaxPri == HOST_ROUTE_PRI) {
+ RTE = FindRTE(Address, Src, IPHash(Address), MaxPri, MaxPri);
+ if (RTE != NULL) {
+ return RTE;
+ }
+ }
+
+ // Don't have or weren't allowed to find a host route. See if we can
+ // find a non-default route.
+ if (MaxPri > DEFAULT_ROUTE_PRI) {
+ RTE = FindRTE(Address, Src, IPHash(Address & IPNetMask(Address)),
+ MaxPri, DEFAULT_ROUTE_PRI + 1);
+ if (RTE != NULL) {
+ return RTE;
+ }
+ }
+
+ // No non-default route. Try a default route.
+ RTE = FindRTE(Address, Src, IPHash(0), MaxPri, DEFAULT_ROUTE_PRI);
+
+ return RTE;
+
+}
+
+//** GetRouteContext - Routine to get the route context for a specific route.
+//
+// Called when we need to get the route context for a path, usually when we're adding
+// a route derived from an existing route. We return the route context for the
+// existing route, or NULL if we can't find one.
+//
+// Input: Destination - Destination address of path.
+// Source - Source address of path.
+//
+// Returns: A ROUTE_CONTEXT, or NULL.
+//
+void *
+GetRouteContext(IPAddr Destination, IPAddr Source)
+{
+ CTELockHandle Handle;
+ RouteTableEntry *RTE;
+ ROUTE_CONTEXT Context;
+
+ CTEGetLock(&RouteTableLock, &Handle);
+ RTE = LookupRTE(Destination, Source, HOST_ROUTE_PRI);
+ if (RTE != NULL) {
+ Context = RTE->rte_context;
+ } else
+ Context = NULL;
+
+ CTEFreeLock(&RouteTableLock, Handle);
+
+ return(Context);
+}
+
+//* FindInsertPoint - Find out where to insert an RTE in the table.
+//
+// Called to find out where to insert an RTE. We hash into the table,
+// and walk the chain until we hit the end or we find an RTE with a
+// lesser priority or a greater metric. Once we find the appropriate spot
+// we return a pointer to the RTE immediately prior to the one we want to
+// insert. We assume the caller holds the lock on the route table when calling
+// this function.
+//
+// Input: InsertRTE - RTE we're going to (eventually) insert.
+//
+// Returns: Pointer to RTE in insert after.
+//
+RouteTableEntry *
+FindInsertPoint(RouteTableEntry *InsertRTE)
+{
+ RouteTableEntry *PrevRTE, *CurrentRTE;
+ IPMask HashMask, Mask;
+ uint Priority, Metric;
+ uint Index;
+
+ Priority = InsertRTE->rte_priority;
+ Metric = InsertRTE->rte_metric;
+
+ // First figure out where he should go. We'll hash on the whole address
+ // if the mask allows us to, or on the net portion if this is a non-default
+ // route.
+
+ Mask = InsertRTE->rte_mask;
+ HashMask = GetHashMask(InsertRTE->rte_dest, Mask);
+
+ Index = IPHash(InsertRTE->rte_dest & HashMask);
+
+ PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[Index], rte_next);
+ CurrentRTE = PrevRTE->rte_next;
+
+ // Walk the table, looking for a place to insert it.
+ while (CurrentRTE != NULL) {
+ if (CurrentRTE->rte_priority < Priority) {
+ break;
+ }
+
+ if (CurrentRTE->rte_priority == Priority) {
+ // Priorities match. Check the metrics.
+ if (CurrentRTE->rte_metric > Metric) {
+ // Our metric is smaller than his, so we're done.
+ break;
+ }
+ }
+
+ // Either his priority is greater than ours or his metric is less
+ // than or equal to ours. Check the next one.
+ PrevRTE = CurrentRTE;
+ CurrentRTE = CurrentRTE->rte_next;
+ }
+
+ // At this point, we've either found the correct spot or hit the end
+ // of the list.
+ return PrevRTE;
+
+}
+
+
+//** LookupNextHop - Look up the next hop
+//
+// Called when we need to find the next hop on our way to a destination. We
+// call LookupRTE to find it, and return the appropriate information.
+//
+// In a PnP build, the interface is referenced here.
+//
+// Entry: Destination - IP address we're trying to reach.
+// Src - Source address of datagram being routed.
+// NextHop - Pointer to IP address of next hop (returned).
+// MTU - Pointer to where to return max MTU used on the
+// route.
+//
+// Returns: Pointer to outgoing interface if we found one, NULL otherwise.
+//
+Interface *
+LookupNextHop(IPAddr Destination, IPAddr Src, IPAddr *NextHop, uint *MTU)
+{
+ CTELockHandle TableLock; // Lock handle for routing table.
+ RouteTableEntry *Route; // Pointer to route table entry for route.
+ Interface *IF;
+
+ CTEGetLock(&RouteTableLock, &TableLock);
+ Route = LookupRTE(Destination, Src, HOST_ROUTE_PRI);
+
+ if (Route != (RouteTableEntry *)NULL) {
+ IF = Route->rte_if;
+
+ // If this is a direct route, send straight to the destination.
+ *NextHop = IP_ADDR_EQUAL(Route->rte_addr, IPADDR_LOCAL) ? Destination :
+ Route->rte_addr;
+
+ *MTU = Route->rte_mtu;
+#ifdef _PNP_POWER
+ IF->if_refcount++;
+#endif
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return IF;
+ } else { // Couldn't find a route.
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return NULL;
+ }
+}
+
+//** LookupNextHopWithBuffer - Look up the next hop, with packet information.
+//
+// Called when we need to find the next hop on our way to a destination and we
+// have packet information that we may use for dial on demand support. We call
+// LookupRTE to find it, and return the appropriate information. We may bring up
+// the link if neccessary.
+//
+// In a PnP build, the interface is referenced here.
+//
+// Entry: Destination - IP address we're trying to reach.
+// Src - Source address of datagram being routed.
+// NextHop - Pointer to IP address of next hop (returned).
+// MTU - Pointer to where to return max MTU used on the
+// route.
+// Protocol - Protocol type for packet that's causing this lookup.
+// Buffer - Pointer to first part of packet causing lookup.
+// Length - Length of Buffer.
+//
+// Returns: Pointer to outgoing interface if we found one, NULL otherwise.
+//
+Interface *
+LookupNextHopWithBuffer(IPAddr Destination, IPAddr Src, IPAddr *NextHop,
+ uint *MTU, uchar Protocol, uchar *Buffer, uint Length)
+{
+ CTELockHandle TableLock; // Lock handle for routing table.
+ RouteTableEntry *Route; // Pointer to route table entry for route.
+ Interface *IF;
+
+ CTEGetLock(&RouteTableLock, &TableLock);
+ Route = LookupRTE(Destination, Src, HOST_ROUTE_PRI);
+
+ if (Route != (RouteTableEntry *)NULL) {
+
+ // If this is a direct route, send straight to the destination.
+ *NextHop = IP_ADDR_EQUAL(Route->rte_addr, IPADDR_LOCAL) ? Destination :
+ Route->rte_addr;
+
+
+ // See if the route we found is connected. If not, try to connect it.
+ if (!(Route->rte_flags & RTE_IF_VALID)) {
+ Route = FindValidIFForRTE(Route, Destination, Src, Protocol, Buffer,
+ Length);
+ if (Route == NULL) {
+ // Couldn't bring it up.
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return NULL;
+ } else
+ IF = Route->rte_if;
+ } else
+ IF = Route->rte_if;
+
+ *MTU = Route->rte_mtu;
+#ifdef _PNP_POWER
+ IF->if_refcount++;
+#endif
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return IF;
+ } else { // Couldn't find a route.
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return NULL;
+ }
+}
+
+//* RTReadNext - Read the next route in the table.
+//
+// Called by the GetInfo code to read the next route in the table. We assume
+// the context passed in is valid, and the caller has the RouteTableLock.
+//
+// Input: Context - Pointer to a RouteEntryContext.
+// Buffer - Pointer to an IPRouteEntry structure.
+//
+// Returns: TRUE if more data is available to be read, FALSE is not.
+//
+uint
+RTReadNext(void *Context, void *Buffer)
+{
+ RouteEntryContext *REContext = (RouteEntryContext *)Context;
+ IPRouteEntry *IPREntry = (IPRouteEntry *)Buffer;
+ RouteTableEntry *CurrentRTE;
+ uint i;
+ uint Now = CTESystemUpTime() / 1000L;
+ Interface *IF;
+ NetTableEntry *SrcNTE;
+
+ CurrentRTE = REContext->rec_rte;
+
+ // Fill in the buffer.
+ IF = CurrentRTE->rte_if;
+
+ IPREntry->ire_dest = CurrentRTE->rte_dest;
+ IPREntry->ire_index = IF->if_index;
+ IPREntry->ire_metric1 = CurrentRTE->rte_metric;
+ IPREntry->ire_metric2 = IRE_METRIC_UNUSED;
+ IPREntry->ire_metric3 = IRE_METRIC_UNUSED;
+ IPREntry->ire_metric4 = IRE_METRIC_UNUSED;
+ IPREntry->ire_metric5 = IRE_METRIC_UNUSED;
+ if (IP_ADDR_EQUAL(CurrentRTE->rte_addr, IPADDR_LOCAL)) {
+ SrcNTE = BestNTEForIF(CurrentRTE->rte_dest, IF);
+ if (IF->if_nte != NULL && SrcNTE != NULL)
+ IPREntry->ire_nexthop = SrcNTE->nte_addr;
+ else
+ IPREntry->ire_nexthop = IPREntry->ire_dest;
+ } else {
+ IPREntry->ire_nexthop = CurrentRTE->rte_addr;
+ }
+ IPREntry->ire_type = (CurrentRTE->rte_flags & RTE_VALID ?
+ CurrentRTE->rte_type : IRE_TYPE_INVALID);
+ IPREntry->ire_proto = CurrentRTE->rte_proto;
+ IPREntry->ire_age = Now - CurrentRTE->rte_valid;
+ IPREntry->ire_mask = CurrentRTE->rte_mask;
+ IPREntry->ire_context = CurrentRTE->rte_context;
+
+ // We've filled it in. Now update the context.
+ if (CurrentRTE->rte_next != NULL) {
+ REContext->rec_rte = CurrentRTE->rte_next;
+ return TRUE;
+ } else {
+ // The next RTE is NULL. Loop through the RouteTable looking for a new
+ // one.
+ i = REContext->rec_index + 1;
+ while (i < ROUTE_TABLE_SIZE) {
+ if (RouteTable[i] != NULL) {
+ REContext->rec_rte = RouteTable[i];
+ REContext->rec_index = i;
+ return TRUE;
+ break;
+ } else
+ i++;
+ }
+
+ REContext->rec_index = 0;
+ REContext->rec_rte = NULL;
+ return FALSE;
+ }
+
+}
+
+//* RTValidateContext - Validate the context for reading the route table.
+//
+// Called to start reading the route table sequentially. We take in
+// a context, and if the values are 0 we return information about the
+// first route in the table. Otherwise we make sure that the context value
+// is valid, and if it is we return TRUE.
+// We assume the caller holds the route table lock.
+//
+// Input: Context - Pointer to a RouteEntryContext.
+// Valid - Where to return information about context being
+// valid.
+//
+// Returns: TRUE if more data to be read in table, FALSE if not. *Valid set
+// to TRUE if input context is valid
+//
+uint
+RTValidateContext(void *Context, uint *Valid)
+{
+ RouteEntryContext *REContext = (RouteEntryContext *)Context;
+ uint i;
+ RouteTableEntry *TargetRTE;
+ RouteTableEntry *CurrentRTE;
+
+ i = REContext->rec_index;
+ TargetRTE = REContext->rec_rte;
+
+ // If the context values are 0 and NULL, we're starting from the beginning.
+ if (i == 0 && TargetRTE == NULL) {
+ *Valid = TRUE;
+ do {
+ if ((CurrentRTE = RouteTable[i]) != NULL) {
+ break;
+ }
+ i++;
+ } while (i < ROUTE_TABLE_SIZE);
+
+ if (CurrentRTE != NULL) {
+ REContext->rec_index = i;
+ REContext->rec_rte = CurrentRTE;
+ return TRUE;
+ } else
+ return FALSE;
+
+ } else {
+
+ // We've been given a context. We just need to make sure that it's
+ // valid.
+
+ if (i < ROUTE_TABLE_SIZE) {
+ CurrentRTE = RouteTable[i];
+ while (CurrentRTE != NULL) {
+ if (CurrentRTE == TargetRTE) {
+ *Valid = TRUE;
+ return TRUE;
+ break;
+ } else {
+ CurrentRTE = CurrentRTE->rte_next;
+ }
+ }
+
+ }
+
+ // If we get here, we didn't find the matching RTE.
+ *Valid = FALSE;
+ return FALSE;
+
+ }
+
+}
+
+
+//* DeleteRTE - Delete an RTE.
+//
+// Called when we need to delete an RTE. We assume the caller has the
+// RouteTableLock. We'll splice out the RTE, invalidate his RCEs, and
+// free the memory.
+//
+// Input: PrevRTE - RTE in 'front' of one being deleted.
+// RTE - RTE to be deleted.
+//
+// Returns: Nothing.
+//
+void
+DeleteRTE(RouteTableEntry *PrevRTE, RouteTableEntry *RTE)
+{
+ PrevRTE->rte_next = RTE->rte_next; // Splice him from the table.
+ IPSInfo.ipsi_numroutes--;
+
+ if (RTE->rte_mask == DEFAULT_MASK) {
+ // We're deleting a default route.
+ DefGWConfigured--;
+ if (RTE->rte_flags & RTE_VALID)
+ DefGWActive--;
+ if (DefGWActive == 0)
+ ValidateDefaultGWs(NULL_IP_ADDR);
+ }
+
+ InvalidateRCEChain(RTE);
+ // Free the old route.
+ CTEFreeMem(RTE);
+
+}
+
+//* DeleteRTEOnIF - Delete all RTEs on a particular IF.
+//
+// A function called by RTWalk when we want to delete all RTEs on a particular
+// inteface. We just check the I/F of each RTE, and if it matches we return
+// FALSE.
+//
+// Input: RTE - RTE to check.
+// Context - Interface on which we're deleting.
+//
+// Returns: FALSE if we want to delete it, TRUE otherwise.
+//
+uint
+DeleteRTEOnIF(RouteTableEntry *RTE, void *Context, void *Context1)
+{
+ Interface *IF = (Interface *)Context;
+
+ if (RTE->rte_if == IF && !IP_ADDR_EQUAL(RTE->rte_dest, IF->if_bcast))
+ return FALSE;
+ else
+ return TRUE;
+
+}
+
+//* InvalidateRCEOnIF - Invalidate all RCEs on a particular IF.
+//
+// A function called by RTWalk when we want to invalidate all RCEs on a
+// particular inteface. We just check the I/F of each RTE, and if it
+// matches we call InvalidateRCEChain to invalidate the RCEs.
+//
+// Input: RTE - RTE to check.
+// Context - Interface on which we're invalidating.
+//
+// Returns: TRUE.
+//
+uint
+InvalidateRCEOnIF(RouteTableEntry *RTE, void *Context, void *Context1)
+{
+ Interface *IF = (Interface *)Context;
+
+ if (RTE->rte_if == IF)
+ InvalidateRCEChain(RTE);
+
+ return TRUE;
+
+}
+
+//* SetMTUOnIF - Set the MTU on an interface.
+//
+// Called when we need to set the MTU on an interface.
+//
+// Input: RTE - RTE to check.
+// Context - Pointer to a context.
+// Context1 - Pointer to the new MTU.
+//
+// Returns: TRUE.
+//
+uint
+SetMTUOnIF(RouteTableEntry *RTE, void *Context, void *Context1)
+{
+ uint NewMTU = *(uint *)Context1;
+ Interface *IF = (Interface *)Context;
+
+ if (RTE->rte_if == IF)
+ RTE->rte_mtu = NewMTU;
+
+ return TRUE;
+}
+
+//* SetMTUToAddr - Set the MTU to a specific address.
+//
+// Called when we need to set the MTU to a specific address. We set the MTU
+// for all routes that use the specified address as a first hop to the new
+// MTU.
+//
+// Input: RTE - RTE to check.
+// Context - Pointer to a context.
+// Context1 - Pointer to the new MTU.
+//
+// Returns: TRUE.
+//
+uint
+SetMTUToAddr(RouteTableEntry *RTE, void *Context, void *Context1)
+{
+ uint NewMTU = *(uint *)Context1;
+ IPAddr Addr = *(IPAddr *)Context;
+
+ if (IP_ADDR_EQUAL(RTE->rte_addr, Addr))
+ RTE->rte_mtu = NewMTU;
+
+ return TRUE;
+}
+
+//* RTWalk - Routine to walk the route table.
+//
+// This routine walks the route table, calling the specified function
+// for each entry. If the called function returns FALSE, the RTE is
+// deleted.
+//
+// Input: CallFunc - Function to call for each entry.
+// Context - Context value to pass to each call.
+//
+// Returns: Nothing.
+//
+void
+RTWalk(uint (*CallFunc)(struct RouteTableEntry *, void *, void *),
+ void *Context, void *Context1)
+{
+ uint i;
+ CTELockHandle Handle;
+ RouteTableEntry *RTE, *PrevRTE;
+
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ for (i = 0; i < ROUTE_TABLE_SIZE; i++) {
+
+ PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i], rte_next);
+ RTE = RouteTable[i];
+ while (RTE != NULL) {
+ if (!(*CallFunc)(RTE, Context, Context1)) {
+ DeleteRTE(PrevRTE, RTE);
+ } else {
+ PrevRTE = RTE;
+ }
+ RTE = PrevRTE->rte_next;
+ }
+ }
+
+ CTEFreeLock(&RouteTableLock, Handle);
+}
+
+//** AttachRCEToRTE - Attach an RCE to an RTE.
+//
+// This procedure takes an RCE, finds the appropriate RTE, and attaches it.
+// We check to make sure that the source address is still valid.
+//
+// Entry: RCE - RCE to be attached.
+// Protocol - Protocol type for packet causing this call.
+// Buffer - Pointer to buffer for packet causing this
+// call.
+// Length - Length of buffer.
+//
+// Returns: TRUE if we attach it, false if we don't.
+//
+uint
+AttachRCEToRTE(RouteCacheEntry *RCE, uchar Protocol, uchar *Buffer, uint Length)
+{
+ CTELockHandle TableHandle, RCEHandle;
+ RouteTableEntry *RTE;
+ NetTableEntry *NTE;
+ uint Status;
+
+
+ CTEGetLock(&RouteTableLock, &TableHandle);
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next)
+ if ((NTE->nte_flags & NTE_VALID) &&
+ IP_ADDR_EQUAL(RCE->rce_src, NTE->nte_addr))
+ break;
+
+ if (NTE == NULL) {
+ // Didn't find a match.
+ CTEFreeLock(&RouteTableLock, TableHandle);
+ return FALSE;
+ }
+
+ RTE = LookupRTE(RCE->rce_dest, RCE->rce_src, HOST_ROUTE_PRI);
+
+ // See if we found an RTE.
+ if (RTE != NULL) {
+
+ Status = TRUE;
+
+ // Yep, we found one. Get the lock on the RCE, and make sure he's
+ // not pointing at an RTE already. We also need to make sure that the usecnt
+ // is 0, so that we can invalidate the RCE at the low level. If we set valid
+ // to TRUE without doing this we may get into a wierd situation where we
+ // link the RCE onto an RTE but the lower layer information is wrong, so we
+ // send to IP address X at mac address Y. So to be safe we don't set valid
+ // to TRUE until both usecnt is 0 and valid is FALSE. We'll keep coming
+ // through this routine on every send until that happens.
+
+ CTEGetLock(&RCE->rce_lock, &RCEHandle);
+ if (RCE->rce_usecnt == 0) {
+ // Nobody is using him, so we can link him up.
+ if (!(RCE->rce_flags & RCE_VALID)) {
+ Interface *IF;
+ // He's not valid. Invalidate the lower layer info, just in
+ // case. Make sure he's connected before we try to do this. If
+ // he's not marked as connected, don't bother to try and invalidate
+ // him as there is no interface.
+ if (RCE->rce_flags & RCE_CONNECTED) {
+ IF = (Interface *)RCE->rce_rte;
+ (*(IF->if_invalidate))(IF->if_lcontext, RCE);
+#ifdef _PNP_POWER
+ if (RCE->rce_flags & RCE_REFERENCED) {
+ LockedDerefIF(IF);
+ RCE->rce_flags &= ~RCE_REFERENCED;
+ }
+#endif
+ } else {
+ CTEAssert(!(RCE->rce_flags & RCE_REFERENCED));
+ }
+
+ // Link the RCE on the RTE, and set up the back pointer.
+ RCE->rce_rte = RTE;
+ RCE->rce_flags |= RCE_VALID;
+ RCE->rce_next = RTE->rte_rcelist;
+ RTE->rte_rcelist = RCE;
+
+ // Make sure the RTE is connected. If not, try to connect him.
+ if (!(RTE->rte_flags & RTE_IF_VALID)) {
+ // Not connected. Try to connect him.
+ RTE = FindValidIFForRTE(RTE, RCE->rce_dest, RCE->rce_src,
+ Protocol, Buffer, Length);
+ if (RTE != NULL) {
+ // Got one, so mark as connected.
+ CTEAssert(!(RCE->rce_flags & RCE_REFERENCED));
+#ifdef _PNP_POWER
+ RCE->rce_flags |= (RCE_CONNECTED | RCE_REFERENCED);
+ RTE->rte_if->if_refcount++;
+#else
+ RCE->rce_flags |= RCE_CONNECTED;
+
+#endif
+ } else {
+
+ // Couldn't get a valid i/f. Mark the RCE as not connected,
+ // and set up to fail this call.
+ CTEAssert(FALSE);
+ RCE->rce_flags &= ~RCE_CONNECTED;
+ Status = FALSE;
+ }
+ } else {
+ // The RTE is connected, mark the RCE as connected.
+ CTEAssert(!(RCE->rce_flags & RCE_REFERENCED));
+#ifdef _PNP_POWER
+ RCE->rce_flags |= (RCE_CONNECTED | RCE_REFERENCED);
+ RTE->rte_if->if_refcount++;
+#else
+ RCE->rce_flags |= RCE_CONNECTED;
+#endif
+ }
+ } else {
+ // The RCE is valid. See if it's connected.
+ if (!(RCE->rce_flags & RCE_CONNECTED)) {
+
+ // Not connected, try to get a valid i/f.
+ if (!(RTE->rte_flags & RTE_IF_VALID)) {
+ RTE = FindValidIFForRTE(RTE, RCE->rce_dest, RCE->rce_src,
+ Protocol, Buffer, Length);
+ if (RTE != NULL) {
+ RCE->rce_flags |= RCE_CONNECTED;
+#ifdef _PNP_POWER
+ CTEAssert(!(RCE->rce_flags & RCE_REFERENCED));
+ RCE->rce_flags |= RCE_REFERENCED;
+ RTE->rte_if->if_refcount++;
+#endif
+ } else {
+
+ // Couldn't connect, so fail.
+ CTEAssert(FALSE);
+ Status = FALSE;
+ }
+ } else { // Already connected, just mark as valid.
+ RCE->rce_flags |= RCE_CONNECTED;
+#ifdef _PNP_POWER
+ if (!(RCE->rce_flags & RCE_REFERENCED)) {
+ RCE->rce_flags |= RCE_REFERENCED;
+ RTE->rte_if->if_refcount++;
+ }
+#endif
+ }
+ }
+ }
+ }
+
+ // Free the locks and we're done.
+ CTEFreeLock(&RCE->rce_lock, RCEHandle);
+ CTEFreeLock(&RouteTableLock, TableHandle);
+ return Status;
+ } else {
+ // No route! Fail the call.
+ CTEFreeLock(&RouteTableLock, TableHandle);
+ return FALSE;
+ }
+
+}
+//** IPGetPInfo - Get information..
+//
+// Called by an upper layer to get information about a path. We return the
+// MTU of the path and the maximum link speed to be expected on the path.
+//
+// Input: Dest - Destination address.
+// Src - Src address.
+// NewMTU - Where to store path MTU (may be NULL).
+// MaxPathSpeed - Where to store maximum path speed (may be NULL).
+//
+// Returns: Status of attempt to get new MTU.
+//
+IP_STATUS
+IPGetPInfo(IPAddr Dest, IPAddr Src, uint *NewMTU, uint *MaxPathSpeed)
+{
+ CTELockHandle Handle;
+ RouteTableEntry *RTE;
+ IP_STATUS Status;
+
+ CTEGetLock(&RouteTableLock, &Handle);
+ RTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI);
+ if (RTE != NULL) {
+ if (NewMTU != NULL)
+ *NewMTU = RTE->rte_mtu;
+ if (MaxPathSpeed != NULL)
+ *MaxPathSpeed = RTE->rte_if->if_speed;
+ Status = IP_SUCCESS;
+ } else
+ Status = IP_DEST_HOST_UNREACHABLE;
+
+ CTEFreeLock(&RouteTableLock, Handle);
+ return Status;
+
+}
+
+//** IPCheckRoute - Check that a route is valid.
+//
+// Called by an upper layer when it believes a route might be invalid.
+// We'll check if we can. If the upper layer is getting there through a
+// route derived via ICMP (presumably a redirect) we'll check to see
+// if it's been learned within the last minute. If it has, it's assumed
+// to still be valid. Otherwise, we'll mark it as down and try to find
+// another route there. If we can, we'll delete the old route. Otherwise
+// we'll leave it. If the route is through a default gateway we'll switch
+// to another one if we can. Otherwise, we'll just leave - we don't mess
+// with manually configured routes.
+//
+// Input: Dest - Destination to be reached.
+// Src - Src we're sending from.
+//
+// Returns: Nothing.
+//
+void
+IPCheckRoute(IPAddr Dest, IPAddr Src)
+{
+ RouteTableEntry *RTE;
+ RouteTableEntry *NewRTE;
+ RouteTableEntry *TempRTE;
+ CTELockHandle Handle;
+ uint Now = CTESystemUpTime() / 1000L;
+
+ if (DeadGWDetect) {
+ // We are doing dead G/W detection. Get the lock, and try and
+ // find the route.
+ CTEGetLock(&RouteTableLock, &Handle);
+ RTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI);
+ if (RTE != NULL && ((Now - RTE->rte_valid) > MIN_RT_VALID)) {
+
+ // Found a route, and it's older than the minimum valid time. If it
+ // goes through a G/W, and is a route we learned via ICMP or is a
+ // default route, do something with it.
+ if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL)) {
+ // It's not through a G/W.
+
+ if (RTE->rte_proto == IRE_PROTO_ICMP) {
+
+ // Came from ICMP. Mark as invalid, and then make sure
+ // we have another route there.
+ RTE->rte_flags &= ~RTE_VALID;
+ NewRTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI);
+
+ if (NewRTE == NULL) {
+ // Can't get there any other way so leave this
+ // one alone.
+ RTE->rte_flags |= RTE_VALID;
+ } else {
+ // There is another route, so destroy this one. Use
+ // FindSpecificRTE to find the previous RTE.
+ TempRTE = FindSpecificRTE(RTE->rte_dest, RTE->rte_mask,
+ RTE->rte_addr, RTE->rte_if, &NewRTE);
+ CTEAssert(TempRTE == RTE);
+ DeleteRTE(NewRTE, TempRTE);
+ }
+ } else {
+ if (RTE->rte_mask == DEFAULT_MASK) {
+
+ // This is a default gateway. If we have more than one
+ // configured move to the next one.
+
+ if (DefGWConfigured > 1) {
+ // Have more than one. Try the next one. First
+ // invalidate any RCEs on this G/W.
+
+ InvalidateRCEChain(RTE);
+ if (DefGWActive == 1) {
+ // No more active. Revalidate all of them,
+ // and try again.
+ ValidateDefaultGWs(NULL_IP_ADDR);
+ CTEAssert(DefGWActive == DefGWConfigured);
+ } else {
+ // More than one active, so invalidate this
+ // one, and move to the next one. Stamp the
+ // next one with a valid time of Now, so we
+ // don't move from him too easily.
+ --DefGWActive;
+ RTE->rte_flags &= ~RTE_VALID;
+ RTE = FindRTE(Dest, Src, IPHash(0),
+ DEFAULT_ROUTE_PRI, DEFAULT_ROUTE_PRI);
+ if (RTE == NULL) {
+ // No more default gateways! This is bad.
+ CTEAssert(FALSE);
+ ValidateDefaultGWs(NULL_IP_ADDR);
+ CTEAssert(DefGWActive == DefGWConfigured);
+ } else {
+ CTEAssert(RTE->rte_mask == DEFAULT_MASK);
+ RTE->rte_valid = Now;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ CTEFreeLock(&RouteTableLock, Handle);
+ }
+}
+
+
+//** FindRCE - Find an RCE on an RTE.
+//
+// A routine to find an RCE that's chained on an RTE. We assume the lock
+// is held on the RTE.
+//
+// Entry: RTE - RTE to search.
+// Dest - Destination address of RTE to find.
+// Src - Source address of RTE to find.
+//
+// Returns: Pointer to RTE found, or NULL.
+//
+RouteCacheEntry *
+FindRCE(RouteTableEntry *RTE, IPAddr Dest, IPAddr Src)
+{
+ RouteCacheEntry *CurrentRCE;
+
+ CTEAssert(!IP_ADDR_EQUAL(Src, NULL_IP_ADDR));
+ for (CurrentRCE = RTE->rte_rcelist; CurrentRCE != NULL;
+ CurrentRCE = CurrentRCE->rce_next) {
+ if ( IP_ADDR_EQUAL(CurrentRCE->rce_dest, Dest) &&
+ IP_ADDR_EQUAL(CurrentRCE->rce_src, Src)) {
+ break;
+ }
+ }
+ return CurrentRCE;
+
+}
+
+//** OpenRCE - Open an RCE for a specific route.
+//
+// Called by the upper layer to open an RCE. We look up the type of the address
+// - if it's invalid, we return 'Destination invalid'. If not, we look up the
+// route, fill in the RCE, and link it on the correct RTE.
+//
+// As an added bonus, this routine will return the local address to use
+// to reach the destination.
+//
+// Entry: Address - Address for which we are to open an RCE.
+// Src - Source address we'll be using.
+// RCE - Pointer to where to return pointer to RCE.
+// Type - Pointer to where to return destination type.
+// MSS - Pointer to where to return MSS for route.
+// OptInfo - Pointer to option information, such as TOS and
+// any source routing info.
+//
+// Returns: Source IP address to use. This will be NULL_IP_ADDR if the
+// specified destination is unreachable for any reason.
+//
+IPAddr
+OpenRCE(IPAddr Address, IPAddr Src, RouteCacheEntry **RCE, uchar *Type,
+ ushort *MSS, IPOptInfo *OptInfo)
+{
+ RouteTableEntry *RTE; // Pointer to RTE to put RCE on.
+ CTELockHandle TableLock;
+ uchar LocalType;
+
+
+ if (!IP_ADDR_EQUAL(OptInfo->ioi_addr, NULL_IP_ADDR))
+ Address = OptInfo->ioi_addr;
+
+ CTEGetLock(&RouteTableLock, &TableLock);
+
+ // Make sure we're not in DHCP update.
+ if (DHCPActivityCount != 0) {
+ // We are updating DHCP. Just fail this now, since we're in an
+ // indeterminate state.
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return NULL_IP_ADDR;
+ }
+
+ LocalType = GetAddrType(Address);
+
+ *Type = LocalType;
+
+ // If the specified address isn't invalid, continue.
+ if (LocalType != DEST_INVALID) {
+ RouteCacheEntry *NewRCE;
+
+ // If he's specified a source address, loop through the NTE table
+ // now and make sure it's valid.
+ if (!IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) {
+ NetTableEntry *NTE;
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next)
+ if ((NTE->nte_flags & NTE_VALID) &&
+ IP_ADDR_EQUAL(Src, NTE->nte_addr))
+ break;
+
+ if (NTE == NULL) {
+ // Didn't find a match.
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return NULL_IP_ADDR;
+ }
+ }
+
+ // Find the route for this guy. If we can't find one, return NULL.
+ RTE = LookupRTE(Address, Src, HOST_ROUTE_PRI);
+
+ if (RTE != (RouteTableEntry *)NULL) {
+ CTELockHandle RCEHandle;
+ RouteCacheEntry *OldRCE;
+
+ // We found one.
+ *MSS = (ushort)RTE->rte_mtu; // Return the route MTU.
+
+ if (IP_LOOPBACK_ADDR(Src) && (RTE->rte_if != &LoopInterface)) {
+ // The upper layer is sending from a loopback address, but the
+ // destination isn't reachable through the loopback interface.
+ // Fail the request.
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return NULL_IP_ADDR;
+ }
+
+ // We have the RTE. Fill in the RCE, and link it on the RTE.
+ if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL))
+ *Type |= DEST_OFFNET_BIT; // Tell upper layer it's off
+ // net.
+
+ //
+ // If no source address was specified, then use the best address
+ // for the interface. This will generally prevent dynamic NTE's from
+ // being chosen as the source for wildcard binds.
+ //
+ if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) {
+
+ if (LocalType == DEST_LOCAL)
+ Src = Address;
+ else {
+ NetTableEntry *SrcNTE;
+
+ SrcNTE = BestNTEForIF(
+ ADDR_FROM_RTE(RTE, Address),
+ RTE->rte_if
+ );
+
+ if (SrcNTE == NULL) {
+ // Can't find an address! Fail the request.
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return NULL_IP_ADDR;
+ }
+
+ Src = SrcNTE->nte_addr;
+ }
+ }
+
+ // Now, see if an RCE already exists for this.
+ if ((OldRCE = FindRCE(RTE, Address, Src)) == NULL) {
+
+ // Don't have an existing RCE. See if we can get a new one,
+ // and fill it in.
+
+ NewRCE = CTEAllocMem(sizeof(RouteCacheEntry));
+ *RCE = NewRCE;
+
+ if (NewRCE != NULL) {
+ CTEMemSet(NewRCE, 0, sizeof(RouteCacheEntry));
+
+ NewRCE->rce_src = Src;
+ NewRCE->rce_dtype = LocalType;
+ NewRCE->rce_cnt = 1;
+ CTEInitLock(&NewRCE->rce_lock);
+ NewRCE->rce_dest = Address;
+ NewRCE->rce_rte = RTE;
+ NewRCE->rce_flags = RCE_VALID;
+ if (RTE->rte_flags & RTE_IF_VALID) {
+ NewRCE->rce_flags |= RCE_CONNECTED;
+#ifdef _PNP_POWER
+ //* Update the ref. count for this interface.
+ NewRCE->rce_flags |= RCE_REFERENCED;
+ RTE->rte_if->if_refcount++;
+#endif
+ }
+ NewRCE->rce_next = RTE->rte_rcelist;
+ RTE->rte_rcelist = NewRCE;
+ }
+
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return Src;
+ } else {
+ // We have an existing RCE. We'll return his source as the
+ // valid source, bump the reference count, free the locks
+ // and return.
+ CTEGetLock(&OldRCE->rce_lock, &RCEHandle);
+ OldRCE->rce_cnt++;
+ *RCE = OldRCE;
+ CTEFreeLock(&OldRCE->rce_lock, RCEHandle);
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return Src;
+ }
+ } else {
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return NULL_IP_ADDR;
+ }
+ }
+
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return NULL_IP_ADDR;
+}
+
+//* CloseRCE - Close an RCE.
+//
+// Called by the upper layer when it wants to close the RCE. We unlink it from
+// the RTE.
+//
+// Entry: RCE - Pointer to the RCE to be closed.
+//
+// Exit: Nothing.
+//
+void
+CloseRCE(RouteCacheEntry *RCE)
+{
+ RouteTableEntry *RTE; // Route on which RCE is linked.
+ RouteCacheEntry *PrevRCE;
+ CTELockHandle TableLock; // Lock handles used.
+ CTELockHandle RCEHandle;
+ Interface *IF;
+
+ if (RCE != NULL) {
+ CTEGetLock(&RouteTableLock, &TableLock);
+ CTEGetLock(&RCE->rce_lock, &RCEHandle);
+
+ if (--RCE->rce_cnt == 0) {
+ CTEAssert(RCE->rce_usecnt == 0);
+ if (RCE->rce_flags & RCE_VALID) {
+ // The RCE is valid, so we have a valid RTE in the pointer
+ // field. Walk down the RTE rcelist, looking for this guy.
+
+ RTE = RCE->rce_rte;
+ IF = RTE->rte_if;
+
+ PrevRCE = STRUCT_OF(RouteCacheEntry, &RTE->rte_rcelist,
+ rce_next);
+
+ // Walk down the list until we find him.
+ while (PrevRCE != NULL) {
+ if (PrevRCE->rce_next == RCE)
+ break;
+ PrevRCE = PrevRCE->rce_next;
+ }
+
+ CTEAssert(PrevRCE != NULL);
+ PrevRCE->rce_next = RCE->rce_next;
+ } else
+ IF = (Interface *)RCE->rce_rte;
+
+ if (RCE->rce_flags & RCE_CONNECTED) {
+ (*(IF->if_invalidate))(IF->if_lcontext, RCE);
+ }
+
+#ifdef _PNP_POWER
+ if (RCE->rce_flags & RCE_REFERENCED) {
+ LockedDerefIF(IF);
+ }
+#endif
+ CTEFreeLock(&RCE->rce_lock, RCEHandle);
+ CTEFreeMem(RCE);
+ }
+ else {
+ CTEFreeLock(&RCE->rce_lock, RCEHandle);
+ }
+
+ CTEFreeLock(&RouteTableLock, TableLock);
+
+ }
+
+}
+
+
+//* LockedAddRoute - Add a route to the routing table.
+//
+// Called by AddRoute to add a route to the routing table. We assume the
+// route table lock is already held. If the route to be added already exists
+// we update it. Routes are identified by a (Destination, Mask, FirstHop,
+// Interface) tuple. If an exact match exists we'll update the metric, which
+// may cause us to promote RCEs from other RTEs, or we may be demoted in which
+// case we'll invalidate our RCEs and let them be reassigned at transmission
+// time.
+//
+// If we have to create a new RTE we'll do so, and find the best previous
+// RTE, and promote RCEs from that one to the new one.
+//
+// The route table is an open hash structure. Within each hash chain the
+// RTEs with the longest masks (the 'priority') come first, and within
+// each priority the RTEs with the smallest metric come first.
+//
+//
+// Entry: Destination - Destination address for which route is being
+// added.
+// Mask - Mask for destination.
+// FirstHop - First hop for address. Could be IPADDR_LOCAL.
+// OutIF - Pointer to outgoing I/F.
+// MTU - Maximum MTU for this route.
+// Metric - Metric for this route.
+// Proto - Protocol type to store in route.
+// AType - Administrative type of route.
+//
+// Returns: Status of attempt to add route.
+//
+IP_STATUS
+LockedAddRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop,
+ Interface *OutIF, uint MTU, uint Metric, uint Proto, uint AType,
+ ROUTE_CONTEXT Context)
+{
+ uint RouteType; // SNMP route type.
+ RouteTableEntry *NewRTE, *OldRTE; // Entries for new and previous
+ // RTEs.
+ RouteTableEntry *PrevRTE; // Pointer to previous RTE.
+ CTELockHandle RCEHandle; // Lock handle for RCEs.
+ uint OldMetric; // Previous metric in use.
+ uint OldPriority; // Priority of previous route to
+ // destination.
+ RouteCacheEntry *CurrentRCE; // Current RCE being examined.
+ RouteCacheEntry *PrevRCE; // Previous RCE examined.
+ Interface *IF; // Interface being added on.
+ uint Priority; // Priority of the route.
+ uint TempMask; // Temporary copy of the mask.
+ uint Now = CTESystemUpTime() / 1000L; // System up time,
+ // in seconds.
+ uint MoveAny; // TRUE if we'll move any RCE.
+ ushort OldFlags;
+
+ // First do some consistency checks. Make sure that the Mask and
+ // Destination agree.
+ if (!IP_ADDR_EQUAL(Destination & Mask, Destination))
+ return IP_BAD_DESTINATION;
+
+ if (AType != ATYPE_PERM && AType != ATYPE_OVERRIDE && AType != ATYPE_TEMP)
+ return IP_BAD_REQ;
+
+#ifdef _PNP_POWER
+ // If the interface is marked as going away, fail this.
+ if (OutIF->if_flags & IF_FLAGS_DELETING) {
+ return IP_BAD_REQ;
+ }
+#endif
+
+ RouteType = IP_ADDR_EQUAL(FirstHop, IPADDR_LOCAL) ? IRE_TYPE_DIRECT :
+ IRE_TYPE_INDIRECT;
+
+
+ MTU = MAX(MTU, MIN_VALID_MTU);
+
+ // If the outgoing interface has NTEs attached but none are valid, fail
+ // this request unless it's a request to add the broadcast route.
+ if (OutIF != (Interface *)&DummyInterface) {
+ if (OutIF->if_ntecount == 0 && OutIF->if_nte != NULL &&
+ !IP_ADDR_EQUAL(Destination, OutIF->if_bcast) ) {
+ // This interface has NTEs attached, but none are valid. Fail the
+ // request.
+ return IP_BAD_REQ;
+ }
+ }
+
+
+ // First look and see if the RTE already exists.
+ NewRTE = FindSpecificRTE(Destination, Mask, FirstHop, OutIF, &PrevRTE);
+
+ if (NewRTE != NULL) {
+
+ // The RTE already exists, and we have a lock on it. See if we're
+ // updating the metric. If we're not, just return. Otherwise
+ // we'll remove the RTE and update the metric. If the metric is
+ // increasing, walk down the RCE chain on the RTE and invalidate
+ // the RCE back pointers so that they'll be revalidated upon
+ // transmits, and then reinsert the RTE. If the metric is
+ // decreasing we'll just fall through to the case below where we'll
+ // promote RCEs and insert the RTE.
+
+
+ // Update the things that won't cause routing table motion.
+ NewRTE->rte_mtu = MTU;
+ NewRTE->rte_proto = Proto;
+ NewRTE->rte_valid = Now;
+ NewRTE->rte_mtuchange = Now;
+ NewRTE->rte_context = Context;
+ OldFlags = NewRTE->rte_flags;
+
+ // We always turn off the increase flag when a route is updated, so
+ // that we take the long timeout to try to increase it. We also
+ // always turn on the valid flag.
+ NewRTE->rte_flags = (OldFlags & ~RTE_INCREASE) | RTE_VALID;
+ if (OutIF != (Interface *)&DummyInterface) {
+ NewRTE->rte_flags |= RTE_IF_VALID;
+ } else {
+
+ //
+ // Invalidating a previously valid route
+ //
+
+ NewRTE->rte_flags &= ~RTE_IF_VALID;
+ }
+
+ // If the RTE is for a default gateway and the old flags indicate
+ // he wasn't valid then we're essentially creating a new active
+ // default gateway. In the case bump the active default gateway count.
+ if (NewRTE->rte_mask == DEFAULT_MASK && !(OldFlags & RTE_VALID))
+ DefGWActive++;
+
+ // Need to update the metric, which will cause this RTE to move
+ // in the table.
+ OldMetric = NewRTE->rte_metric; // Save the old one.
+ RemoveRTE(PrevRTE, NewRTE); // Pull him from the chain.
+ NewRTE->rte_metric = Metric;
+
+ // Now see if we're increasing or decreasing the metric, or
+ // re-validating a previously invalid route. By definition, if the
+ // route wasn't valid before this there can't have been any RCEs
+ // attached to it, so we'll fall through to the promotion code
+ // and see if we can move any onto it. Otherwise, if we're
+ // demoting this route invalidate any RCEs on it.
+
+ if (Metric >= OldMetric && (OldFlags & RTE_VALID)) {
+ // We're increasing it, or leaving it the same. His valid state
+ // may have changed, so we'll we'll invalidate any RCEs on him
+ // now in any case.
+ InsertRTE(NewRTE);
+
+ InvalidateRCEChain(NewRTE);
+
+ //
+ // Whether the IF has changed or not, we can always overwrite it
+ // We wait till here to overwrite because if old rte_if was
+ // not dummy if, then we will want to invalidate RCEChain
+ // Since the invalidate function is stored in the interface
+ // structure we want to keep it around
+ //
+
+ NewRTE->rte_if = OutIF;
+
+ return IP_SUCCESS;
+ }
+
+
+ NewRTE->rte_if = OutIF;
+
+ // The metric is less than the old metric, so we're promoting this
+ // RTE. Fall through to the code below that deals with this, after
+ // saving the priority of the RTE.
+ Priority = NewRTE->rte_priority;
+
+ } else {
+
+ // Didn't find a matching RTE. Allocate a new one, and fill it in.
+ NewRTE = CTEAllocMem(sizeof(RouteTableEntry));
+
+ if (NewRTE == NULL) {
+ // Couldn't get the memory.
+ return IP_NO_RESOURCES;
+ }
+
+ IPSInfo.ipsi_numroutes++;
+
+ // Fill him in, and take the lock on him. To do this we'll need to
+ // calculate the priority.
+ Priority = 0;
+ TempMask = Mask;
+
+ while (TempMask) {
+ Priority += TempMask & 1;
+ TempMask >>= 1;
+ }
+
+ // Now initialize all of his fields.
+ NewRTE->rte_priority = Priority;
+
+ NewRTE->rte_dest = Destination;
+ NewRTE->rte_mask = Mask;
+ if (Mask == DEFAULT_MASK) {
+ // We're adding a default route.
+ DefGWConfigured++;
+ DefGWActive++;
+ }
+ NewRTE->rte_addr = FirstHop;
+ NewRTE->rte_metric = Metric;
+ NewRTE->rte_mtu = MTU;
+ NewRTE->rte_if = OutIF;
+ NewRTE->rte_rcelist = NULL;
+ NewRTE->rte_type = (ushort)RouteType;
+ NewRTE->rte_flags = RTE_VALID;
+ if (OutIF != (Interface *)&DummyInterface) {
+ NewRTE->rte_flags |= RTE_IF_VALID;
+ }
+ NewRTE->rte_proto = Proto;
+ NewRTE->rte_valid = Now;
+ NewRTE->rte_mtuchange = Now;
+ NewRTE->rte_admintype = AType;
+ NewRTE->rte_context = Context;
+ }
+
+ // At this point, NewRTE points to an initialized RTE that is not in the
+ // table. We hold the lock on NewRTE and on the RouteTable. First we'll
+ // find where we'll eventually insert the new RTE (we can't insert it
+ // yet, because we'll still need to search the table again and we can't
+ // do that while we hold a lock on an element in the table). Then we'll
+ // search the table for the next best route (i.e. a route with a priority
+ // less than or equal to ours), and if we find one we'll promote RCEs from
+ // that route to us. We'll actually have to search a chain of RTEs.
+
+ PrevRTE = FindInsertPoint(NewRTE);
+ OldRTE = LookupRTE(Destination, NULL_IP_ADDR, Priority);
+
+ // If we found one, try to promote from him.
+ if (OldRTE != NULL) {
+
+ // Found another RTE, and we have the lock on it. Starting with him,
+ // walk down the chain. At the start of this loop we know that the
+ // route described by OldRTE can reach our destination. If his metric
+ // is better than ours, we're done and we'll just insert our route.
+ // If his priority and metric are equal to ours we'll promote only
+ // those RCEs that exactly match our source address. Otherwise either
+ // his priority or metric is worse than ours, and we'll promote all
+ // appropriate RCEs.
+ //
+ // Since we specified the maximum priority in the call to LookupRTE
+ // as our priority, we know the priority of the route we found
+ // can't be greater than our priority.
+
+ OldMetric = OldRTE->rte_metric;
+ OldPriority = OldRTE->rte_priority;
+
+ // We'll do the search if his priority is less than ours, or his
+ // metric is > (i.e. worse than) our metric, or his metric equals
+ // our metric and the old route is on a different interface. We
+ // know OldPriority is <= our Priority, since we specified our
+ // priority in the call to LookupRTE above.
+
+ CTEAssert(OldPriority <= Priority);
+
+ if (OldPriority < Priority || OldMetric > Metric ||
+ (OldMetric == Metric && OldRTE->rte_if != OutIF)) {
+
+ // We're going to search. Figure out the mask to use in comparing
+ // source addresses.
+ if (OldPriority == Priority && OldMetric == Metric)
+ MoveAny = FALSE; // Only promote an exact match.
+ else
+ MoveAny = TRUE; // Promote any match.
+
+ for (;;) {
+ IF = OldRTE->rte_if;
+
+ PrevRCE = STRUCT_OF(RouteCacheEntry, &OldRTE->rte_rcelist,
+ rce_next);
+ CurrentRCE = PrevRCE->rce_next;
+ // Walk the list, promoting any that match.
+ while (CurrentRCE != NULL) {
+ CTEGetLock(&CurrentRCE->rce_lock, &RCEHandle);
+
+ // If the masked source address matches our masked address,
+ // and the destinations match, promote him.
+ if ((MoveAny || AddrOnIF(OutIF, CurrentRCE->rce_src)) &&
+ IP_ADDR_EQUAL(CurrentRCE->rce_dest & Mask, Destination)) {
+ // He matches. Pull him from the list and mark him as invalid.
+ // This will force a new lookup of the route next time he's
+ // used, which as a side effect will cause the route to be
+ // connected in the dial-on-demand case.
+ CTEAssert(CurrentRCE->rce_flags & RCE_VALID);
+ PrevRCE->rce_next = CurrentRCE->rce_next;
+ CurrentRCE->rce_flags &= ~RCE_VALID;
+ CurrentRCE->rce_rte = (RouteTableEntry *)IF;
+ if (CurrentRCE->rce_usecnt == 0) {
+ // No one's currently using him, so invalidate him.
+ if (CurrentRCE->rce_flags & RCE_CONNECTED) {
+ (*(IF->if_invalidate))(IF->if_lcontext, CurrentRCE);
+#ifdef _PNP_POWER
+ if (CurrentRCE->rce_flags & RCE_REFERENCED) {
+ LockedDerefIF(IF);
+ CurrentRCE->rce_flags &= ~RCE_REFERENCED;
+ }
+#endif
+ } else {
+#ifdef _PNP_POWER
+ CTEAssert(!(CurrentRCE->rce_flags & RCE_REFERENCED));
+#endif
+ }
+ }
+
+ } else {
+ // Doesn't match. Try the next one.
+ PrevRCE = CurrentRCE;
+ }
+ CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle);
+ CurrentRCE = PrevRCE->rce_next;
+ }
+
+ // We've walked the RCE list on that old RTE. Look at the
+ // next one. If it has the same priority and metric as the
+ // old one, and also matches our destination, check it also. We
+ // don't need to RTEs that don't match this criteria, since we know
+ // RCEs are always kept on the 'best' RTE.
+ OldRTE = OldRTE->rte_next;
+ if (OldRTE != NULL) {
+ // We have another one. Check it out.
+ if (OldRTE->rte_priority == Priority &&
+ OldRTE->rte_metric == OldMetric &&
+ IP_ADDR_EQUAL(Destination & OldRTE->rte_mask,
+ OldRTE->rte_dest))
+ continue; // It matches, so try to promote
+ // RCEs.
+ }
+ break; // Exit out of the for (;;) loop.
+ }
+ } else {
+ // OldRTE is a better route than the one we're inserting, so don't
+ // do anything.
+ }
+ }
+
+ // At this point we're promoted any routes we need to, we hold the lock
+ // on NewRTE which still isn't inserted, and PrevRTE describes where to
+ // insert NewRTE. Insert it, free the lock, and return success.
+ InsertAfterRTE(PrevRTE, NewRTE);
+ return IP_SUCCESS;
+
+}
+
+//* AddRoute - Add a route to the routing table.
+//
+// This is just a shell for the real add route routine. All we do is take
+// the route table lock, and call the LockedAddRoute routine to deal with
+// the request. This is done this way because there are certain routines that
+// need to be able to atomically examine and add routes.
+//
+// Entry: Destination - Destination address for which route is being
+// added.
+// Mask - Mask for destination.
+// FirstHop - First hop for address. Could be IPADDR_LOCAL.
+// OutIF - Pointer to outgoing I/F.
+// MTU - Maximum MTU for this route.
+// Metric - Metric for this route.
+// Proto - Protocol type to store in route.
+// AType - Administrative type of route.
+// Context - Context for this route.
+//
+// Returns: Status of attempt to add route.
+//
+IP_STATUS
+AddRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop,
+ Interface *OutIF, uint MTU, uint Metric, uint Proto, uint AType,
+ ROUTE_CONTEXT Context)
+{
+ CTELockHandle TableHandle;
+ IP_STATUS Status;
+
+ CTEGetLock(&RouteTableLock, &TableHandle);
+ Status = LockedAddRoute(Destination, Mask, FirstHop, OutIF, MTU, Metric,
+ Proto, AType, Context);
+
+ CTEFreeLock(&RouteTableLock, TableHandle);
+ return Status;
+}
+
+//* DeleteRoute - Delete a route from the routing table.
+//
+// Called by upper layer or management code to delete a route from the routing
+// table. If we can't find the route we return an error. If we do find it, we
+// remove it, and invalidate any RCEs associated with it. These RCEs will be
+// reassigned the next time they're used. A route is uniquely identified by
+// a (Destination, Mask, FirstHop, Interface) tuple.
+//
+// Entry: Destination - Destination address for which route is being
+// deleted.
+// Mask - Mask for destination.
+// FirstHop - First hop on way to Destination. -1 means route is
+// local.
+// OutIF - Outgoing interface for route.
+//
+// Returns: Status of attempt to delete route.
+//
+IP_STATUS
+DeleteRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop, Interface *OutIF)
+{
+ RouteTableEntry *RTE; // RTE being deleted.
+ RouteTableEntry *PrevRTE; // Pointer to RTE in front of one
+ // being deleted.
+ CTELockHandle TableLock; // Lock handle for table.
+
+
+ // Look up the route by calling FindSpecificRTE. If we can't find it,
+ // fail the call.
+ CTEGetLock(&RouteTableLock, &TableLock);
+ RTE = FindSpecificRTE(Destination, Mask, FirstHop, OutIF, &PrevRTE);
+
+ if (RTE == NULL) {
+ // Didn't find the route, so fail the call.
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return IP_BAD_ROUTE;
+ }
+
+#ifndef NT
+//
+// Disable admin check for NT, because the RAS server needs to be able to
+// delete a subnet route. This ability is restricted to Administrators only
+// by NT security checks.
+//
+//
+ if (RTE->rte_admintype == ATYPE_PERM) {
+ // Can't delete a permanent route.
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return IP_BAD_REQ;
+ }
+#endif
+
+ // When we reach here we hold the lock on the RTE to be deleted, and
+ // PrevRTE points to the RTE immediately ahead of ours in the table.
+ // Call DeleteRTE to delete him.
+ DeleteRTE(PrevRTE, RTE);
+
+ CTEFreeLock(&RouteTableLock, TableLock);
+ return IP_SUCCESS;
+
+
+}
+
+//** Redirect - Process a redirect request.
+//
+// This is the redirect handler . We treat all redirects as host redirects as per the
+// host requirements RFC. We make a few sanity checks on the new first hop address, and then
+// we look up the current route. If it's not through the source of the redirect, just return.
+// If the current route to the destination is a host route, update the first hop and return.
+// If the route is not a host route, remove any RCE for this route from the RTE, create a
+// host route and place the RCE (if any) on the new RTE.
+//
+// Entry: NTE - Pointer to NetTableEntry for net on which Redirect
+// arrived.
+// RDSrc - IPAddress of source of redirect.
+// Target - IPAddress being redirected.
+// Src - Src IP address of DG that triggered RD.
+// FirstHop - New first hop for Target.
+//
+// Returns: Nothing.
+//
+void
+Redirect(NetTableEntry *NTE, IPAddr RDSrc, IPAddr Target, IPAddr Src,
+ IPAddr FirstHop)
+{
+ uint MTU;
+ RouteTableEntry *RTE;
+ CTELockHandle Handle;
+
+ if (IP_ADDR_EQUAL(FirstHop, NULL_IP_ADDR) || IP_LOOPBACK(FirstHop))
+ return; // Can't redirect to loopback
+ // address.
+
+ CTEAssert(IP_ADDR_EQUAL(NTE->nte_addr, Src));
+
+ // First make sure that this came from the gateway we're currently using to
+ // get to Target, and then lookup up the route to the new first hop. The new
+ // firsthop must be directly reachable, and on the same subnetwork or
+ // physical interface on which we received the redirect.
+
+
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ // Make sure the source of the redirect is the current first hop gateway.
+ RTE = LookupRTE(Target, Src, HOST_ROUTE_PRI);
+ if (RTE == NULL || IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL) ||
+ !IP_ADDR_EQUAL(RTE->rte_addr, RDSrc)) {
+ CTEFreeLock(&RouteTableLock, Handle);
+ return; // A bad redirect.
+ }
+
+ CTEAssert(RTE->rte_flags & RTE_IF_VALID);
+
+ // If the current first hop gateway is a default gateway, see if we have
+ // another default gateway at FirstHop that is down. If so, mark him as
+ // up and invalidate the RCEs on this guy.
+ if (RTE->rte_mask == DEFAULT_MASK && ValidateDefaultGWs(FirstHop) != 0) {
+ // Have a default gateway that's been newly activated. Invalidate RCEs
+ // on the route, and we're done.
+ InvalidateRCEChain(RTE);
+ CTEFreeLock(&RouteTableLock, Handle);
+ return;
+ }
+
+ // We really need to add a host route through FirstHop. Make sure he's
+ // a valid first hop.
+ RTE = LookupRTE(FirstHop, Src, HOST_ROUTE_PRI);
+ if (RTE == NULL) {
+ CTEFreeLock(&RouteTableLock, Handle);
+ return; // Can't get there from here.
+ }
+
+ CTEAssert(RTE->rte_flags & RTE_IF_VALID);
+
+
+ // Check to make sure the new first hop is directly reachable, and is on the
+ // same subnet or physical interface we received the redirect on.
+ if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL) || // Not directly reachable
+ // or wrong subnet.
+ ((NTE->nte_addr & NTE->nte_mask) != (FirstHop & NTE->nte_mask))) {
+ CTEFreeLock(&RouteTableLock, Handle);
+ return;
+ }
+
+ MTU = RTE->rte_mtu;
+
+ // Now add a host route. AddRoute will do the correct things with shifting
+ // RCEs around. We know that FirstHop is on the same subnet as NTE (from
+ // the check above), so it's valid to add the route to FirstHop as out
+ // going through NTE.
+ LockedAddRoute(Target, HOST_MASK, IP_ADDR_EQUAL(FirstHop, Target) ? IPADDR_LOCAL :
+ FirstHop, NTE->nte_if, MTU, 1, IRE_PROTO_ICMP, ATYPE_OVERRIDE,
+ RTE->rte_context);
+
+ CTEFreeLock(&RouteTableLock, Handle);
+}
+
+//* GetRaisedMTU - Get the next largest MTU in table..
+//
+// A utility function to search the MTU table for a larger value.
+//
+// Input: PrevMTU - MTU we're currently using. We want the
+// next largest one.
+//
+// Returns: New MTU size.
+//
+uint
+GetRaisedMTU(uint PrevMTU)
+{
+ uint i;
+
+ for (i = (sizeof(MTUTable)/sizeof(uint)) - 1; i != 0; i--) {
+ if (MTUTable[i] > PrevMTU)
+ break;
+ }
+
+ return MTUTable[i];
+}
+
+//* GuessNewMTU - Guess a new MTU, giving a DG size too big.
+//
+// A utility function to search the MTU table. As input we take in an MTU
+// size we believe to be too large, and search the table looking for the
+// next smallest one.
+//
+// Input: TooBig - Size that's too big.
+//
+// Returns: New MTU size.
+//
+uint
+GuessNewMTU(uint TooBig)
+{
+ uint i;
+
+ for (i = 0; i < ((sizeof(MTUTable)/sizeof(uint)) - 1); i++)
+ if (MTUTable[i] < TooBig)
+ break;
+
+ return MTUTable[i];
+}
+
+//* RouteFragNeeded - Handle being told we need to fragment.
+//
+// Called when we receive some external indication that we need to fragment
+// along a particular path. If we're doing MTU discovery we'll try to
+// update the route, if we can. We'll also notify the upper layers about
+// the new MTU.
+//
+// Input: IPH - Pointer to IP Header of datagram needing
+// fragmentation.
+// NewMTU - New MTU to be used (may be 0).
+//
+// Returns: Nothing.
+//
+void
+RouteFragNeeded(IPHeader UNALIGNED *IPH, ushort NewMTU)
+{
+ uint OldMTU;
+ CTELockHandle Handle;
+ RouteTableEntry *RTE;
+ ushort HeaderLength;
+
+ // If we're not doing PMTU discovery, don't do anything.
+ if (PMTUDiscovery) {
+
+ // We're doing PMTU discovery. Correct the given new MTU for the IP
+ // header size, which we don't save as we track MTUs.
+ if (NewMTU != 0) {
+ // Make sure the new MTU we got is at least the minimum valid size.
+ NewMTU = MAX(NewMTU, MIN_VALID_MTU);
+ NewMTU -= sizeof(IPHeader);
+ }
+
+ HeaderLength = (IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2;
+
+ // Get the current routing information.
+
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ // Find an RTE for the destination.
+ RTE = LookupRTE(IPH->iph_dest, IPH->iph_src, HOST_ROUTE_PRI);
+
+ // If we couldn't find one, or the existing MTU is less than the new
+ // MTU, give up now.
+
+ if (RTE == NULL || (OldMTU = RTE->rte_mtu) < NewMTU) {
+ // No RTE, or an invalid new MTU. Just bail out now.
+ CTEFreeLock(&RouteTableLock, Handle);
+ return;
+ }
+
+ // If the new MTU is zero, figure out what the new MTU should be.
+ if (NewMTU == 0) {
+ ushort DGLength;
+
+ // The new MTU is zero. We'll make a best guess what the new
+ // MTU should be. We have the RTE for this route already.
+
+
+ // Get the length of the datagram that triggered this. Since we'll
+ // be comparing it against MTU values that we track without the
+ // IP header size included, subtract off that amount.
+ DGLength = (ushort)net_short(IPH->iph_length) - sizeof(IPHeader);
+
+ // We may need to correct this as per RFC 1191 for dealing with
+ // old style routers.
+ if (DGLength >= OldMTU) {
+ // The length of the datagram sent is not less than our
+ // current MTU estimate, so we need to back it down (assuming
+ // that the sending route has incorrectly added in the header
+ // length).
+ DGLength -= HeaderLength;
+
+ }
+
+ // If it's still larger than our current MTU, use the current
+ // MTU. This could happen if the upper layer sends a burst of
+ // packets which generate a sequence of ICMP discard messages. The
+ // first one we receive will cause us to lower our MTU. We then
+ // want to discard subsequent messages to avoid lowering it
+ // too much. This could conceivably be a problem if our
+ // first adjustment still results in an MTU that's too big,
+ // but we should converge adequately fast anyway, and it's
+ // better than accidentally underestimating the MTU.
+
+ if (DGLength > OldMTU)
+ NewMTU = OldMTU;
+ else
+ // Move down the table to the next lowest MTU.
+ NewMTU = GuessNewMTU(DGLength);
+ }
+
+ // We have the new MTU. Now add it to the table as a host route.
+ if (NewMTU != OldMTU)
+ LockedAddRoute(IPH->iph_dest, HOST_MASK, RTE->rte_addr, RTE->rte_if,
+ NewMTU, RTE->rte_metric, IRE_PROTO_ICMP, ATYPE_OVERRIDE,
+ RTE->rte_context);
+
+ CTEFreeLock(&RouteTableLock, Handle);
+
+ // We've added the route. Now notify the upper layers of the change.
+ ULMTUNotify(IPH->iph_dest, IPH->iph_src, IPH->iph_protocol,
+ (void *)((uchar *)IPH + HeaderLength), NewMTU);
+
+ }
+}
+
+//** IPRouteTimeout - IP routeing timeout handler.
+//
+// The IP routeing timeout routine, called once a minute. We look at all
+// host routes, and if we raise the MTU on them we do so.
+//
+// Entry: Timer - Timer being fired.
+// Context - Pointer to NTE being time out.
+//
+// Returns: Nothing.
+//
+void
+IPRouteTimeout(CTEEvent *Timer, void *Context)
+{
+ uint Now = CTESystemUpTime() / 1000L;
+ CTELockHandle Handle;
+ uint i;
+ RouteTableEntry *RTE, *PrevRTE;
+ uint RaiseMTU, Delta;
+ Interface *IF;
+ IPAddr Dest;
+ uint NewMTU;
+ NetTableEntry *NTE;
+
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ for (i = 0; i < ROUTE_TABLE_SIZE; i++) {
+ // Walk down each chain, looking at the host routes. If we're
+ // doing PMTU discovery, see if we can raise the MTU.
+ PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i], rte_next);
+ RTE = RouteTable[i];
+ while (RTE != NULL && RTE->rte_mask == HOST_MASK) {
+ // Make sure he's valid.
+ if (RTE->rte_flags & RTE_VALID) {
+ if (PMTUDiscovery) {
+ // Check to see if we can raise the MTU on this guy.
+ Delta = Now - RTE->rte_mtuchange;
+
+ if (RTE->rte_flags & RTE_INCREASE)
+ RaiseMTU = (Delta >= MTU_INCREASE_TIME ? 1 : 0);
+ else
+ RaiseMTU = (Delta >= MTU_DECREASE_TIME ? 1 : 0);
+
+ if (RaiseMTU) {
+ // We need to raise this MTU. Set his change time to
+ // Now, so we don't do this again, and figure out
+ // what the new MTU should be.
+ RTE->rte_mtuchange = Now;
+ IF = RTE->rte_if;
+ if (RTE->rte_mtu < IF->if_mtu) {
+
+ RTE->rte_flags |= RTE_INCREASE;
+ // This is a candidate for change. Figure out
+ // what it should be.
+ NewMTU = MIN(GetRaisedMTU(RTE->rte_mtu),
+ IF->if_mtu);
+ RTE->rte_mtu = NewMTU;
+ Dest = RTE->rte_dest;
+
+ // We have the new MTU. Free the lock, and walk
+ // down the NTEs on the I/F. For each NTE,
+ // call up to the upper layer and tell him what
+ // his new MTU is.
+ CTEFreeLock(&RouteTableLock, Handle);
+ NTE = IF->if_nte;
+ while (NTE != NULL) {
+ if (NTE->nte_flags & NTE_VALID) {
+ ULMTUNotify(Dest, NTE->nte_addr, 0, NULL,
+ MIN(NewMTU, NTE->nte_mss));
+ }
+ NTE = NTE->nte_ifnext;
+ }
+
+ // We've notified everyone. Get the lock again,
+ // and start from the first element of this
+ // chain in case something's changed after we
+ // free the lock. We've updated the mtuchange
+ // time of this RTE, so we won't hit him again.
+ CTEGetLock(&RouteTableLock, &Handle);
+ PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i],
+ rte_next);
+ RTE = RouteTable[i];
+ continue;
+ } else
+ RTE->rte_flags &= ~RTE_INCREASE;
+ }
+ }
+ // If this route came in via ICMP, and we have no RCEs on it,
+ // and it's at least 10 minutes old, delete it.
+ if (RTE->rte_proto == IRE_PROTO_ICMP &&
+ RTE->rte_rcelist == NULL &&
+ (Now - RTE->rte_valid) > MAX_ICMP_ROUTE_VALID) {
+ // He needs to be deleted. Call DeleteRTE to do this.
+ DeleteRTE(PrevRTE, RTE);
+ RTE = PrevRTE->rte_next;
+ continue;
+ }
+ }
+ PrevRTE = RTE;
+ RTE = RTE->rte_next;
+ }
+ }
+
+ CTEFreeLock(&RouteTableLock, Handle);
+ CTEStartTimer(&IPRouteTimer, IP_ROUTE_TIMEOUT, IPRouteTimeout, NULL);
+
+}
+
+//* FreeFWPacket - Free a forwarding packet when we're done with it.
+//
+//
+// Input: Packet - Packet to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeFWPacket(PNDIS_PACKET Packet)
+{
+ CTELockHandle Handle;
+ FWContext *FWC;
+
+// BUGBUG - Portability issue
+
+#ifdef VXD
+ Packet->Private.Head = (PNDIS_BUFFER)NULL;
+ Packet->Private.Count = 0;
+ Packet->Private.PhysicalCount = 0;
+ Packet->Private.TotalLength = 0;
+#else // VXD
+#ifdef NT
+ //
+ // BUGBUG: This is inefficient. Need something better.
+ //
+ NdisReinitializePacket(Packet);
+#else // NT
+#error Need portable way to do this.
+#endif // NT
+#endif // VXD
+
+ FWC = (FWContext *)Packet->ProtocolReserved;
+ if (FWC->fc_options) {
+ CTEFreeMem(FWC->fc_options);
+ FWC->fc_options = (uchar *)NULL;
+ }
+
+ if (FWC->fc_buffhead) {
+ CTEGetLock(&FWBufFreeLock, &Handle);
+ FWC->fc_bufftail->Next = FWBufFree; // BUGBUG more portable.
+ FWBufFree = FWC->fc_buffhead;
+ CTEFreeLock(&FWBufFreeLock, Handle);
+ FWC->fc_buffhead = (PNDIS_BUFFER)NULL;
+ }
+#ifdef _PNP_POWER
+ // If there's an interface pointer here, dereference in now.
+ if (FWC->fc_if != NULL) {
+ DerefIF(FWC->fc_if);
+ FWC->fc_if = NULL;
+ }
+#endif
+
+ CTEGetLock(&FWPacketFreeLock, &Handle);
+ FWC->fc_pc.pc_common.pc_link = FWPacketFree;
+ FWPacketFree = Packet;
+ CTEFreeLock(&FWPacketFreeLock, Handle);
+
+}
+
+//* GrowFWPackets - Grow the FW packet list, if we can.
+//
+// Called when we need to allocate a FW packet, but don't have one. We'll try to grow the
+// FWPacket list now.
+//
+// Input: Nothing.
+//
+// Returns: TRUE if we succeeded in growing the list, FALSE otherwise.
+//
+uint
+GrowFWPackets(void)
+{
+ CTELockHandle Handle;
+ IPHeader *HeaderPtr;
+ NDIS_HANDLE BufferPool;
+ NDIS_HANDLE PacketPool;
+ PNDIS_BUFFER Buffer;
+ PNDIS_PACKET Packet;
+ NDIS_STATUS Status;
+ uint i;
+ uint AmountToGrow;
+
+ CTEGetLock(&FWPacketFreeLock, &Handle);
+
+ AmountToGrow = MIN(MaxFWPackets - CurrentFWPackets, FWPACKET_GROW_AMOUNT);
+ HeaderPtr = NULL;
+
+ if (AmountToGrow != 0) {
+
+ // We have room to grow yet, so try to. First get the memory for our header buffers.
+ HeaderPtr = CTEAllocMem(AmountToGrow * sizeof(IPHeader));
+ if (HeaderPtr == (IPHeader *)NULL)
+ goto failure; // Couldn't get it.
+
+ // Now try to get NDIS buffers for the headers.
+ NdisAllocateBufferPool(&Status, &BufferPool, AmountToGrow);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ goto failure;
+ }
+
+ // Now try to get the packets themselves.
+ NdisAllocatePacketPool(&Status, &PacketPool, AmountToGrow, sizeof(FWContext));
+ if (Status != NDIS_STATUS_SUCCESS) {
+ NdisFreeBufferPool(BufferPool);
+ goto failure;
+ }
+
+ // Since we have everything we need, update the current count.
+ CurrentFWPackets += AmountToGrow;
+
+ CTEFreeLock(&FWPacketFreeLock, Handle);
+
+ // We got the resources we need. Loop through and put them on.
+ for (i = 0; i < AmountToGrow; i++) {
+ FWContext *FWC;
+
+ NdisAllocateBuffer(&Status, &Buffer, BufferPool, HeaderPtr,
+ sizeof(IPHeader));
+ if (Status != NDIS_STATUS_SUCCESS)
+ CTEAssert(FALSE);
+ NdisAllocatePacket(&Status, &Packet, PacketPool);
+ if (Status != NDIS_STATUS_SUCCESS)
+ CTEAssert(FALSE);
+
+ CTEMemSet(Packet->ProtocolReserved, 0, sizeof(FWContext));
+ FWC = (FWContext *)Packet->ProtocolReserved;
+ FWC->fc_hndisbuff = Buffer;
+ FWC->fc_hbuff = HeaderPtr;
+ FWC->fc_pc.pc_common.pc_flags = PACKET_FLAG_FW;
+ FWC->fc_pc.pc_common.pc_owner = PACKET_OWNER_IP;
+ FWC->fc_pc.pc_pi = RtPI;
+ FWC->fc_pc.pc_context = Packet;
+
+ FreeFWPacket(Packet);
+ HeaderPtr++;
+ }
+ return TRUE;
+ }
+
+failure:
+ CTEFreeLock(&FWPacketFreeLock, Handle);
+ if (HeaderPtr != NULL) {
+ CTEFreeMem(HeaderPtr);
+ }
+ return FALSE;
+}
+
+//* GrowFWBuffer - Grow the FW buffer pool, if we can.
+//
+// Called when we need to grow the FW buffer pool. We'll grow it up to the maximum size
+// specified by the user.
+//
+// Input: Nothing.
+//
+// Returns: TRUE if we succeeded in growing the pool, FALSE otherwise.
+//
+uint
+GrowFWBuffer(void)
+{
+ CTELockHandle Handle;
+ uint AvailableBufferSpace;
+ uint NewBufferCount;
+ uint i;
+ uchar *BufferPtr = NULL;
+ NDIS_STATUS Status;
+ PNDIS_BUFFER Buffer;
+ NDIS_HANDLE BufferPool;
+
+ CTEGetLock(&FWPacketFreeLock, &Handle);
+ AvailableBufferSpace = MIN(MaxFWBufferSize - CurrentFWBufferSize, FW_BUF_GROW_AMOUNT);
+
+ // If we have room to grow, do so.
+ if (AvailableBufferSpace >= FW_BUF_SIZE) {
+ // We have room to grow the buffer, so do so. First, round to a multiple of our
+ // FW buffer size.
+ NewBufferCount = AvailableBufferSpace / FW_BUF_SIZE;
+ AvailableBufferSpace = NewBufferCount * FW_BUF_SIZE;
+
+ // Allocate the resources we need.
+ BufferPtr = CTEAllocMem(AvailableBufferSpace);
+ if (BufferPtr == NULL) {
+ goto failure;
+ }
+
+ NdisAllocateBufferPool(&Status, &BufferPool, NewBufferCount);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ goto failure;
+ }
+
+ // We got what we needed. Now loop through and put them on the list.
+ for (i = 0; i < NewBufferCount; i++) {
+ NdisAllocateBuffer(&Status, &Buffer, BufferPool, BufferPtr, FW_BUF_SIZE);
+ if (Status != NDIS_STATUS_SUCCESS)
+ CTEAssert(FALSE);
+
+ Buffer->Next = FWBufFree;
+ FWBufFree = Buffer;
+ BufferPtr += FW_BUF_SIZE;
+ }
+
+ CurrentFWBufferSize += AvailableBufferSpace;
+ CTEFreeLock(&FWPacketFreeLock, Handle);
+ return TRUE;
+
+ }
+
+failure:
+ CTEFreeLock(&FWPacketFreeLock, Handle);
+ if (BufferPtr != NULL) {
+ CTEFreeMem(BufferPtr);
+ }
+ return FALSE;
+
+}
+
+//* FWSendComplete - Complete the transmission of a forwarded packet.
+//
+// This is called when the send of a forwarded packet is done. We'll free the resources
+// and get the next send going, if there is one. If there isn't, we'll decrement the pending
+// count.
+//
+// Input: Packet - Packet being completed.
+// Buffer - Pointer to buffer chain being completed.
+//
+// Returns: Nothing.
+//
+void
+FWSendComplete(void *SendContext, PNDIS_BUFFER Buffer)
+{
+ PNDIS_PACKET Packet = (PNDIS_PACKET)SendContext;
+ FWContext *FWC = (FWContext *)Packet->ProtocolReserved;
+ RouteSendQ *RSQ;
+ CTELockHandle Handle;
+ FWQ *NewFWQ;
+ PNDIS_PACKET NewPacket;
+
+
+#ifdef DEBUG
+ if (!Buffer)
+ DEBUGCHK;
+#endif
+
+ if (!IS_BCAST_DEST(FWC->fc_dtype))
+ RSQ = &((RouteInterface *)FWC->fc_if)->ri_q;
+ else
+ RSQ = BCastRSQ;
+
+ FreeFWPacket(Packet);
+
+ CTEGetLock(&RSQ->rsq_lock, &Handle);
+ CTEAssert(RSQ->rsq_pending <= RSQ->rsq_maxpending);
+
+ RSQ->rsq_pending--;
+
+ CTEAssert(*(int *)&RSQ->rsq_pending >= 0);
+
+ if (RSQ->rsq_qlength != 0) { // Have more to send.
+ // Make sure we're not already running through this. If we are, quit.
+ if (!RSQ->rsq_running) {
+
+ // We could schedule this off for an event, but under NT that
+ // could me a context switch for every completing packet in the
+ // normal case. For now, just do it in a loop guarded with
+ // rsq_running.
+ RSQ->rsq_running = TRUE;
+
+ // Loop while we haven't hit our send limit and we still have
+ // stuff to send.
+ while (RSQ->rsq_pending < RSQ->rsq_maxpending &&
+ RSQ->rsq_qlength != 0) {
+#ifdef DEBUG
+ if (RSQ->rsq_qh.fq_next == &RSQ->rsq_qh)
+ DEBUGCHK; // Empty Q!
+#endif
+ // Pull one off the queue, and update qlength.
+ NewFWQ = RSQ->rsq_qh.fq_next;
+ RSQ->rsq_qh.fq_next = NewFWQ->fq_next;
+ NewFWQ->fq_next->fq_prev = NewFWQ->fq_prev;
+ RSQ->rsq_qlength--;
+
+ // Update pending before we send.
+ RSQ->rsq_pending++;
+ CTEFreeLock(&RSQ->rsq_lock, Handle);
+ NewPacket = PACKET_FROM_FWQ(NewFWQ);
+ TransmitFWPacket(NewPacket,
+ ((FWContext *)NewPacket->ProtocolReserved)->fc_datalength);
+ CTEGetLock(&RSQ->rsq_lock, &Handle);
+ }
+
+ RSQ->rsq_running = FALSE;
+ }
+ }
+
+ CTEFreeLock(&RSQ->rsq_lock, Handle);
+
+}
+
+//* TransmitFWPacket - Transmit a forwarded packet on a link.
+//
+// Called when we know we can send a packet. We fix up the header, and send it.
+//
+// Input: Packet - Packet to be sent.
+// DataLength - Length of data.
+//
+// Returns: Nothing.
+//
+void
+TransmitFWPacket(PNDIS_PACKET Packet, uint DataLength)
+{
+ FWContext *FC = (FWContext *)Packet->ProtocolReserved;
+ PNDIS_BUFFER HBuffer, Buffer;
+ IP_STATUS Status;
+ PVOID VirtualAddress;
+ UINT BufLen;
+
+ // Fix up the packet. Remove the existing buffer chain, and put our header on
+ // the front.
+
+ // BUGBUG - Get NDIS fixed to make this portable.
+#ifdef VXD
+ Buffer = Packet->Private.Head;
+ HBuffer = FC->fc_hndisbuff;
+ Packet->Private.Head = HBuffer;
+ Packet->Private.Tail = HBuffer;
+ HBuffer->Next = (PNDIS_BUFFER)NULL;
+ Packet->Private.TotalLength = sizeof(IPHeader);
+ Packet->Private.Count = 1;
+
+ Packet->Private.PhysicalCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(HBuffer->VirtualAddress,
+ sizeof(IPHeader));
+#else // VXD
+#ifdef NT
+ Buffer = Packet->Private.Head;
+ HBuffer = FC->fc_hndisbuff;
+ Packet->Private.Head = HBuffer;
+ Packet->Private.Tail = HBuffer;
+ NDIS_BUFFER_LINKAGE(HBuffer) = (PNDIS_BUFFER)NULL;
+ Packet->Private.TotalLength = sizeof(IPHeader);
+ Packet->Private.Count = 1;
+
+ NdisQueryBuffer(HBuffer, &VirtualAddress, &BufLen);
+
+ Packet->Private.PhysicalCount =
+ ADDRESS_AND_SIZE_TO_SPAN_PAGES(
+ VirtualAddress,
+ sizeof(IPHeader)
+ );
+#else // NT
+#error HELP! Need to make this code portable.
+#endif // NT
+#endif // VXD
+
+ // Figure out how to send it. If it's not a broadcast we'll either send it or
+ // have it fragmented. If it is a broadcast we'll let our send broadcast routine
+ // handle it.
+ if (FC->fc_dtype != DEST_BCAST) {
+
+ if ((DataLength + (uint)FC->fc_optlength) <= FC->fc_mtu)
+ Status = SendIPPacket(FC->fc_if, FC->fc_nexthop, Packet, Buffer,
+ FC->fc_hbuff, FC->fc_options, (uint)FC->fc_optlength);
+ else { // Need to fragment this.
+ BufferReference *BR = CTEAllocMem(sizeof(BufferReference));
+
+ if (BR == (BufferReference *)NULL) { // Couldn't get a BufferReference
+ FWSendComplete(Packet, Buffer);
+ return;
+ }
+ BR->br_buffer = Buffer;
+ BR->br_refcount = 0;
+ CTEInitLock(&BR->br_lock);
+ FC->fc_pc.pc_br = BR;
+ Status = IPFragment(FC->fc_if, FC->fc_mtu, FC->fc_nexthop, Packet,
+ FC->fc_hbuff, Buffer, DataLength, FC->fc_options,
+ (uint)FC->fc_optlength, (int *)NULL);
+
+ //
+ // Fragmentation needed with the DF flag set should have been
+ // handled in IPForward. We don't have the original header
+ // any longer, so silently drop the packet.
+ //
+ CTEAssert(Status != IP_PACKET_TOO_BIG);
+ }
+ } else
+ Status = SendIPBCast(FC->fc_srcnte, FC->fc_nexthop, Packet, FC->fc_hbuff,
+ Buffer, DataLength, FC->fc_options, (uint)FC->fc_optlength,
+ FC->fc_sos, &FC->fc_index);
+
+ if (Status != IP_PENDING)
+ FWSendComplete(Packet, Buffer);
+}
+
+//* SendFWPacket - Send a packet that needs to be forwarded.
+//
+// This routine is invoked when we actually get around to sending a packet.
+// We look and see if we can give another queued send to the outgoing link,
+// and if so we send on that link. Otherwise we put it on the outgoing queue
+// and remove it later.
+//
+// Input: SrcNTE - Source NTE of packet.
+// Packet - Packet to be send, containg all needed context info.
+// Status - Status of transfer data.
+// DataLength - Length in bytes of data to be send.
+//
+// Returns: Nothing.
+//
+void
+SendFWPacket(PNDIS_PACKET Packet, NDIS_STATUS Status, uint DataLength)
+{
+
+ FWContext *FC = (FWContext *)Packet->ProtocolReserved;
+ Interface *IF = FC->fc_if;
+ RouteSendQ *RSQ;
+ CTELockHandle Handle;
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+ // Figure out which logical queue it belongs on, and if we don't already
+ // have too many things going there, send it. If we can't send it now we'll
+ // queue it for later.
+ if (IS_BCAST_DEST(FC->fc_dtype))
+ RSQ = BCastRSQ;
+ else
+ RSQ = &((RouteInterface *)IF)->ri_q;
+
+ CTEGetLock(&RSQ->rsq_lock, &Handle);
+
+ if (RSQ->rsq_pending < RSQ->rsq_maxpending && RSQ->rsq_qlength == 0) {
+ // We can send on this interface.
+ RSQ->rsq_pending++;
+ CTEFreeLock(&RSQ->rsq_lock, Handle);
+
+ TransmitFWPacket(Packet, DataLength);
+
+ } else { // Need to queue this packet for later.
+
+ FC->fc_datalength = DataLength;
+ FC->fc_q.fq_next = &RSQ->rsq_qh;
+ FC->fc_q.fq_prev = RSQ->rsq_qh.fq_prev;
+ RSQ->rsq_qh.fq_prev->fq_next = &FC->fc_q;
+ RSQ->rsq_qh.fq_prev = &FC->fc_q;
+ RSQ->rsq_qlength++;
+ CTEFreeLock(&RSQ->rsq_lock, Handle);
+ }
+ } else{
+ IPSInfo.ipsi_outdiscards++;
+ FreeFWPacket(Packet);
+ }
+
+}
+
+//* RemoveRandomFWPacket - Remove a random packet from the FW queue.
+//
+// Called when we run out of resources. We pick a random packet from the FW queue,
+// free it, and return. The caller will hopefully then get it for his own use.
+//
+// Input: RSQ - Pointer to outgoing route send q..
+//
+// Returns: TRUE if we free a packet, false if we didn't.
+//
+uchar
+RemoveRandomFWPacket(RouteSendQ *RSQ)
+{
+ uint Now = (uint)CTESystemUpTime();
+ CTELockHandle Handle;
+ uint PacketCount;
+ PNDIS_PACKET FreedPacket;
+ FWQ *CurrentFWQ;
+#ifdef DEBUG
+ FWQ *FirstFWQ;
+#endif
+
+#ifdef DEBUG
+ FirstFWQ = &RSQ->rsq_qh;
+#endif
+
+ CTEGetLock(&RSQ->rsq_lock, &Handle);
+ if (RSQ->rsq_qlength) { // We have a least one in the list.
+
+
+ PacketCount = Now % (RSQ->rsq_qlength + 1);
+ if (PacketCount == RSQ->rsq_qlength) {
+ CTEFreeLock(&RSQ->rsq_lock, Handle);
+ return FALSE;
+ }
+
+ CurrentFWQ = RSQ->rsq_qh.fq_next;
+ while (PacketCount--) {
+#ifdef DEBUG
+ if (CurrentFWQ == FirstFWQ)
+ DEBUGCHK;
+#endif
+ CurrentFWQ = CurrentFWQ->fq_next;
+ }
+
+ // We've got the proper packet. Splice him out.
+ CurrentFWQ->fq_next->fq_prev = CurrentFWQ->fq_prev;
+ CurrentFWQ->fq_prev->fq_next = CurrentFWQ->fq_next;
+ RSQ->rsq_qlength--;
+ CTEFreeLock(&RSQ->rsq_lock, Handle);
+ FreedPacket = PACKET_FROM_FWQ(CurrentFWQ);
+ FreeFWPacket(FreedPacket);
+ IPSInfo.ipsi_outdiscards++;
+ return TRUE;
+ }
+ CTEFreeLock(&RSQ->rsq_lock, Handle);
+ return FALSE;
+
+
+}
+
+//* GetFWBuffer - Get a list of buffers for forwarding.
+//
+// This routine gets a list of buffers for forwarding, and puts the data into it. This
+// may involve calling TransferData, or we may be able to copy directly into them
+// ourselves.
+//
+// Input: SrcNTE - Pointer to NTE on which packet was received.
+// Packet - Packet being forwarded, used for TD.
+// Data - Pointer to data buffer being forwarded.
+// DataLength - Length in bytes of Data.
+// BufferLength - Length in bytes available in buffer pointer to by Data.
+// Offset - Offset into original data from which to transfer.
+// LContext1, LContext2 - Context values for the link layer.
+//
+// Returns: NDIS_STATUS of attempt to get buffer.
+//
+NDIS_STATUS
+GetFWBuffer(NetTableEntry *SrcNTE, PNDIS_PACKET Packet, uchar *Data,
+ uint DataLength, uint BufferLength, uint Offset, NDIS_HANDLE LContext1,
+ uint LContext2)
+{
+ CTELockHandle Handle;
+ uint BufNeeded, i;
+ PNDIS_BUFFER FirstBuffer, CurrentBuffer;
+ void *DestPtr;
+ Interface *SrcIF;
+ FWContext *FWC;
+ uint BufLen;
+ uint LastBufSize;
+#ifdef DEBUG
+ uint TotalBufferSize;
+ PNDIS_BUFFER TempBuffer;
+#endif
+
+ // Figure out how many buffers we need.
+ BufNeeded = DataLength / FW_BUF_SIZE;
+ LastBufSize = DataLength % FW_BUF_SIZE;
+ if (LastBufSize != 0)
+ BufNeeded++;
+
+#ifdef DEBUG
+ if (!BufNeeded)
+ DEBUGCHK;
+#endif
+ FWC = (FWContext *)Packet->ProtocolReserved;
+
+ // Now run down the buffer free list, getting the buffers we need. If we
+ // can't get enough the first time, we'll free a random packet from our
+ // pending list and try again.
+ for (;;) {
+ CTEGetLock(&FWBufFreeLock, &Handle);
+ FirstBuffer = FWBufFree;
+ CurrentBuffer = STRUCT_OF(NDIS_BUFFER, &FWBufFree, Next);
+ i = 0;
+ do {
+ CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
+ if (!CurrentBuffer)
+ break;
+
+ // Zap this buffer length to the full buffer size, since it may
+ // have been modified from a previous send.
+ NdisAdjustBufferLength(CurrentBuffer, FW_BUF_SIZE);
+ i++;
+ } while (i < BufNeeded);
+
+ if (i != BufNeeded) { // We ran out of buffers. Free a packet and try again.
+ RouteSendQ *RSQ;
+
+ if ((MaxFWBufferSize - CurrentFWBufferSize) <= FW_BUF_SIZE) {
+ CTEFreeLock(&FWBufFreeLock, Handle);
+ if (GrowFWBuffer()) {
+ continue;
+ }
+ } else
+ CTEFreeLock(&FWBufFreeLock, Handle);
+
+ if (!IS_BCAST_DEST(FWC->fc_dtype))
+ RSQ = &((RouteInterface *)FWC->fc_if)->ri_q;
+ else
+ RSQ = BCastRSQ;
+
+ if (!RemoveRandomFWPacket(RSQ)) {
+ if (IS_BCAST_DEST(FWC->fc_dtype))
+ return NDIS_STATUS_RESOURCES;
+
+ // Couldn't get one for a non-broadcast packet. If the qlen is
+ // 0 on the outgoing queue, we'll try other queues, on the
+ // presumption that traffic through some other interface is
+ // starving this one.
+ if (RSQ->rsq_qlength == 0) {
+ Interface *IF;
+ for (IF = IFList; IF != NULL; IF = IF->if_next) {
+ RSQ = &((RouteInterface *)IF)->ri_q;
+ if (RemoveRandomFWPacket(RSQ))
+ break;
+ }
+ if (IF == NULL)
+ return NDIS_STATUS_RESOURCES;
+ } else
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ // Otherwise we'll fall through and try again, now that we have
+ // hopefully put some more on the free queue.
+
+ } else {
+ // We have as many as we need. Update the free list.
+ FWBufFree = NDIS_BUFFER_LINKAGE(CurrentBuffer);
+ CTEFreeLock(&FWBufFreeLock, Handle);
+ NDIS_BUFFER_LINKAGE(CurrentBuffer) = (PNDIS_BUFFER)NULL;
+
+ // If we have a non-full last buffer, adjust it's size.
+ if (LastBufSize != 0)
+ NdisAdjustBufferLength(CurrentBuffer, LastBufSize);
+
+ FWC->fc_buffhead = FirstBuffer;
+ FWC->fc_bufftail = CurrentBuffer;
+ break;
+ }
+ }
+
+ NdisChainBufferAtFront(Packet, FirstBuffer);
+
+#ifdef DEBUG
+ // Sanity check the buffer chain and packet.
+ TempBuffer = FirstBuffer;
+ TotalBufferSize = 0;
+ while (TempBuffer != NULL) {
+ TotalBufferSize += NdisBufferLength(TempBuffer);
+ TempBuffer = NDIS_BUFFER_LINKAGE(TempBuffer);
+ }
+
+ CTEAssert(TotalBufferSize == DataLength);
+ NdisQueryPacket(Packet, NULL, NULL, NULL, &TotalBufferSize);
+ CTEAssert(TotalBufferSize == DataLength);
+#endif
+
+ // First buffer points to the list of buffers we have. If we can copy the
+ // data here, do so, otherwise invoke the link's transfer data routine.
+ if ((DataLength <= BufferLength) && (SrcNTE->nte_flags & NTE_COPY)) {
+ while (DataLength) {
+ uint CopyLength;
+
+#ifdef VXD
+ DestPtr = FirstBuffer->VirtualAddress;
+#else
+ //
+ // BUGBUG: This is inefficient.
+ //
+ NdisQueryBuffer(FirstBuffer, &DestPtr, &BufLen);
+#endif
+ CopyLength = MIN(DataLength, FW_BUF_SIZE);
+ CTEAssert(CopyLength == NdisBufferLength(FirstBuffer));
+ CTEMemCopy(DestPtr, Data, CopyLength);
+ Data += CopyLength;
+ DataLength -= CopyLength;
+ FirstBuffer = NDIS_BUFFER_LINKAGE(FirstBuffer);
+ }
+ return NDIS_STATUS_SUCCESS;
+ }
+
+ // We need to call transfer data for this.
+
+ SrcIF = SrcNTE->nte_if;
+ return (*(SrcIF->if_transfer))(SrcIF->if_lcontext, LContext1, LContext2,
+ Offset, DataLength, Packet, &DataLength);
+
+
+}
+
+
+//* GetFWPacket - Get a packet for forwarding.
+//
+// Called when we need to get a packet to forward a datagram.
+//
+// Entry: Packet - Pointer to where to return a packet.
+// IF - Outgoing I/F for packet.
+// DestType - Type of outgoing packet.
+//
+// Returns: Pointer to header buffer.
+//
+//
+IPHeader *
+GetFWPacket(PNDIS_PACKET *Packet, Interface *IF, uchar DestType)
+{
+ CTELockHandle Handle;
+ PNDIS_PACKET NewPacket;
+ RouteSendQ *RSQ;
+
+ for (;;) {
+ CTEGetLock(&FWPacketFreeLock, &Handle);
+ if ((NewPacket = FWPacketFree) != (PNDIS_PACKET)NULL) {
+ FWContext *FWC;
+
+ FWC = (FWContext *)NewPacket->ProtocolReserved;
+ FWPacketFree = FWC->fc_pc.pc_common.pc_link;
+ FWC->fc_pc.pc_common.pc_flags |= PACKET_FLAG_IPHDR;
+ FWC->fc_pc.pc_br = NULL;
+ *Packet = NewPacket;
+ CTEFreeLock(&FWPacketFreeLock, Handle);
+ return FWC->fc_hbuff;
+ }
+
+ // If we couldn't get one, try to grow the list if we can.
+ if (MaxFWPackets > CurrentFWPackets) {
+ CTEFreeLock(&FWPacketFreeLock, Handle);
+ // We're allowed to grow, so try to.
+ if (GrowFWPackets()) {
+ continue; // If we grew it, try again.
+ }
+ } else
+ CTEFreeLock(&FWPacketFreeLock, Handle);
+
+ // Either we weren't allowed to grow the list, or we tried to but couldn't. Try yo
+ // get one that's on the queue already.
+ if (!IS_BCAST_DEST(DestType))
+ RSQ = &((RouteInterface *)IF)->ri_q;
+ else
+ RSQ = BCastRSQ;
+
+ if (!RemoveRandomFWPacket(RSQ))
+ break;
+ }
+
+ return (IPHeader *)NULL;
+}
+
+
+//** IPForward - Forward a packet.
+//
+// The routine called when we need to forward a packet. We check if we're supposed
+// to act as a gateway, and if we are and the incoming packet is a bcast we check
+// and see if we're supposed to forward broadcasts. Assuming we're supposed to
+// forward it, we will process any options. If we find some, we do some validation
+// to make sure everything is good. After that, we look up the next hop. If we can't
+// find one, we'll issue an error. Then we get a packet and buffers, and send it.
+//
+// Input: SrcNTE - NTE for net on which we received this.
+// Header - Pointer to received IPheader.
+// HeaderLength - Length of header.
+// Data - Pointer to data to be forwarded.
+// BufferLength - Length in bytes available in the buffer.
+// DestType - Type of destination.
+//
+// Returns: Nothing.
+//
+void
+IPForward(NetTableEntry *SrcNTE, IPHeader UNALIGNED *Header, uint HeaderLength,
+ void *Data, uint BufferLength, NDIS_HANDLE LContext1, uint LContext2,
+ uchar DestType)
+{
+ uchar *Options;
+ uchar OptLength;
+ OptIndex Index;
+ IPAddr DestAddr; // IP address we're routing towards.
+ uchar SendOnSource = FALSE;
+ IPAddr NextHop; // Next hop IP address.
+ PNDIS_PACKET Packet;
+ FWContext *FWC;
+ IPHeader *NewHeader; // New header.
+ NDIS_STATUS Status;
+ uint DataLength;
+ CTELockHandle TableHandle;
+ uchar ErrIndex;
+ IPAddr OutAddr; // Address of interface we're send out on.
+ Interface *IF; // Interface we're sending out on.
+ uint MTU;
+
+ if (ForwardPackets) {
+
+ DestAddr = Header->iph_dest;
+
+ // If it's a broadcast, see if we can forward it. We won't forward it if broadcast
+ // forwarding is turned off, or the destination if the local (all one's) broadcast,
+ // or it's a multicast (Class D address). We'll pass through subnet broadcasts in
+ // case there's a source route. This would be odd - maybe we should disable this?
+ if (IS_BCAST_DEST(DestType)) {
+ if (!ForwardBCast) {
+ if (DestType > DEST_REMOTE)
+ IPSInfo.ipsi_inaddrerrors++;
+ return;
+ }
+ if ((DestAddr == IP_LOCAL_BCST) ||
+ (DestAddr == IP_ZERO_BCST) ||
+ (DestType == DEST_SN_BCAST) ||
+ CLASSD_ADDR(DestAddr)) {
+ return;
+ }
+ } else
+ if (DestType == DEST_REMOTE) {
+ SrcNTE = BestNTEForIF(Header->iph_src, SrcNTE->nte_if);
+ if (SrcNTE == NULL) {
+ // Something bad happened.
+ return;
+ }
+ }
+
+ // If the TTL would expire, send a message.
+ if (Header->iph_ttl <= 1) {
+ IPSInfo.ipsi_inhdrerrors++;
+ SendICMPErr(SrcNTE->nte_addr, Header, ICMP_TIME_EXCEED, TTL_IN_TRANSIT,0);
+ return;
+ }
+
+ DataLength = net_short(Header->iph_length) - HeaderLength;
+
+ Index.oi_srtype = NO_SR; // So we know we don't have a source route.
+ Index.oi_srindex = MAX_OPT_SIZE;
+ Index.oi_rrindex = MAX_OPT_SIZE;
+ Index.oi_tsindex = MAX_OPT_SIZE;
+
+ // Now check for options, and process any we find.
+ if (HeaderLength != sizeof(IPHeader)) {
+ IPOptInfo OptInfo;
+
+ OptInfo.ioi_options = (uchar *)(Header + 1);
+ OptInfo.ioi_optlength = HeaderLength - sizeof(IPHeader);
+ // Validate options, and set up indices.
+ if ((ErrIndex = ParseRcvdOptions(&OptInfo, &Index)) < MAX_OPT_SIZE) {
+ IPSInfo.ipsi_inhdrerrors++;
+ SendICMPErr(SrcNTE->nte_addr, Header, ICMP_PARAM_PROBLEM,
+ PTR_VALID, ((ulong)ErrIndex + sizeof(IPHeader)));
+ return;
+ }
+
+ Options = CTEAllocMem(OptInfo.ioi_optlength);
+ if (!Options) {
+ IPSInfo.ipsi_outdiscards++;
+ return; // Couldn't get an
+ } // option buffer, return;
+
+ // Now copy into our buffer.
+ CTEMemCopy(Options, OptInfo.ioi_options, OptLength = OptInfo.ioi_optlength);
+
+ // See if we have a source routing option, and if so we may need to process it. If
+ // we have one, and the destination in the header is us, we need to update the
+ // route and the header.
+ if (Index.oi_srindex != MAX_OPT_SIZE) {
+ if (DestType >= DEST_REMOTE) { // Not for us.
+ if (Index.oi_srtype == IP_OPT_SSRR) {
+ // This packet is strict source routed, but we're not the destination!
+ // We can't continue from here - perhaps we should send an ICMP, but
+ // I'm not sure which one it would be.
+ CTEFreeMem(Options);
+ IPSInfo.ipsi_inaddrerrors++;
+ return;
+ }
+ Index.oi_srindex = MAX_OPT_SIZE; // Don't need to update this.
+
+ } else { // This came here, we need to update the destination address.
+ uchar *SROpt = Options + Index.oi_srindex;
+ uchar Pointer;
+
+ Pointer = SROpt[IP_OPT_PTR] - 1; // Index starts from one.
+
+ // Get the next hop address, and see if it's a broadcast.
+ DestAddr = *(IPAddr UNALIGNED *)&SROpt[Pointer];
+ DestType = GetAddrType(DestAddr); // Find address type.
+ if (DestType == DEST_INVALID) {
+ SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH, SR_FAILED, 0);
+ IPSInfo.ipsi_inhdrerrors++;
+ CTEFreeMem(Options);
+ return;
+ }
+
+ // If we came through here, any sort of broadcast needs to be sent out
+ // the way it came, so update that flag.
+ SendOnSource = TRUE;
+ }
+ }
+ } else { // No options.
+ Options = (uchar *)NULL;
+ OptLength = 0;
+ }
+
+ IPSInfo.ipsi_forwdatagrams++;
+
+ // We've processed the options. Now look up the next hop. If we can't
+ // find one, send back an error.
+ IF = LookupNextHopWithBuffer(DestAddr, SrcNTE->nte_addr, &NextHop, &MTU,
+ Header->iph_protocol, (uchar *)Data, BufferLength);
+
+ if (IF == NULL) {
+ // Couldn't find an outgoing route.
+ IPSInfo.ipsi_outnoroutes++;
+ SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH,
+ HOST_UNREACH, 0);
+ if (Options)
+ CTEFreeMem(Options);
+ return;
+ }
+
+ //
+ // If the DF flag is set, make sure the packet doesn't need
+ // fragmentation. If this is the case, send an ICMP error
+ // now while we still have the original IP header. The ICMP
+ // message includes the MTU so the source host can perform
+ // Path MTU discovery.
+ //
+ if ( (Header->iph_offset & IP_DF_FLAG) &&
+ ((DataLength + (uint)OptLength) > MTU)
+ )
+ {
+ CTEAssert((MTU + sizeof(IPHeader)) >= 68);
+ CTEAssert((MTU + sizeof(IPHeader)) <= 0xFFFF);
+
+ IPSInfo.ipsi_fragfails++;
+ SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH,
+ FRAG_NEEDED, net_long((ulong)(MTU + sizeof(IPHeader)))
+ );
+
+ if (Options)
+ CTEFreeMem(Options);
+#ifdef _PNP_POWER
+ DerefIF(IF);
+#endif
+ return;
+ }
+
+ // See if we need to filter this packet. If we do, call the filter routine
+ // to see if it's OK to forward it.
+ if (ForwardFilterPtr != NULL) {
+ FORWARD_ACTION Action;
+
+ Action = (*ForwardFilterPtr)(Header, Data, BufferLength,
+ SrcNTE->nte_if->if_filtercontext, IF->if_filtercontext);
+
+ if (Action != FORWARD) {
+ IPSInfo.ipsi_outdiscards++;
+ if (Options)
+ CTEFreeMem(Options);
+#ifdef _PNP_POWER
+ DerefIF(IF);
+#endif
+ return;
+ }
+ }
+
+ // If we have a strict source route and the next hop is not the one
+ // specified, send back an error.
+ if (Index.oi_srtype == IP_OPT_SSRR) {
+ if (DestAddr != NextHop) {
+ IPSInfo.ipsi_outnoroutes++;
+ SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH,
+ SR_FAILED, 0);
+ CTEFreeMem(Options);
+#ifdef _PNP_POWER
+ DerefIF(IF);
+#endif
+ return;
+ }
+ }
+
+ // Update the options, if we can and we need to.
+ if ((DestType != DEST_BCAST) && Options != NULL) {
+ NetTableEntry *OutNTE;
+
+ // Need to find a valid source address for the outgoing interface.
+ CTEGetLock(&RouteTableLock, &TableHandle);
+ OutNTE = BestNTEForIF(DestAddr, IF);
+ if (OutNTE == NULL) {
+ // No NTE for this IF. Something's wrong, just bail out.
+ CTEFreeLock(&RouteTableLock, TableHandle);
+ CTEFreeMem(Options);
+#ifdef _PNP_POWER
+ DerefIF(IF);
+#endif
+ return;
+ } else {
+ OutAddr = OutNTE->nte_addr;
+ CTEFreeLock(&RouteTableLock, TableHandle);
+ }
+
+ ErrIndex = UpdateOptions(Options, &Index,
+ (IP_LOOPBACK(OutAddr) ? DestAddr : OutAddr));
+
+ if (ErrIndex != MAX_OPT_SIZE) {
+ IPSInfo.ipsi_inhdrerrors++;
+ SendICMPErr(OutAddr, Header, ICMP_PARAM_PROBLEM, PTR_VALID,
+ ((ulong)ErrIndex + sizeof(IPHeader)));
+ CTEFreeMem(Options);
+#ifdef _PNP_POWER
+ DerefIF(IF);
+#endif
+ return;
+ }
+ }
+
+
+ // Send a redirect, if we need to. We'll send a redirect if the packet
+ // is going out on the interface it came in on and the next hop address
+ // is on the same subnet as the NTE we received it on, and if there
+ // are no source route options. We also need to make sure that the
+ // source of the datagram is on the I/F we received it on, so we don't
+ // send a redirect to another gateway.
+ // SendICMPErr will check and not send a redirect if this is a broadcast.
+ if ((SrcNTE->nte_if == IF) &&
+ IP_ADDR_EQUAL(SrcNTE->nte_addr & SrcNTE->nte_mask,
+ NextHop & SrcNTE->nte_mask) &&
+ IP_ADDR_EQUAL(SrcNTE->nte_addr & SrcNTE->nte_mask,
+ Header->iph_src & SrcNTE->nte_mask))
+ {
+ if (Index.oi_srindex == MAX_OPT_SIZE)
+ {
+
+#ifdef REDIRECT_DEBUG
+
+#define PR_IP_ADDR(x) \
+ ((x)&0x000000ff),(((x)&0x0000ff00)>>8),(((x)&0x00ff0000)>>16),(((x)&0xff000000)>>24)
+
+
+ DbgPrint("IP: Sending Redirect. IF = %x SRC_NTE = %x SrcNteIF = %x\n",
+ IF,SrcNTE,SrcNTE->nte_if);
+
+ DbgPrint("IP: SrcNteAddr = %d.%d.%d.%d Mask = %d.%d.%d.%d\n",
+ PR_IP_ADDR(SrcNTE->nte_addr), PR_IP_ADDR(SrcNTE->nte_mask));
+
+ DbgPrint("IP: NextHop = %d.%d.%d.%d Header Src = %d.%d.%d.%d, Dst = %d.%d.%d.%d\n",
+ PR_IP_ADDR(NextHop),
+ PR_IP_ADDR(Header->iph_src),
+ PR_IP_ADDR(Header->iph_dest));
+
+#endif
+
+ SendICMPErr(SrcNTE->nte_addr, Header, ICMP_REDIRECT,
+ REDIRECT_HOST, NextHop);
+ }
+ }
+
+ // We have the next hop. Now get a forwarding packet.
+ if ((NewHeader = GetFWPacket(&Packet, IF, DestType)) !=
+ (IPHeader *)NULL) {
+
+ // Got the header. Fill it in.
+
+ NewHeader->iph_verlen = Header->iph_verlen;
+ NewHeader->iph_tos = Header->iph_tos;
+ NewHeader->iph_length = Header->iph_length;
+ NewHeader->iph_id = Header->iph_id;
+ NewHeader->iph_offset = Header->iph_offset;
+ NewHeader->iph_protocol = Header->iph_protocol;
+ NewHeader->iph_src = Header->iph_src;
+
+ NewHeader->iph_dest = DestAddr;
+ NewHeader->iph_ttl = Header->iph_ttl - 1;
+ NewHeader->iph_xsum = 0;
+
+ // Save the packet forwarding context info.
+ FWC = (FWContext *)Packet->ProtocolReserved;
+ FWC->fc_options = Options;
+ FWC->fc_optlength = OptLength;
+ FWC->fc_if = IF;
+ FWC->fc_mtu = MTU;
+ FWC->fc_srcnte = SrcNTE;
+ FWC->fc_nexthop = NextHop;
+ FWC->fc_sos = SendOnSource;
+ FWC->fc_dtype = DestType;
+ FWC->fc_index = Index;
+
+ // Now that we have a packet, go ahead and transfer data the
+ // data in if we need to.
+ Status = GetFWBuffer(SrcNTE, Packet, Data, DataLength, BufferLength,
+ HeaderLength, LContext1, LContext2);
+
+ // If the status is pending, don't do anything now. Otherwise,
+ // if the status is success send the packet.
+ if (Status != NDIS_STATUS_PENDING)
+ if (Status == NDIS_STATUS_SUCCESS) {
+ SendFWPacket(Packet, Status, DataLength);
+ } else {
+ // Some sort of failure. Free the packet.
+ IPSInfo.ipsi_outdiscards++;
+ FreeFWPacket(Packet);
+ }
+ } else { // Couldn't get a packet, so drop this.
+ IPSInfo.ipsi_outdiscards++;
+ if (Options)
+ CTEFreeMem(Options);
+#ifdef _PNP_POWER
+ DerefIF(IF);
+#endif
+ }
+ } else { // Forward called, but forwarding
+ // turned off.
+ if (DestType != DEST_BCAST && DestType != DEST_SN_BCAST) {
+ // No need to go through here for strictly broadcast packets,
+ // although we want to bump the counters for remote bcast stuff.
+ IPSInfo.ipsi_inaddrerrors++;
+
+ if (!IS_BCAST_DEST(DestType)) {
+ if (DestType == DEST_LOCAL) // Called when local, must be SR.
+ SendICMPErr(SrcNTE->nte_addr, Header,
+ ICMP_DEST_UNREACH, SR_FAILED, 0);
+ }
+ }
+ }
+
+}
+
+//* AddNTERoutes - Add the routes for an NTE.
+//
+// Called during initalization or during DHCP address assignment to add
+// routes. We add routes for the address of the NTE, including routes
+// to the subnet and the address itself.
+//
+// Input: NTE - NTE for which to add routes.
+//
+// Returns: TRUE if they were all added, FALSE if not.
+//
+uint
+AddNTERoutes(NetTableEntry *NTE)
+{
+ IPMask Mask, SNMask;
+ Interface *IF;
+ CTELockHandle Handle;
+ IPAddr AllSNBCast;
+ IP_STATUS Status;
+
+ // First, add the route to the address itself. This is a route through
+ // the loopback interface.
+
+ if (AddRoute(NTE->nte_addr, HOST_MASK, IPADDR_LOCAL, LoopNTE->nte_if,
+ LOOPBACK_MSS, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL) != IP_SUCCESS)
+ return FALSE;
+
+ Mask = IPNetMask(NTE->nte_addr);
+ IF = NTE->nte_if;
+
+ // Now add the route for the all-subnet's broadcast, if one doesn't already
+ // exist. There is special case code to handle this in SendIPBCast, so the
+ // exact interface we add this on doesn't really matter.
+
+ CTEGetLock(&RouteTableLock, &Handle);
+ AllSNBCast = (NTE->nte_addr & Mask) | (IF->if_bcast & ~Mask);
+ if (LookupRTE(AllSNBCast, NULL_IP_ADDR, HOST_ROUTE_PRI) == NULL) {
+
+ Status = LockedAddRoute(AllSNBCast, HOST_MASK, IPADDR_LOCAL, IF,
+ NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL);
+
+ } else
+ Status = IP_SUCCESS;
+
+ CTEFreeLock(&RouteTableLock, Handle);
+
+ if (Status != IP_SUCCESS)
+ return FALSE;
+
+ // If we're doing IGMP, add the route to the multicast address.
+ if (IGMPLevel != 0) {
+ if (AddRoute(CLASSD_MASK, CLASSD_MASK, IPADDR_LOCAL, NTE->nte_if,
+ NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL) != IP_SUCCESS)
+ return FALSE;
+ }
+
+ if(NTE->nte_mask != HOST_MASK)
+ {
+ // And finally the route to the subnet.
+ SNMask = NTE->nte_mask;
+ if (AddRoute(NTE->nte_addr & SNMask, SNMask, IPADDR_LOCAL, NTE->nte_if,
+ NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL) != IP_SUCCESS)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+#ifndef CHICAGO
+#pragma BEGIN_INIT
+#endif
+
+uint BCastMinMTU = 0xffff;
+
+//* InitNTERouting - do per NTE route initialization.
+//
+// Called when we need to initialize per-NTE routing. For the specified NTE,
+// call AddNTERoutes to add a route for a net bcast, subnet bcast, and local
+// attached subnet. The net bcast entry is sort of a filler - net and
+// global bcasts are always handled specially. For this reason we specify
+// the FirstInterface when adding the route. Subnet bcasts are assumed to
+// only go out on one interface, so the actual interface to be used is
+// specifed. If two interfaces are on the same subnet the last interface is
+// the one that will be used.
+//
+// Input: NTE - NTE for which routing is to be initialized.
+// NumGWs - Number of default gateways to add.
+// GWList - List of default gateways.
+//
+// Returns: TRUE if we succeed, FALSE if we don't.
+//
+uint
+InitNTERouting(NetTableEntry *NTE, uint NumGWs, IPAddr *GWList)
+{
+ uint i;
+ Interface *IF;
+
+ CTERefillMem();
+ if (NTE != LoopNTE) {
+ BCastMinMTU = MIN(BCastMinMTU, NTE->nte_mss);
+
+ IF = NTE->nte_if;
+ AddRoute(IF->if_bcast, HOST_MASK, IPADDR_LOCAL, FirstIF,
+ BCastMinMTU, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL);// Route for local
+ // bcast.
+ if (NTE->nte_flags & NTE_VALID) {
+ if (!AddNTERoutes(NTE))
+ return FALSE;
+
+ // Now add the default routes that are present on this net. We
+ // don't check for errors here, but we should probably
+ // log an error.
+ for (i = 0; i < NumGWs;i++) {
+ IPAddr GWAddr;
+
+ GWAddr = net_long(GWList[i]);
+
+ if (IP_ADDR_EQUAL(GWAddr, NTE->nte_addr)) {
+ AddRoute(NULL_IP_ADDR, DEFAULT_MASK,
+ IPADDR_LOCAL, NTE->nte_if, NTE->nte_mss, 1,
+ IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL);
+ } else
+ AddRoute(NULL_IP_ADDR, DEFAULT_MASK,
+ net_long(GWList[i]), NTE->nte_if, NTE->nte_mss, 1,
+ IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL);
+ }
+ }
+ }
+ return TRUE;
+}
+
+#ifdef CHICAGO
+#pragma BEGIN_INIT
+#endif
+
+//* InitRouting - Initialize our routing table.
+//
+// Called during initialization to initialize the routing table.
+//
+// Entry: Nothing.
+//
+// Returns: True if we succeeded, False if we didn't.
+//
+int
+InitRouting(IPConfigInfo *ci)
+{
+ int i;
+
+ CTERefillMem();
+
+ CTEInitLock(&RouteTableLock);
+
+ DefGWConfigured = 0;
+ DefGWActive = 0;
+
+ CTEMemSet(&DummyInterface, 0, sizeof(DummyInterface));
+ DummyInterface.ri_if.if_xmit = DummyXmit;
+ DummyInterface.ri_if.if_transfer = DummyXfer;
+ DummyInterface.ri_if.if_close = DummyClose;
+ DummyInterface.ri_if.if_invalidate = DummyInvalidate;
+ DummyInterface.ri_if.if_qinfo = DummyQInfo;
+ DummyInterface.ri_if.if_setinfo = DummySetInfo;
+ DummyInterface.ri_if.if_getelist = DummyGetEList;
+ DummyInterface.ri_if.if_addaddr = DummyAddAddr;
+ DummyInterface.ri_if.if_deladdr = DummyDelAddr;
+ DummyInterface.ri_if.if_bcast = IP_LOCAL_BCST;
+ DummyInterface.ri_if.if_speed = 10000000;
+ DummyInterface.ri_if.if_mtu = 1500;
+ DummyInterface.ri_if.if_index = INVALID_IF_INDEX;
+
+ for (i = 0; i < ROUTE_TABLE_SIZE; i++)
+ RouteTable[i] = (RouteTableEntry *)NULL;
+
+ // We've created at least one net. We need to add routing table entries for
+ // the global broadcast address, as well as for subnet and net broadcasts,
+ // and routing entries for the local subnet. We alse need to add a loopback
+ // route for the loopback net. Below, we'll add a host route for ourselves
+ // through the loopback net.
+ AddRoute(LOOPBACK_ADDR & CLASSA_MASK, CLASSA_MASK, IPADDR_LOCAL,
+ LoopNTE->nte_if, LOOPBACK_MSS, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL);
+ // Route for loopback.
+ RouterConfigured = (uchar)ci->ici_gateway;
+
+ CTEInitTimer(&IPRouteTimer);
+
+ CTEStartTimer(&IPRouteTimer, IP_ROUTE_TIMEOUT, IPRouteTimeout, NULL);
+ return TRUE;
+
+}
+
+//* InitGateway - Initialize our gateway functionality.
+//
+// Called during init. time to initialize our gateway functionality. If we're
+// not connfigured as a router, we do nothing. If we are, we allocate the
+// resources we need and do other router initialization.
+//
+// Input: ci - Config info.
+//
+// Returns: TRUE if we succeed, FALSE if don't.
+//
+uint
+InitGateway(IPConfigInfo *ci)
+{
+ uint FWBufSize, FWPackets;
+ uint FWBufCount;
+ NDIS_STATUS Status;
+ NDIS_HANDLE BufferPool, FWBufferPool, PacketPool;
+ IPHeader *HeaderPtr = NULL;
+ uchar *FWBuffer = NULL;
+ PNDIS_BUFFER Buffer;
+ PNDIS_PACKET Packet;
+ RouteInterface *RtIF;
+ NetTableEntry *NTE;
+ uint i;
+
+ // If we're going to be a router, allocate and initialize the resources we
+ // need for that.
+ BCastRSQ = NULL;
+ if (RouterConfigured) {
+
+
+ CTERefillMem();
+ RtPI = CTEAllocMem(sizeof(ProtInfo));
+ if (RtPI == (ProtInfo *)NULL)
+ goto failure;
+
+ RtPI->pi_xmitdone = FWSendComplete;
+
+ CTEInitLock(&FWPacketFreeLock);
+ CTEInitLock(&FWBufFreeLock);
+
+ MaxFWBufferSize = ci->ici_maxfwbufsize;
+ MaxFWPackets = ci->ici_maxfwpackets;
+ FWBufSize = MIN(ci->ici_fwbufsize, MaxFWBufferSize);
+ FWPackets = MIN(ci->ici_fwpackets, MaxFWPackets);
+
+ for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
+ RtIF = (RouteInterface *)NTE->nte_if;
+
+ RtIF->ri_q.rsq_qh.fq_next = &RtIF->ri_q.rsq_qh;
+ RtIF->ri_q.rsq_qh.fq_prev = &RtIF->ri_q.rsq_qh;
+ RtIF->ri_q.rsq_running = FALSE;
+ RtIF->ri_q.rsq_pending = 0;
+ RtIF->ri_q.rsq_qlength = 0;
+ CTEInitLock(&RtIF->ri_q.rsq_lock);
+ }
+
+ BCastRSQ = CTEAllocMem(sizeof(RouteSendQ));
+
+ if (BCastRSQ == (RouteSendQ *)NULL)
+ goto failure;
+
+ BCastRSQ->rsq_qh.fq_next = &BCastRSQ->rsq_qh;
+ BCastRSQ->rsq_qh.fq_prev = &BCastRSQ->rsq_qh;
+ BCastRSQ->rsq_pending = 0;
+ BCastRSQ->rsq_maxpending = DEFAULT_MAX_PENDING;
+ BCastRSQ->rsq_qlength = 0;
+ BCastRSQ->rsq_running = FALSE;
+ CTEInitLock(&BCastRSQ->rsq_lock);
+
+ RtIF = (RouteInterface *)&LoopInterface;
+ RtIF->ri_q.rsq_maxpending = DEFAULT_MAX_PENDING;
+
+ // Round the specified size down to a multiple of our FW buf size.
+ CTERefillMem();
+ FWBufCount = FWBufSize / FW_BUF_SIZE;
+ FWBufSize = FWBufCount * FW_BUF_SIZE;
+
+ // Allocate the buffers, packets, and memory for our header buffers.
+ HeaderPtr = CTEAllocMem(FWPackets * sizeof(IPHeader));
+ if (HeaderPtr == (IPHeader *)NULL)
+ goto failure;
+
+ NdisAllocateBufferPool(&Status, &BufferPool, FWPackets);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ goto failure; // Couldn't be a router, fail.
+ }
+
+ NdisAllocatePacketPool(&Status, &PacketPool, FWPackets, sizeof(FWContext));
+ if (Status != NDIS_STATUS_SUCCESS) {
+ NdisFreeBufferPool(BufferPool);
+ goto failure;
+ }
+
+ // Allocate resources for our the buffer pool.
+ CTERefillMem();
+ FWBuffer = CTEAllocMem(FWBufSize);
+ if (FWBuffer == NULL) { // Couldn't get buffer space.
+ NdisFreePacketPool(PacketPool);
+ NdisFreeBufferPool(BufferPool);
+ goto failure;
+ }
+
+ NdisAllocateBufferPool(&Status, &FWBufferPool, FWBufCount);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ NdisFreePacketPool(PacketPool);
+ NdisFreeBufferPool(BufferPool);
+ goto failure;
+ }
+
+ // Everythings allocated. Put it all together and stick them on the
+ // free list.
+ for (i = 0; i < FWPackets; i++) {
+ FWContext *FWC;
+
+ NdisAllocateBuffer(&Status, &Buffer, BufferPool, HeaderPtr,
+ sizeof(IPHeader));
+ if (Status != NDIS_STATUS_SUCCESS)
+ DEBUGCHK;
+ NdisAllocatePacket(&Status, &Packet, PacketPool);
+ if (Status != NDIS_STATUS_SUCCESS)
+ DEBUGCHK;
+
+ CTEMemSet(Packet->ProtocolReserved, 0, sizeof(FWContext));
+ FWC = (FWContext *)Packet->ProtocolReserved;
+ FWC->fc_hndisbuff = Buffer;
+ FWC->fc_hbuff = HeaderPtr;
+ FWC->fc_pc.pc_common.pc_flags = PACKET_FLAG_FW;
+ FWC->fc_pc.pc_common.pc_owner = PACKET_OWNER_IP;
+ FWC->fc_pc.pc_pi = RtPI;
+ FWC->fc_pc.pc_context = Packet;
+
+ FreeFWPacket(Packet);
+ HeaderPtr++;
+ }
+
+ for (i = 0; i < FWBufCount; i++) {
+ NdisAllocateBuffer(&Status, &Buffer, FWBufferPool, FWBuffer,
+ FW_BUF_SIZE);
+ if (Status != NDIS_STATUS_SUCCESS)
+ DEBUGCHK;
+
+ Buffer->Next = FWBufFree; // BUGBUG portability
+ FWBufFree = Buffer;
+ FWBuffer += FW_BUF_SIZE;
+ }
+
+ CurrentFWPackets = FWPackets;
+ CurrentFWBufferSize = FWBufSize;
+
+
+#if 0
+ ForwardBCast = (uchar)ci->ici_fwbcast;
+#else
+ ForwardBCast = FALSE;
+#endif
+ ForwardPackets = TRUE;
+ }
+
+ return TRUE;
+
+failure:
+ if (RtPI != NULL)
+ CTEFreeMem(RtPI);
+ if (BCastRSQ != NULL)
+ CTEFreeMem(BCastRSQ);
+ if (HeaderPtr != NULL)
+ CTEFreeMem(HeaderPtr);
+ if (FWBuffer != NULL)
+ CTEFreeMem(FWBuffer);
+
+ ForwardBCast = FALSE;
+ ForwardPackets = FALSE;
+ RouterConfigured = FALSE;
+ return FALSE;
+
+}
+#pragma END_INIT
diff --git a/private/ntos/tdi/tcpip/ip/iproute.h b/private/ntos/tdi/tcpip/ip/iproute.h
new file mode 100644
index 000000000..e06af7100
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/iproute.h
@@ -0,0 +1,107 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** IPROUTE.H - IP routing definitions.
+//
+// This file contains all of the definitions for routing code that are
+// visible to modules outside iproute.c
+
+
+extern struct Interface *LookupNextHop(IPAddr Dest, IPAddr Src,
+ IPAddr *FirstHop, uint *MTU);
+extern struct Interface *LookupNextHopWithBuffer(IPAddr Dest, IPAddr Src,
+ IPAddr *FirstHop, uint *MTU, uchar Protocol,
+ uchar *Buffer, uint Length);
+
+extern void FlushATCache(IPAddr Address);
+extern uchar GetAddrType(IPAddr Address);
+extern uint InvalidSourceAddress(IPAddr Address);
+extern uchar GetLocalNTE(IPAddr Address, NetTableEntry **NTE);
+extern uchar IsBCastOnNTE(IPAddr Address, NetTableEntry *NTE);
+extern void SendFWPacket(PNDIS_PACKET Packet, NDIS_STATUS Status,
+ uint DataLength);
+extern void IPForward(NetTableEntry *SrcNTE,
+ IPHeader UNALIGNED *Header, uint HeaderLength,
+ void *Data, uint BufferLength,
+ NDIS_HANDLE LContext1, uint LContext2,
+ uchar DestType);
+
+extern uint AttachRCEToRTE(RouteCacheEntry *RCE, uchar Protocol,
+ uchar *Buffer, uint Length);
+extern void Redirect(NetTableEntry *NTE, IPAddr RDSrc,
+ IPAddr Target, IPAddr Src, IPAddr FirstHop);
+extern IP_STATUS AddRoute(IPAddr Destination, IPMask Mask,
+ IPAddr FirstHop, Interface *OutIF, uint MTU,
+ uint Metric, uint Proto, uint AType,
+ void *Context);
+extern IP_STATUS DeleteRoute(IPAddr Destination, IPMask Mask,
+ IPAddr FirstHop, Interface *OutIF);
+extern void *GetRouteContext(IPAddr Destination, IPAddr Source);
+
+extern NetTableEntry *BestNTEForIF(IPAddr Dest, Interface *IF);
+extern void RTWalk(uint (*CallFunc)(struct RouteTableEntry *,
+ void *, void *), void *Context, void *Context1);
+
+extern uint DeleteRTEOnIF(struct RouteTableEntry *RTE,
+ void *Context, void *Context1);
+extern uint InvalidateRCEOnIF(struct RouteTableEntry *RTE,
+ void *Context, void *Context1);
+extern uint SetMTUOnIF(struct RouteTableEntry *RTE, void *Context,
+ void *Context1);
+extern uint SetMTUToAddr(struct RouteTableEntry *RTE, void *Context,
+ void *Context1);
+extern uint AddNTERoutes(struct NetTableEntry *NTE);
+extern void IPCheckRoute(IPAddr Dest, IPAddr Src);
+extern void RouteFragNeeded(IPHeader UNALIGNED *IPH, ushort NewMTU);
+extern IP_STATUS IPGetPInfo(IPAddr Dest, IPAddr Src, uint *NewMTU,
+ uint *MaxPathSpeed);
+extern int InitRouting(struct IPConfigInfo *ci);
+extern uint InitNTERouting(NetTableEntry *NTE, uint NumGWs,
+ IPAddr *GW);
+extern uint InitGateway(struct IPConfigInfo *ci);
+extern IPAddr OpenRCE(IPAddr Address, IPAddr Src, RouteCacheEntry **RCE,
+ uchar *Type, ushort *MSS, IPOptInfo *OptInfo);
+extern void CloseRCE(RouteCacheEntry *RCE);
+extern uint IsRouteICMP(IPAddr Dest, IPMask Mask, IPAddr FirstHop,
+ Interface *OutIF);
+
+EXTERNAL_LOCK(RouteTableLock)
+
+extern uint DeadGWDetect;
+extern uint PMTUDiscovery;
+extern uchar ForwardPackets;
+extern uchar RouterConfigured;
+// Pointer to callout routine for dial on demand.
+extern IPMapRouteToInterfacePtr DODCallout;
+
+// Pointer to packet filter handler.
+extern IPPacketFilterPtr ForwardFilterPtr;
+
+#define IPADDR_LOCAL 0xffffffff // Indicates that IP address is
+ // directly connected.
+
+#define IP_LOCAL_BCST 0xffffffff
+#define IP_ZERO_BCST 0
+
+#define HOST_MASK 0xffffffff
+#define DEFAULT_MASK 0
+
+
+#ifdef NT
+#define LOOPBACK_MSS (1500 - sizeof(IPHeader))
+#else // NT
+#define LOOPBACK_MSS 256
+#endif // NT
+
+
+#define LOOPBACK_ADDR 0x0100007f
+#define IP_LOOPBACK(x) (((x) & CLASSA_MASK) == 0x7f)
+
+#define ATYPE_PERM 0 // A permanent route.
+#define ATYPE_OVERRIDE 1 // Semi-permanent - can be
+ // overriden.
+#define ATYPE_TEMP 2 // A temporary route.
+
diff --git a/private/ntos/tdi/tcpip/ip/iprtdef.h b/private/ntos/tdi/tcpip/ip/iprtdef.h
new file mode 100644
index 000000000..1082646ba
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/iprtdef.h
@@ -0,0 +1,135 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+#include "ipfilter.h"
+
+//** IPRTDEF.H - IP private routing definitions.
+//
+// This file contains all of the definitions private to the routing
+// module.
+
+//* Route table entry.
+
+struct RouteTableEntry {
+ struct RouteTableEntry *rte_next; // Next in hash chain.
+ IPAddr rte_dest; // Destination of route.
+ IPMask rte_mask; // Mask to use when examining route.
+ IPAddr rte_addr; // First hop for this route.
+ uint rte_priority; // Priority of this route:
+ // essentially the number
+ // of bits set in mask.
+ uint rte_metric; // Metric of route. Lower
+ // is better.
+ uint rte_mtu; // MTU for this route.
+ struct Interface *rte_if; // Outbound interface.
+ RouteCacheEntry *rte_rcelist;
+ ushort rte_type; // Type of route.
+ ushort rte_flags; // Flags for route.
+ uint rte_admintype; // Admin type of route.
+ uint rte_proto; // How we learned about the
+ // route.
+ uint rte_valid; // Up time (in seconds)
+ // route was last known to be
+ // valid.
+ uint rte_mtuchange; // System up time (in seconds)
+ // MTU was changed.
+ ROUTE_CONTEXT rte_context; // Dial-on-demand context for this route.
+}; /* RouteTableEntry */
+
+#define ADDR_FROM_RTE(R, A) (IP_ADDR_EQUAL((R)->rte_addr, IPADDR_LOCAL) ? (A) : \
+ (R)->rte_addr)
+#define IF_FROM_RTE(R) ((R)->rte_if)
+#define MTU_FROM_RTE(R) ((R)->rte_mtu)
+#define SRC_FROM_RTE(R) ((R)->rte_src)
+
+#define RTE_VALID 1
+#define RTE_INCREASE 2 // Set if last MTU change was an
+ // increase.
+#define RTE_IF_VALID 4 // Set to TRUE if rte_if is valid.
+
+#define IP_ROUTE_TIMEOUT 60L*1000L // Route timer fires once a minute.
+
+#define MTU_INCREASE_TIME 120 // Number of seconds after increase
+ // to re-increase.
+#define MTU_DECREASE_TIME 600 // Number of seconds after decrease
+ // to re-increase.
+
+#define MAX_ICMP_ROUTE_VALID 600 // Time to timeout an unused ICMP
+ // derived route, in seconds.
+
+#define MIN_RT_VALID 60 // Minimum time a route is assumed
+ // to be valid, in seconds.
+
+#define MIN_VALID_MTU 68 // Minimum valid MTU we can have.
+#define HOST_ROUTE_PRI 32
+#define DEFAULT_ROUTE_PRI 0
+
+typedef struct RouteTableEntry RouteTableEntry;
+
+//* Forward Q linkage structure.
+struct FWQ {
+ struct FWQ *fq_next;
+ struct FWQ *fq_prev;
+}; /* FWQ */
+
+typedef struct FWQ FWQ;
+
+
+//* Forward context structure, used when TD'ing a packet to be forwarded.
+struct FWContext {
+ PacketContext fc_pc; // Dummy packet context for send routines.
+ FWQ fc_q; // Queue structure.
+ PNDIS_BUFFER fc_hndisbuff; // Pointer to NDIS buffer for header.
+ IPHeader *fc_hbuff; // Header buffer.
+ PNDIS_BUFFER fc_buffhead; // Head of list of NDIS buffers.
+ PNDIS_BUFFER fc_bufftail; // Tail of list of NDIS buffers.
+ uchar *fc_options; // Options,
+ Interface *fc_if; // Destination interface.
+ IPAddr fc_outaddr; // IP address of interface.
+ uint fc_mtu; // Max MTU outgoing.
+ NetTableEntry *fc_srcnte; // Source NTE.
+ IPAddr fc_nexthop; // Next hop.
+ uint fc_datalength; // Length in bytes of data.
+ OptIndex fc_index; // Index of relevant options.
+ uchar fc_optlength; // Length of options.
+ uchar fc_sos; // Send on source indicator.
+ uchar fc_dtype; // Dest type.
+ uchar fc_pad;
+}; /* FWContext */
+
+typedef struct FWContext FWContext;
+
+#define PACKET_FROM_FWQ(_fwq_) (PNDIS_PACKET)((uchar *)(_fwq_) - (offsetof(struct FWContext, fc_q) + \
+ offsetof(NDIS_PACKET, ProtocolReserved)))
+
+//* Route send queue structure. This consists of a dummy FWContext for use as
+// a queue head, a count of sends pending on the interface, and a count of packets
+// in the queue.
+struct RouteSendQ {
+ FWQ rsq_qh;
+ uint rsq_pending;
+ uint rsq_maxpending;
+ uint rsq_qlength;
+ uint rsq_running;
+ DEFINE_LOCK_STRUCTURE(rsq_lock)
+}; /* RouteSendQ */
+
+typedef struct RouteSendQ RouteSendQ;
+
+
+//* Routing interface, a superset of the ordinary interface when we're configured as a router.
+struct RouteInterface {
+ Interface ri_if;
+ RouteSendQ ri_q;
+}; /* RouteInterface */
+
+typedef struct RouteInterface RouteInterface;
+
+extern IPMask IPMaskTable[];
+
+#define IPNetMask(a) IPMaskTable[(*(uchar *)&(a)) >> 4]
+
+
diff --git a/private/ntos/tdi/tcpip/ip/ipstatus.c b/private/ntos/tdi/tcpip/ip/ipstatus.c
new file mode 100644
index 000000000..e71e36280
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/ipstatus.c
@@ -0,0 +1,205 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** ipstatus.c - IP status routines.
+//
+// This module contains all routines related to status indications.
+//
+
+
+#include "oscfg.h"
+#include "cxport.h"
+#include "ndis.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "llipif.h"
+#include "iproute.h"
+#include "ipinfo.h"
+
+#if 0
+EXTERNAL_LOCK(PILock)
+#endif
+extern ProtInfo IPProtInfo[]; // Protocol information table.
+extern int NextPI; // Next PI field to be used.
+extern ProtInfo *RawPI; // Raw IP protinfo
+
+//* FindULStatus - Find the upper layer status handler.
+//
+// Called when we need to find the upper layer status handler for a particular
+// protocol.
+//
+// Entry: Protocol - Protocol to look up
+//
+// Returns: A pointer to the ULStatus proc, or NULL if it can't find one.
+//
+ULStatusProc
+FindULStatus(uchar Protocol)
+{
+ ULStatusProc StatusProc = (ULStatusProc)NULL;
+ int i;
+#if 0
+ CTELockHandle Handle;
+
+
+ CTEGetLock(&PILock, &Handle);
+#endif
+ for ( i = 0; i < NextPI; i++) {
+ if (IPProtInfo[i].pi_protocol == Protocol) {
+ StatusProc = IPProtInfo[i].pi_status;
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+ return StatusProc;
+ }
+ }
+
+ if (RawPI != NULL) {
+ StatusProc = RawPI->pi_status;
+ }
+
+#if 0
+ CTEFreeLock(&PILock, Handle);
+#endif
+
+ return StatusProc;
+}
+
+
+//* ULMTUNotify - Notify the upper layers of an MTU change.
+//
+// Called when we need to notify the upper layers of an MTU change. We'll
+// loop through the status table, calling each status proc with the info.
+//
+// This routine doesn't do any locking of the protinfo table. We might need
+// to check this.
+//
+// Input: Dest - Destination address affected.
+// Src - Source address affected.
+// Prot - Protocol that triggered change, if any.
+// Ptr - Pointer to protocol info, if any.
+// NewMTU - New MTU to tell them about.
+//
+// Returns: Nothing.
+//
+void
+ULMTUNotify(IPAddr Dest, IPAddr Src, uchar Prot, void *Ptr, uint NewMTU)
+{
+ ULStatusProc StatusProc;
+ int i;
+
+ // First, notify the specific client that a frame has been dropped
+ // and needs to be retransmitted.
+
+ StatusProc = FindULStatus(Prot);
+ if (StatusProc != NULL)
+ (*StatusProc)(IP_NET_STATUS, IP_SPEC_MTU_CHANGE, Dest, Src,
+ NULL_IP_ADDR, NewMTU, Ptr);
+
+ // Now notify all UL entities that the MTU has changed.
+ for (i = 0; i < NextPI; i++) {
+ StatusProc = IPProtInfo[i].pi_status;
+
+ if (StatusProc != NULL)
+ (*StatusProc)(IP_HW_STATUS, IP_MTU_CHANGE, Dest, Src, NULL_IP_ADDR,
+ NewMTU, Ptr);
+ }
+}
+
+#ifdef CHICAGO
+
+//* IPULUnloadNotify - Notify clients that we're unloading.
+//
+// Called when we receive an unload message. We'll notify the upper layers
+// that we're unloading.
+//
+// Input: Nothing.
+//
+// Returns: Nothing.
+//
+void
+IPULUnloadNotify(void)
+{
+ ULStatusProc StatusProc;
+ int i;
+
+ // Now notify all UL entities that the MTU has changed.
+ for (i = 0; i < NextPI; i++) {
+ StatusProc = IPProtInfo[i].pi_status;
+
+ if (StatusProc != NULL)
+ (*StatusProc)(IP_HW_STATUS, IP_UNLOAD, NULL_IP_ADDR, NULL_IP_ADDR,
+ NULL_IP_ADDR, 0, NULL);
+ }
+}
+
+#endif
+
+//* IPStatus - Handle a link layer status call.
+//
+// This is the routine called by the link layer when some sort of 'important'
+// status change occurs.
+//
+// Entry: Context - Context value we gave to the link layer.
+// Status - Status change code.
+// Buffer - Pointer to buffer of status information.
+// BufferSize - Size of Buffer.
+//
+// Returns: Nothing.
+//
+void
+IPStatus(void *Context, uint Status, void *Buffer, uint BufferSize)
+{
+ NetTableEntry *NTE = (NetTableEntry *)Context;
+ LLIPSpeedChange *LSC;
+ LLIPMTUChange *LMC;
+ LLIPAddrMTUChange *LAM;
+ uint NewMTU;
+ Interface *IF;
+
+
+ switch (Status) {
+
+ case LLIP_STATUS_SPEED_CHANGE:
+ if (BufferSize < sizeof(LLIPSpeedChange))
+ break;
+ LSC = (LLIPSpeedChange *)Buffer;
+ NTE->nte_if->if_speed = LSC->lsc_speed;
+ break;
+ case LLIP_STATUS_MTU_CHANGE:
+ if (BufferSize < sizeof(LLIPMTUChange))
+ break;
+ // Walk through the NTEs on the IF, updating their MTUs.
+ IF = NTE->nte_if;
+ LMC = (LLIPMTUChange *)Buffer;
+ IF->if_mtu = LMC->lmc_mtu;
+ NewMTU = LMC->lmc_mtu - sizeof(IPHeader);
+ NTE = IF->if_nte;
+ while (NTE != NULL) {
+ NTE->nte_mss = NewMTU;
+ NTE = NTE->nte_ifnext;
+ }
+ RTWalk(SetMTUOnIF, IF, &NewMTU);
+ break;
+ case LLIP_STATUS_ADDR_MTU_CHANGE:
+ if (BufferSize < sizeof(LLIPAddrMTUChange))
+ break;
+ // The MTU for a specific remote address has changed. Update all
+ // routes that use that remote address as a first hop, and then
+ // add a host route to that remote address, specifying the new
+ // MTU.
+ LAM = (LLIPAddrMTUChange *)Buffer;
+ NewMTU = LAM->lam_mtu - sizeof(IPHeader);
+ RTWalk(SetMTUToAddr, &LAM->lam_addr, &NewMTU);
+ AddRoute(LAM->lam_addr, HOST_MASK, IPADDR_LOCAL, NTE->nte_if, NewMTU,
+ 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, GetRouteContext(LAM->lam_addr,
+ NTE->nte_addr));
+ break;
+ default:
+ break;
+ }
+
+}
+
diff --git a/private/ntos/tdi/tcpip/ip/ipxmit.c b/private/ntos/tdi/tcpip/ip/ipxmit.c
new file mode 100644
index 000000000..61ee32b90
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/ipxmit.c
@@ -0,0 +1,1949 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** ipxmit.c - IP transmit routines.
+//
+// This module contains all transmit related IP routines.
+//
+
+#include "oscfg.h"
+#include "cxport.h"
+#include "ndis.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "ipinit.h"
+#include "info.h"
+#include "iproute.h"
+#include "iprtdef.h"
+#include "ipfilter.h"
+
+typedef struct NdisResEntry {
+ struct NdisResEntry *nre_next;
+ NDIS_HANDLE nre_handle;
+ uchar *nre_buffer;
+} NdisResEntry;
+
+extern BufferReference *GetBufferReference(void);
+
+extern NetTableEntry *NetTableList; // Pointer to the net table.
+extern NetTableEntry *LoopNTE; // Pointer to loopback NTE.
+extern NetTableEntry *DHCPNTE; // Pointer to NTE currently being
+ // DHCP'd.
+
+extern ulong TimeStamp; // Starting timestamp.
+extern ulong TSFlag; // Mask to use on this.
+extern uint NumNTE;
+
+//* Global variables for buffers and packets.
+DEFINE_LOCK_STRUCTURE(HeaderLock)
+#ifdef NT
+SLIST_HEADER PacketList;
+SLIST_HEADER HdrBufList;
+#else
+PNDIS_PACKET PacketList;
+PNDIS_BUFFER HdrBufList = NULL;
+#endif
+
+NdisResEntry *PacketPoolList = NULL;
+NdisResEntry *HdrPoolList = NULL;
+
+uint CurrentPacketCount = 0;
+uint MaxPacketCount = 0xfffffff;
+
+uint CurrentHdrBufCount = 0;
+uint MaxHdrBufCount = 0xffffffff;
+
+NDIS_HANDLE BufferPool;
+
+#define HDR_BUF_GROW_COUNT 16
+#define PACKET_GROW_COUNT 16
+
+//* Global IP ID.
+ulong IPID;
+
+//** FreeIPHdrBuffer - Free a buffer back to the pool.
+//
+// Input: Buffer - Hdr buffer to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeIPHdrBuffer(PNDIS_BUFFER Buffer)
+{
+
+#ifdef VXD
+ NDIS_BUFFER_LINKAGE(Buffer) = HdrBufList;
+ HdrBufList = Buffer;
+#else
+
+ ExInterlockedPushEntrySList(
+ &HdrBufList,
+ STRUCT_OF(SINGLE_LIST_ENTRY, &(Buffer->Next), Next),
+ &HeaderLock
+ );
+
+#endif
+
+}
+
+//** FreeIPBufferChain - Free a chain of IP buffers.
+//
+// This routine takes a chain of NDIS_BUFFERs, and frees them all.
+//
+// Entry: Buffer - Pointer to buffer chain to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeIPBufferChain(PNDIS_BUFFER Buffer)
+{
+ PNDIS_BUFFER NextBuffer;
+
+ while (Buffer != (PNDIS_BUFFER)NULL) {
+ NdisGetNextBuffer(Buffer, &NextBuffer);
+ NdisFreeBuffer(Buffer);
+ Buffer = NextBuffer;
+ }
+}
+
+//* FreeIPPacket - Free an IP packet when we're done with it.
+//
+// Called when a send completes and a packet needs to be freed. We look at the
+// packet, decide what to do with it, and free the appropriate components.
+//
+// Entry: Packet - Packet to be freed.
+//
+// Returns: Pointer to next unfreed buffer on packet, or NULL if all buffers freed
+// (i.e. this was a fragmented packet).
+//
+PNDIS_BUFFER
+FreeIPPacket(PNDIS_PACKET Packet)
+{
+ PNDIS_BUFFER NextBuffer, OldBuffer;
+ PacketContext *pc = (PacketContext *)Packet->ProtocolReserved;
+
+
+ // BUGBUG - Get NDIS fixed to make this portable.
+#ifdef VXD
+ NextBuffer = Packet->Private.Head;
+#else // VXD
+ NdisQueryPacket(Packet, NULL, NULL, &NextBuffer, NULL);
+#endif // VXD
+
+ // If there's no IP header on this packet, we have nothing else to do.
+ if (!(pc->pc_common.pc_flags & (PACKET_FLAG_IPHDR | PACKET_FLAG_FW))) {
+ CTEAssert(pc->pc_common.pc_flags == 0);
+
+ NdisReinitializePacket(Packet);
+
+#ifdef VXD
+ pc->pc_common.pc_link = PacketList;
+ PacketList = Packet;
+#else
+ ExInterlockedPushEntrySList(
+ &PacketList,
+ STRUCT_OF(SINGLE_LIST_ENTRY, &(pc->pc_common.pc_link), Next),
+ &HeaderLock
+ );
+#endif
+
+ return NextBuffer;
+ }
+
+ pc->pc_common.pc_flags &= ~PACKET_FLAG_IPHDR;
+
+ OldBuffer = NextBuffer;
+ CTEAssert(OldBuffer != NULL);
+
+ NextBuffer = NDIS_BUFFER_LINKAGE(NextBuffer);
+
+ if (pc->pc_common.pc_flags & PACKET_FLAG_OPTIONS) { // Have options with
+ // this packet.
+ PNDIS_BUFFER OptBuffer;
+ void *Options;
+ uint OptSize;
+
+ OptBuffer = NextBuffer;
+ CTEAssert(OptBuffer != NULL);
+
+ NdisGetNextBuffer(OptBuffer,&NextBuffer);
+
+ CTEAssert(NextBuffer != NULL);
+
+ NdisQueryBuffer(OptBuffer, &Options, &OptSize);
+ // If this is a FW packet, the options don't really belong to us, so
+ // don't free them.
+ if (!(pc->pc_common.pc_flags & PACKET_FLAG_FW))
+ CTEFreeMem(Options);
+ NdisFreeBuffer(OptBuffer);
+ pc->pc_common.pc_flags &= ~PACKET_FLAG_OPTIONS;
+ }
+
+ if (pc->pc_common.pc_flags & PACKET_FLAG_IPBUF) { // This packet is all
+ // IP buffers.
+ (void)FreeIPBufferChain(NextBuffer);
+ NextBuffer = (PNDIS_BUFFER)NULL;
+ pc->pc_common.pc_flags &= ~PACKET_FLAG_IPBUF;
+ }
+
+
+ if (!(pc->pc_common.pc_flags & PACKET_FLAG_FW)) {
+ FreeIPHdrBuffer(OldBuffer);
+ NdisReinitializePacket(Packet);
+#ifdef _PNP_POWER
+ pc->pc_if = NULL;
+#endif
+
+#ifdef VXD
+ pc->pc_common.pc_link = PacketList;
+ PacketList = Packet;
+#else
+ ExInterlockedPushEntrySList(
+ &PacketList,
+ STRUCT_OF(SINGLE_LIST_ENTRY, &(pc->pc_common.pc_link), Next),
+ &HeaderLock
+ );
+#endif
+ }
+
+ return NextBuffer;
+}
+
+//** GrowIPPacketList - Grow the number of packets in our list.
+//
+// Called when we need to grow the number of packets in our list. We assume
+// this routine is called with the HeaderLock held. We check to see if
+// we've reached our limit on the number of packets, and if we haven't we'll
+// grow the free list.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to newly allocated packet, or NULL if this faild.
+//
+PNDIS_PACKET
+GrowIPPacketList(void)
+{
+ NdisResEntry *NewEntry;
+ NDIS_STATUS Status;
+ PNDIS_PACKET Packet, ReturnPacket;
+ uint i;
+ CTELockHandle Handle;
+
+ CTEGetLock(&HeaderLock, &Handle);
+
+ if (CurrentPacketCount >= MaxPacketCount)
+ goto failure;
+
+ // First, allocate a tracking structure.
+ NewEntry = CTEAllocMem(sizeof(NdisResEntry));
+ if (NewEntry == NULL)
+ goto failure;
+
+ // Got a tracking structure. Now allocate a packet pool.
+ NdisAllocatePacketPool(&Status, &NewEntry->nre_handle, PACKET_GROW_COUNT,
+ sizeof(PacketContext));
+
+ if (Status != NDIS_STATUS_SUCCESS) {
+ CTEFreeMem(NewEntry);
+ goto failure;
+ }
+
+ // We've allocated the pool. Now initialize the packets, and link them
+ // on the free list.
+ ReturnPacket = NULL;
+
+ // Link the new NDIS resource tracker entry onto the list.
+ NewEntry->nre_next = PacketPoolList;
+ PacketPoolList = NewEntry;
+ CurrentPacketCount += PACKET_GROW_COUNT;
+ CTEFreeLock(&HeaderLock, Handle);
+
+ for (i = 0; i < PACKET_GROW_COUNT; i++) {
+ PacketContext *PC;
+
+ NdisAllocatePacket(&Status, &Packet, NewEntry->nre_handle);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ CTEAssert(FALSE);
+ break;
+ }
+
+ CTEMemSet(Packet->ProtocolReserved, 0, sizeof(PacketContext));
+ PC = (PacketContext *)Packet->ProtocolReserved;
+ PC->pc_common.pc_owner = PACKET_OWNER_IP;
+ if (i != 0) {
+ (void)FreeIPPacket(Packet);
+ } else
+ ReturnPacket = Packet;
+
+ }
+
+ // We've put all but the first one on the list. Return the first one.
+ return ReturnPacket;
+
+failure:
+ CTEFreeLock(&HeaderLock, Handle);
+ return NULL;
+
+}
+
+
+//** GrowHdrBufList - Grow the our IP header buffer list.
+//
+// Called when we need to grow our header buffer list. We allocate a tracking
+// structure, a buffer pool and a bunch of buffers. Put them all together
+// and link them on the list.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to newly header buffer, or NULL if this faild.
+//
+PNDIS_BUFFER
+GrowHdrBufList(void)
+{
+ NdisResEntry *NewEntry;
+ NDIS_STATUS Status;
+ PNDIS_BUFFER Buffer, ReturnBuffer;
+ uchar *Hdr;
+ uint i;
+ CTELockHandle Handle;
+
+ CTEGetLock(&HeaderLock, &Handle);
+
+ // Make sure we can grow.
+ if (CurrentHdrBufCount >= MaxHdrBufCount)
+ goto failure;
+
+ // First, allocate a tracking structure.
+ NewEntry = CTEAllocMem(sizeof(NdisResEntry));
+ if (NewEntry == NULL)
+ goto failure;
+
+ // Got a tracking structure. Now allocate a buffer pool.
+ NdisAllocateBufferPool(&Status, &NewEntry->nre_handle, HDR_BUF_GROW_COUNT);
+
+ if (Status != NDIS_STATUS_SUCCESS) {
+ CTEFreeMem(NewEntry);
+ goto failure;
+ }
+
+ // We've allocated the pool. Now allocate memory for the buffers.
+ Hdr = CTEAllocMem(sizeof(IPHeader) * HDR_BUF_GROW_COUNT);
+ if (Hdr == NULL) {
+ // Couldn't get memory for the headers.
+ NdisFreeBufferPool(NewEntry->nre_handle);
+ CTEFreeMem(NewEntry);
+ goto failure;
+ }
+
+ NewEntry->nre_buffer = Hdr;
+
+ NewEntry->nre_next = HdrPoolList;
+ HdrPoolList = NewEntry;
+ ReturnBuffer = NULL;
+ CurrentHdrBufCount += HDR_BUF_GROW_COUNT;
+ CTEFreeLock(&HeaderLock, Handle);
+
+ for (i = 0; i < HDR_BUF_GROW_COUNT; i++) {
+
+ NdisAllocateBuffer(&Status, &Buffer, NewEntry->nre_handle,
+ Hdr, sizeof(IPHeader));
+ if (Status != NDIS_STATUS_SUCCESS) {
+ CTEAssert(FALSE);
+ break;
+ }
+ if (i != 0) {
+ FreeIPHdrBuffer(Buffer);
+ } else
+ ReturnBuffer = Buffer;
+
+ Hdr += sizeof(IPHeader);
+
+ }
+
+ // Update the count for any we didn't actually allocate.
+ CTEInterlockedAddUlong(&CurrentHdrBufCount, i - HDR_BUF_GROW_COUNT,
+ &HeaderLock);
+
+ // We've put all but the first one on the list. Return the first one.
+ return ReturnBuffer;
+
+failure:
+ CTEFreeLock(&HeaderLock, Handle);
+ return NULL;
+
+}
+
+//** GetIPPacket - Get an NDIS packet to use.
+//
+// A routine to allocate an NDIS packet.
+//
+// Entry: Nothing.
+//
+// Returns: Pointer to NDIS_PACKET if allocated, or NULL.
+//
+PNDIS_PACKET
+GetIPPacket(void)
+{
+ PNDIS_PACKET Packet;
+
+
+#ifdef VXD
+ Packet = PacketList;
+ if (Packet != (PNDIS_PACKET)NULL) {
+ PacketContext *PC;
+
+ PC = (PacketContext *)Packet->ProtocolReserved;
+ PacketList = PC->pc_common.pc_link;
+ return Packet;
+#else
+ PSINGLE_LIST_ENTRY Link;
+ PacketContext *PC;
+ struct PCCommon *Common;
+
+ Link = ExInterlockedPopEntrySList(
+ &PacketList,
+ &HeaderLock
+ );
+ if (Link != NULL) {
+ Common = STRUCT_OF(struct PCCommon, Link, pc_link);
+ PC = STRUCT_OF(PacketContext, Common, pc_common);
+ Packet = STRUCT_OF(NDIS_PACKET, PC, ProtocolReserved);
+
+ return Packet;
+#endif
+
+
+ } else {
+ // Couldn't get a packet. Try to grow the list.
+ Packet = GrowIPPacketList();
+ }
+
+ return Packet;
+}
+
+
+//** GetIPHdrBuffer - Get an IP header buffer.
+//
+// A routine to allocate an IP header buffer, with an NDIS buffer.
+//
+// Entry: Nothing.
+//
+// Returns: Pointer to NDIS_BUFFER if allocated, or NULL.
+//
+PNDIS_BUFFER
+GetIPHdrBuffer(void)
+{
+ PNDIS_BUFFER Buffer;
+
+#ifdef VXD
+ Buffer = HdrBufList;
+ if (Buffer != NULL) {
+
+ HdrBufList = NDIS_BUFFER_LINKAGE(Buffer);
+ NDIS_BUFFER_LINKAGE(Buffer) = NULL;
+#else
+ PSINGLE_LIST_ENTRY BufferLink;
+
+ BufferLink = ExInterlockedPopEntrySList(
+ &HdrBufList,
+ &HeaderLock
+ );
+ if (BufferLink != NULL) {
+ Buffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next);
+ NDIS_BUFFER_LINKAGE(Buffer) = NULL;
+
+ return Buffer;
+
+#endif
+
+ } else {
+ Buffer = GrowHdrBufList();
+ }
+
+ return Buffer;
+
+}
+
+
+//** GetIPHeader - Get a header buffer and packet.
+//
+// Called when we need to get a header buffer and packet. We allocate both,
+// and chain them together.
+//
+// Input: Pointer to where to store packet.
+//
+// Returns: Pointer to IP header.
+//
+IPHeader *
+GetIPHeader(PNDIS_PACKET *PacketPtr)
+{
+ PNDIS_BUFFER Buffer;
+ PNDIS_PACKET Packet;
+
+
+ Packet = GetIPPacket();
+ if (Packet != NULL) {
+ Buffer = GetIPHdrBuffer();
+ if (Buffer != NULL) {
+ PacketContext *PC = (PacketContext *)Packet->ProtocolReserved;
+
+ NdisChainBufferAtBack(Packet, Buffer);
+ *PacketPtr = Packet;
+ PC->pc_common.pc_flags |= PACKET_FLAG_IPHDR;
+ return (IPHeader *)NdisBufferVirtualAddress(Buffer);
+
+ } else
+ FreeIPPacket(Packet);
+ }
+ return NULL;
+}
+
+
+//** ReferenceBuffer - Reference a buffer.
+//
+// Called when we need to update the count of a BufferReference strucutre, either
+// by a positive or negative value. If the count goes to 0, we'll free the buffer
+// reference and return success. Otherwise we'll return pending.
+//
+// Entry: BR - Pointer to buffer reference.
+// Count - Amount to adjust refcount by.
+//
+// Returns: Success, or pending.
+//
+int
+ReferenceBuffer(BufferReference *BR, int Count)
+{
+ CTELockHandle handle;
+ int NewCount;
+
+ CTEGetLock(&BR->br_lock, &handle);
+ BR->br_refcount += Count;
+ NewCount = BR->br_refcount;
+ CTEFreeLock(&BR->br_lock, handle);
+ return NewCount;
+}
+
+//* IPSendComplete - IP send complete handler.
+//
+// Called by the link layer when a send completes. We're given a pointer to a
+// net structure, as well as the completing send packet and the final status of
+// the send.
+//
+// Entry: Context - Context we gave to the link layer.
+// Packet - Completing send packet.
+// Status - Final status of send.
+//
+// Returns: Nothing.
+//
+void
+IPSendComplete(void *Context, PNDIS_PACKET Packet, NDIS_STATUS Status)
+{
+ NetTableEntry *NTE = (NetTableEntry *)Context;
+ PacketContext *PContext = (PacketContext *)Packet->ProtocolReserved;
+ PNDIS_BUFFER Buffer;
+ void (*xmitdone)(void *, PNDIS_BUFFER); // Pointer to xmit done routine.
+ void *UContext; // Upper layer context.
+ BufferReference *BufRef; // Buffer reference, if any.
+#ifdef _PNP_POWER
+ Interface *IF; // The interface on which this
+ // completed.
+#endif
+
+ xmitdone = PContext->pc_pi->pi_xmitdone; // Copy useful information from packet.
+ UContext = PContext->pc_context;
+ BufRef = PContext->pc_br;
+#ifdef _PNP_POWER
+ IF = PContext->pc_if;
+#endif
+
+ Buffer = FreeIPPacket(Packet);
+ if (BufRef == (BufferReference *)NULL) {
+#ifdef DEBUG
+ if (!Buffer)
+ DEBUGCHK;
+#endif
+ (*xmitdone)(UContext, Buffer);
+ } else {
+ if (!ReferenceBuffer(BufRef, -1)) {
+ Buffer = BufRef->br_buffer;
+#ifdef DEBUG
+ if (!Buffer)
+ DEBUGCHK;
+#endif
+ CTEFreeMem(BufRef);
+ (*xmitdone)(UContext, Buffer);
+ } else {
+#ifdef _PNP_POWER
+ // We're not done with the send yet, so NULL the IF to
+ // prevent dereferencing it.
+ IF = NULL;
+#endif
+ }
+ }
+
+#ifdef _PNP_POWER
+ // We're done with the packet now, we may need to dereference
+ // the interface.
+ if (IF == NULL) {
+ return;
+ } else {
+ DerefIF(IF);
+ }
+#endif
+
+}
+
+
+#ifndef NT
+
+//** xsum - Checksum a flat buffer.
+//
+// This is the lowest level checksum routine. It returns the uncomplemented
+// checksum of a flat buffer.
+//
+// Entry: Buffer - Buffer to be checksummed.
+// Size - Size in bytes of Buffer.
+//
+// Returns: The uncomplemented checksum of buffer.
+//
+ushort
+xsum(void *Buffer, int Size)
+{
+ ushort UNALIGNED *Buffer1 = (ushort UNALIGNED *)Buffer; // Buffer expressed as shorts.
+ ulong csum = 0;
+
+ while (Size > 1) {
+ csum += *Buffer1++;
+ Size -= sizeof(ushort);
+ }
+
+ if (Size)
+ csum += *(uchar *)Buffer1; // For odd buffers, add in last byte.
+
+ csum = (csum >> 16) + (csum & 0xffff);
+ csum += (csum >> 16);
+ return (ushort)csum;
+}
+
+#endif // NT
+
+
+//** SendIPPacket - Send an IP packet.
+//
+// Called when we have a filled in IP packet we need to send. Basically, we
+// compute the xsum and send the thing.
+//
+// Entry: IF - Interface to send it on.
+// FirstHop - First hop address to send it to.
+// Packet - Packet to be sent.
+// Buffer - Buffer to be sent.
+// Header - Pointer to IP Header of packet.
+// Options - Pointer to option buffer.
+// OptionLength - Length of options.
+//
+// Returns: IP_STATUS of attempt to send.
+IP_STATUS
+SendIPPacket(Interface *IF, IPAddr FirstHop, PNDIS_PACKET Packet,
+ PNDIS_BUFFER Buffer, IPHeader *Header, uchar *Options, uint OptionSize)
+{
+ ulong csum;
+ NDIS_STATUS Status;
+
+
+ csum = xsum(Header, sizeof(IPHeader));
+ if (Options) { // We have options, oh boy.
+ PNDIS_BUFFER OptBuffer;
+ PacketContext *pc = (PacketContext *)Packet->ProtocolReserved;
+ NdisAllocateBuffer(&Status, &OptBuffer, BufferPool, Options, OptionSize);
+ if (Status != NDIS_STATUS_SUCCESS) { // Couldn't get the needed
+ // option buffer.
+ CTEFreeMem(Options);
+ FreeIPPacket(Packet);
+ return IP_NO_RESOURCES;
+ }
+ pc->pc_common.pc_flags |= PACKET_FLAG_OPTIONS;
+ NdisChainBufferAtBack(Packet, OptBuffer);
+ csum += xsum(Options, OptionSize);
+ csum = (csum >> 16) + (csum & 0xffff);
+ csum += (csum >> 16);
+ }
+ Header->iph_xsum = ~(ushort)csum;
+ NdisChainBufferAtBack(Packet,Buffer);
+
+ Status = (*(IF->if_xmit))(IF->if_lcontext, Packet, FirstHop, NULL);
+
+ if (Status == NDIS_STATUS_PENDING)
+ return IP_PENDING;
+
+ // Status wasn't pending. Free the packet, and map the status.
+ FreeIPPacket(Packet);
+ if (Status == NDIS_STATUS_SUCCESS)
+ return IP_SUCCESS;
+ else
+ return IP_HW_ERROR;
+}
+
+//* SendDHCPPacket - Send a broadcast for DHCP.
+//
+// Called when somebody is sending a broadcast packet with a NULL source
+// address. We assume this means they're sending a DHCP packet. We loop
+// through the NTE table, and when we find an entry that's not valid we
+// send out the interface associated with that entry.
+//
+// Input: Dest - Destination of packet.
+// Packet - Packet to be send.
+// Buffer - Buffer chain to be sent.
+// Header - Pointer to header buffer being sent.
+//
+// Return: Status of send attempt.
+//
+IP_STATUS
+SendDHCPPacket(IPAddr Dest, PNDIS_PACKET Packet, PNDIS_BUFFER Buffer,
+ IPHeader *IPH)
+{
+ if (DHCPNTE != NULL && ((DHCPNTE->nte_flags & (NTE_VALID | NTE_ACTIVE))
+ == NTE_ACTIVE)) {
+ // The DHCP NTE is currently invalid, and active. Send on that
+ // interface.
+ return SendIPPacket(DHCPNTE->nte_if, Dest, Packet, Buffer, IPH, NULL,
+ 0);
+ }
+
+ // Didn't find an invalid NTE! Free the resources, and return the failure.
+ FreeIPPacket(Packet);
+ IPSInfo.ipsi_outdiscards++;
+ return IP_DEST_HOST_UNREACHABLE;
+
+}
+
+
+//
+// Macros needed by IpCopyBuffer
+//
+#ifdef VXD
+
+#define NdisBufferLength(Buffer) (Buffer)->Length
+#define NdisBufferVirtualAddress(Buffer) (Buffer)->VirtualAddress
+
+#else // VXD
+#ifdef NT
+
+#define NdisBufferLength(Buffer) MmGetMdlByteCount(Buffer)
+#define NdisBufferVirtualAddress(Buffer) MmGetSystemAddressForMdl(Buffer)
+
+#else // NT
+
+#error Need appropriate NDIS macros here
+
+#endif NT
+#endif // VXD
+//* IPCopyBuffer - Copy an NDIS buffer chain at a specific offset.
+//
+// This is the IP version of the function NdisCopyBuffer, which didn't
+// get done properly in NDIS3. We take in an NDIS buffer chain, an offset,
+// and a length, and produce a buffer chain describing that subset of the
+// input buffer chain.
+//
+// This routine is not particularly efficient. Since only IPFragment uses
+// it currently, it might be better to just incorporate this functionality
+// directly into IPFragment.
+//
+// Input: OriginalBuffer - Original buffer chain to copy from.
+// Offset - Offset from start to dup.
+// Length - Length in bytes to dup.
+//
+// Returns: Pointer to new chain if we can make one, NULL if we can't.
+//
+PNDIS_BUFFER
+IPCopyBuffer(PNDIS_BUFFER OriginalBuffer,uint Offset, uint Length)
+{
+
+ PNDIS_BUFFER CurrentBuffer; // Pointer to current buffer.
+ PNDIS_BUFFER *NewBuffer; // Pointer to pointer to current new buffer.
+ PNDIS_BUFFER FirstBuffer; // First buffer in new chain.
+ UINT CopyLength; // Length of current copy.
+ NDIS_STATUS NewStatus; // Status of NdisAllocateBuffer operation.
+
+ // First skip over the number of buffers we need to to reach Offset.
+ CurrentBuffer = OriginalBuffer;
+
+ while (Offset >= NdisBufferLength(CurrentBuffer)) {
+ Offset -= NdisBufferLength(CurrentBuffer);
+ CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
+ if (CurrentBuffer == (PNDIS_BUFFER)NULL)
+ return NULL;
+ }
+
+ // Now CurrentBuffer is the buffer from which we start building the new chain, and
+ // Offset is the offset into CurrentBuffer from which to start.
+ FirstBuffer = NULL;
+ NewBuffer = &FirstBuffer;
+
+ do {
+
+ CopyLength = MIN(
+ Length,
+ NdisBufferLength(CurrentBuffer) - Offset
+ );
+ NdisAllocateBuffer(&NewStatus, NewBuffer, BufferPool,
+ ((uchar *)NdisBufferVirtualAddress(CurrentBuffer)) + Offset,
+ CopyLength);
+ if (NewStatus != NDIS_STATUS_SUCCESS)
+ break;
+
+ Offset = 0; // No offset from next buffer.
+ NewBuffer = &(NDIS_BUFFER_LINKAGE(*NewBuffer));
+ CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
+ Length -= CopyLength;
+ } while (Length != 0 && CurrentBuffer != (PNDIS_BUFFER)NULL);
+
+ if (Length == 0) { // We succeeded
+ return FirstBuffer;
+ } else { // We exited the loop because of an error.
+
+ // We need to free any allocated buffers, and return.
+ CurrentBuffer = FirstBuffer;
+ while (CurrentBuffer != (PNDIS_BUFFER)NULL) {
+ PNDIS_BUFFER Temp = CurrentBuffer;
+ CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
+ NdisFreeBuffer(Temp);
+ }
+ return NULL;
+ }
+}
+
+//** IPFragment - Fragment and send an IP datagram.
+//
+// Called when an outgoing datagram is larger than the local MTU, and needs to be
+// fragmented. This is a somewhat complicated operation. The caller gives us a
+// prebuilt IP header, packet, and options. We use the header and packet on
+// the last fragment of the send, as the passed in header already has the more
+// fragments bit set correctly for the last fragment.
+//
+// The basic idea is to figure out the maximum size which we can send as a multiple
+// of 8. Then, while we can send a maximum size fragment we'll allocate a header, packet,
+// etc. and send it. At the end we'll send the final fragment using the provided header
+// and packet.
+//
+// Entry: DestIF - Outbound interface of datagram.
+// MTU - MTU to use in transmitting.
+// FirstHop - First (or next) hop for this datagram.
+// Packet - Packet to be sent.
+// Header - Prebuilt IP header.
+// Buffer - Buffer chain for data to be sent.
+// DataSize - Size in bytes of data.
+// Options - Pointer to option buffer, if any.
+// OptionSize - Size in bytes of option buffer.
+// SentCount - Pointer to where to return pending send count (may be NULL).
+//
+// Returns: IP_STATUS of send.
+//
+IP_STATUS
+IPFragment(Interface *DestIF, uint MTU, IPAddr FirstHop,
+ PNDIS_PACKET Packet, IPHeader *Header, PNDIS_BUFFER Buffer, uint DataSize,
+ uchar *Options, uint OptionSize, int *SentCount)
+{
+ BufferReference *BR; // Buffer reference we'll use.
+ PacketContext *PContext = (PacketContext *)Packet->ProtocolReserved;
+ PacketContext *CurrentContext; // Current Context in use.
+ uint MaxSend; // Maximum size (in bytes) we can send here.
+ uint PendingSends = 0; // Counter of how many pending sends we have.
+ PNDIS_BUFFER CurrentBuffer; // Current buffer to be sent.
+ PNDIS_PACKET CurrentPacket; // Current packet we're using.
+ IP_STATUS SendStatus; // Status of send command.
+ IPHeader *CurrentHeader; // Current header buffer we're using.
+ ushort Offset = 0; // Current offset into fragmented packet.
+ ushort StartOffset; // Starting offset of packet.
+ ushort RealOffset; // Offset of new fragment.
+ uint FragOptSize = 0; // Size (in bytes) of fragment options.
+ uchar FragmentOptions[MAX_OPT_SIZE]; // Master copy of options sent for fragments.
+ uchar Error = FALSE; // Set if we get an error in our main loop.
+
+ MaxSend = (MTU - OptionSize) & ~7; // Determine max send size.
+
+#ifdef DEBUG
+ if (MaxSend >= DataSize)
+ DEBUGCHK;
+#endif
+
+ BR = PContext->pc_br; // Get the buffer reference we'll need.
+
+#ifdef DEBUG
+ if (!BR)
+ DEBUGCHK;
+#endif
+
+ if (Header->iph_offset & IP_DF_FLAG) { // Don't fragment flag set.
+ // Error out.
+ FreeIPPacket(Packet);
+ if (Options)
+ CTEFreeMem(Options);
+ if (SentCount == (int *)NULL) // No sent count is to be
+ // returned.
+ CTEFreeMem(BR);
+ IPSInfo.ipsi_fragfails++;
+ return IP_PACKET_TOO_BIG;
+ }
+
+ StartOffset = Header->iph_offset & IP_OFFSET_MASK;
+ StartOffset = net_short(StartOffset) * 8;
+
+ // If we have any options, copy the ones that need to be copied, and figure
+ // out the size of these new copied options.
+
+ if (Options != (uchar *)NULL) { // We have options.
+ uchar *TempOptions = Options;
+ const uchar *EndOptions = (const uchar *)(Options+OptionSize);
+
+ // Copy the options into the fragment options buffer.
+ CTEMemSet(FragmentOptions, IP_OPT_EOL, MAX_OPT_SIZE);
+ while ((TempOptions[IP_OPT_TYPE] != IP_OPT_EOL) &&
+ (TempOptions < EndOptions)) {
+ if (TempOptions[IP_OPT_TYPE] & IP_OPT_COPIED) { // This option needs
+ // to be copied.
+ uint TempOptSize;
+
+ TempOptSize = TempOptions[IP_OPT_LENGTH];
+ CTEMemCopy(&FragmentOptions[FragOptSize], TempOptions,
+ TempOptSize);
+ FragOptSize += TempOptSize;
+ TempOptions += TempOptSize;
+ } else { // A non-copied option, just
+ // skip over it.
+ if (TempOptions[IP_OPT_TYPE] == IP_OPT_NOP)
+ TempOptions++;
+ else
+ TempOptions += TempOptions[IP_OPT_LENGTH];
+ }
+ }
+ // Round the copied size up to a multiple of 4.
+ FragOptSize = ((FragOptSize & 3) ? ((FragOptSize & ~3) + 4) : FragOptSize);
+ }
+
+ PContext->pc_common.pc_flags |= PACKET_FLAG_IPBUF;
+
+ // Now, while we can build maximum size fragments, do so.
+ do {
+ if ((CurrentHeader = GetIPHeader(&CurrentPacket)) == (IPHeader *)NULL) {
+ // Couldn't get a buffer. Break out, since no point in sending others.
+ Error = TRUE;
+ break;
+ }
+
+ // Copy the buffer into a new one, if we can.
+ CurrentBuffer = IPCopyBuffer(Buffer, Offset, MaxSend);
+ if (CurrentBuffer == NULL) { // No buffer, free resources and
+ // break.
+ FreeIPPacket(CurrentPacket);
+ Error = TRUE;
+ break;
+ }
+
+ // Options for this send are set up when we get here, either from the
+ // entry from the loop, or from the allocation below.
+
+ // We have all the pieces we need. Put the packet together and send it.
+ CurrentContext = (PacketContext *)CurrentPacket->ProtocolReserved;
+ *CurrentContext = *PContext;
+ *CurrentHeader = *Header;
+ CurrentContext->pc_common.pc_flags &= ~PACKET_FLAG_FW;
+ CurrentHeader->iph_verlen = IP_VERSION +
+ ((OptionSize + sizeof(IPHeader)) >> 2);
+ CurrentHeader->iph_length = net_short(MaxSend+OptionSize+sizeof(IPHeader));
+ RealOffset = (StartOffset + Offset) >> 3;
+ CurrentHeader->iph_offset = net_short(RealOffset) | IP_MF_FLAG;
+
+ SendStatus = SendIPPacket(DestIF, FirstHop, CurrentPacket,
+ CurrentBuffer, CurrentHeader, Options, OptionSize);
+ if (SendStatus == IP_PENDING)
+ PendingSends++;
+
+ IPSInfo.ipsi_fragcreates++;
+ Offset += MaxSend;
+ DataSize -= MaxSend;
+
+ // If we have any fragmented options, set up to use them next time.
+ if (FragOptSize) {
+ Options = CTEAllocMem(OptionSize = FragOptSize);
+ if (Options == (uchar *)NULL) { // Can't get an option
+ // buffer.
+ Error = TRUE;
+ break;
+ }
+ CTEMemCopy(Options, FragmentOptions, OptionSize);
+ } else {
+ Options = (uchar *)NULL;
+ OptionSize = 0;
+ }
+ } while (DataSize > MaxSend);
+
+ // We've sent all of the previous fragments, now send the last one. We already
+ // have the packet and header buffer, as well as options if there are any -
+ // we need to copy the appropriate data.
+ if (!Error) { // Everything went OK above.
+ CurrentBuffer = IPCopyBuffer(Buffer, Offset, DataSize);
+ if (CurrentBuffer == NULL) { // No buffer, free resources
+ // and stop.
+ if (Options)
+ CTEFreeMem(Options); // Free the option buffer
+ FreeIPPacket(Packet);
+ IPSInfo.ipsi_outdiscards++;
+ } else { // Everything's OK, send it.
+ Header->iph_verlen = IP_VERSION + ((OptionSize + sizeof(IPHeader)) >> 2);
+ Header->iph_length = net_short(DataSize+OptionSize+sizeof(IPHeader));
+ RealOffset = (StartOffset + Offset) >> 3;
+ Header->iph_offset = net_short(RealOffset) |
+ (Header->iph_offset & IP_MF_FLAG);
+ SendStatus = SendIPPacket(DestIF, FirstHop, Packet,
+ CurrentBuffer, Header, Options, OptionSize);
+ if (SendStatus == IP_PENDING)
+ PendingSends++;
+ IPSInfo.ipsi_fragcreates++;
+ IPSInfo.ipsi_fragoks++;
+ }
+ } else { // We had some sort of error.
+ // Free resources.
+ FreeIPPacket(Packet);
+ if (Options)
+ CTEFreeMem(Options);
+ IPSInfo.ipsi_outdiscards++;
+ }
+
+
+ // Now, figure out what error code to return and whether or not we need to
+ // free the BufferReference.
+
+ if (SentCount == (int *)NULL) { // No sent count is to be
+ // returned.
+ if (!ReferenceBuffer(BR, PendingSends)) {
+ CTEFreeMem(BR);
+ return IP_SUCCESS;
+ }
+ return IP_PENDING;
+ } else
+ *SentCount += PendingSends;
+
+ return IP_PENDING;
+
+}
+
+//* UpdateRouteOption - Update a SR or RR options.
+//
+// Called by UpdateOptions when it needs to update a route option.
+//
+// Input: RTOption - Pointer to route option to be updated.
+// Address - Address to update with.
+//
+// Returns: TRUE if we updated, FALSE if we didn't.
+//
+uchar
+UpdateRouteOption(uchar *RTOption, IPAddr Address)
+{
+ uchar Pointer; // Pointer value of option.
+
+ Pointer = RTOption[IP_OPT_PTR] - 1;
+ if (Pointer < RTOption[IP_OPT_LENGTH]) {
+ if ((RTOption[IP_OPT_LENGTH] - Pointer) < sizeof(IPAddr)) {
+ return FALSE;
+ }
+ *(IPAddr UNALIGNED *)&RTOption[Pointer] = Address;
+ RTOption[IP_OPT_PTR] += sizeof(IPAddr);
+ }
+
+ return TRUE;
+
+}
+
+//* UpdateOptions - Update an options buffer.
+//
+// Called when we need to update an options buffer outgoing. We stamp the indicated
+// options with our local address.
+//
+// Input: Options - Pointer to options buffer to be updated.
+// Index - Pointer to information about which ones to update.
+// Address - Local address with which to update the options.
+//
+// Returns: Index of option causing the error, or MAX_OPT_SIZE if all goes well.
+//
+uchar
+UpdateOptions(uchar *Options, OptIndex *Index, IPAddr Address)
+{
+ uchar *LocalOption;
+ uchar LocalIndex;
+
+ // If we have both options and an index, update the options.
+ if (Options != (uchar *)NULL && Index != (OptIndex *)NULL) {
+
+ // If we have a source route to update, update it. If this fails return the index
+ // of the source route.
+ LocalIndex = Index->oi_srindex;
+ if (LocalIndex != MAX_OPT_SIZE)
+ if (!UpdateRouteOption(Options+LocalIndex, Address))
+ return LocalIndex;
+
+ // Do the same thing for any record route option.
+ LocalIndex = Index->oi_rrindex;
+ if (LocalIndex != MAX_OPT_SIZE)
+ if (!UpdateRouteOption(Options+LocalIndex, Address))
+ return LocalIndex;
+
+ // Now handle timestamp.
+ if ((LocalIndex = Index->oi_tsindex) != MAX_OPT_SIZE) {
+ uchar Flags, Length, Pointer;
+
+ LocalOption = Options + LocalIndex;
+ Pointer = LocalOption[IP_OPT_PTR] - 1;
+ Flags = LocalOption[IP_TS_OVFLAGS] & IP_TS_FLMASK;
+
+ // If we have room in the option, update it.
+ if (Pointer < (Length = LocalOption[IP_OPT_LENGTH])) {
+ ulong Now;
+ ulong UNALIGNED *TSPtr;
+
+ // Get the current time as milliseconds from midnight GMT, mod the number
+ // of milliseconds in 24 hours.
+ Now = ((TimeStamp + CTESystemUpTime()) | TSFlag) % (24*3600*1000);
+ Now = net_long(Now);
+ TSPtr = (ulong UNALIGNED *)&LocalOption[Pointer];
+
+ switch (Flags) {
+
+ // Just record the TS. If there is some room but not enough for an IP
+ // address we have an error.
+ case TS_REC_TS:
+ if ((Length - Pointer) < sizeof(IPAddr))
+ return LocalIndex; // Error - not enough room.
+ *TSPtr = Now;
+ LocalOption[IP_OPT_PTR] += sizeof(ulong);
+ break;
+
+ // Record only matching addresses.
+ case TS_REC_SPEC:
+ // If we're not the specified address, break out, else fall through
+ // to the record address case.
+ if (*(IPAddr UNALIGNED *)TSPtr != Address)
+ break;
+
+ // Record an address and timestamp pair. If there is some room
+ // but not enough for the address/timestamp pait, we have an error,
+ // so bail out.
+ case TS_REC_ADDR:
+ if ((Length - Pointer) < (sizeof(IPAddr) + sizeof(ulong)))
+ return LocalIndex; // Not enough room.
+ *(IPAddr UNALIGNED *)TSPtr = Address; // Store the address.
+ TSPtr++; // Update to where to put TS.
+ *TSPtr = Now; // Store TS
+ LocalOption[IP_OPT_PTR] += (sizeof(ulong) + sizeof(IPAddr));
+ break;
+ default: // Unknown flag type. Just ignore it.
+ break;
+ }
+ } else { // Have overflow.
+ // We have an overflow. If the overflow field isn't maxed, increment it. If
+ // it is maxed we have an error.
+ if ((LocalOption[IP_TS_OVFLAGS] & IP_TS_OVMASK) != IP_TS_MAXOV) // Not maxed.
+ LocalOption[IP_TS_OVFLAGS] += IP_TS_INC; // So increment it.
+ else
+ return LocalIndex; // Would have overflowed.
+ }
+ }
+ }
+ return MAX_OPT_SIZE;
+}
+
+
+typedef struct {
+ IPAddr bsl_addr;
+ Interface *bsl_if;
+ uint bsl_mtu;
+ ushort bsl_flags;
+} BCastSendList;
+
+//** SendIPBcast - Send a local BCast IP packet.
+//
+// This routine is called when we need to send a bcast packet. This may
+// involve sending on multiple interfaces. We figure out which interfaces
+// to send on, then loop through sending on them.
+//
+// Some care is needed to avoid sending the packet onto the same physical media
+// multiple times. What we do is loop through the NTE table, deciding in we
+// should send on the interface. As we go through we build up a list of
+// interfaces to send on. Then we loop through this list, sending on each
+// interface. This is a little cumbersome, but allows us to localize the
+// decision on where to send datagrams into one spot. If SendOnSource is FALSE
+// coming in we assume we've already sent on the specified source NTE and
+// initialize data structures accordingly. This feature is used in routing
+// datagrams.
+//
+// Entry: SrcNTE - NTE for source of send (unused if SendOnSource == TRUE).
+// Destination - Destination address
+// Packet - Prebuilt packet to broadcast.
+// IPH - Pointer to header buffer
+// Buffer - Buffer of data to be sent.
+// DataSize - Size of data to be sent.
+// Options - Pointer to options buffer.
+// OptionSize - Size in bytes of options.
+// SendOnSource - Indicator of whether or not this should be sent on the source net.
+// Index - Pointer to opt index array; may be NULL;
+//
+// Returns: Status of attempt to send.
+//
+IP_STATUS
+SendIPBCast(NetTableEntry *SrcNTE, IPAddr Destination, PNDIS_PACKET Packet,
+ IPHeader *IPH, PNDIS_BUFFER Buffer, uint DataSize, uchar *Options,
+ uint OptionSize, uchar SendOnSource, OptIndex *Index)
+{
+ BufferReference *BR; // Buffer reference to use for this
+ // buffer.
+ PacketContext *PContext = (PacketContext *)Packet->ProtocolReserved;
+ NetTableEntry *TempNTE;
+ uint i, j;
+ uint NeedFragment; // TRUE if we think we'll need to
+ // fragment.
+ int Sent = 0; // Count of how many we've sent.
+ IP_STATUS Status;
+ uchar *NewOptions; // Options we'll use on each send.
+ IPHeader *NewHeader;
+ PNDIS_BUFFER NewUserBuffer;
+ PNDIS_PACKET NewPacket;
+ BCastSendList *SendList;
+ uint NetsToSend;
+ IPAddr SrcAddr;
+ Interface *SrcIF;
+ IPHeader *Temp;
+ FORWARD_ACTION Action;
+
+
+ SendList = CTEAllocMem(sizeof(BCastSendList) * NumNTE);
+
+ if (SendList == NULL) {
+ return(IP_NO_RESOURCES);
+ }
+
+ CTEMemSet(SendList, 0, sizeof(BCastSendList) * NumNTE);
+
+ // If SendOnSource, initalize SrcAddr and SrcIF to be non-matching.
+ // Otherwise initialize them to the masked source address and source
+ // interface.
+ if (SendOnSource) {
+ SrcAddr = NULL_IP_ADDR;
+ SrcIF = NULL;
+ } else {
+ CTEAssert(SrcNTE != NULL);
+ SrcAddr = (SrcNTE->nte_addr & SrcNTE->nte_mask);
+ SrcIF = SrcNTE->nte_if;
+ }
+
+
+ NeedFragment = FALSE;
+ // Loop through the NTE table, making a list of interfaces and
+ // corresponding addresses to send on.
+ for (NetsToSend = 0, TempNTE = NetTableList; TempNTE != NULL;
+ TempNTE = TempNTE->nte_next) {
+ IPAddr TempAddr;
+
+ // Don't send through invalid or the loopback NTE.
+ if (!(TempNTE->nte_flags & NTE_VALID) || TempNTE == LoopNTE)
+ continue;
+
+ TempAddr = TempNTE->nte_addr & TempNTE->nte_mask;
+
+ // If he matches the source address or SrcIF, skip him.
+ if (IP_ADDR_EQUAL(TempAddr, SrcAddr) || TempNTE->nte_if == SrcIF)
+ continue;
+
+ // If the destination isn't a broadcast on this NTE, skip him.
+ if (!IS_BCAST_DEST(IsBCastOnNTE(Destination, TempNTE)))
+ continue;
+
+ // if this NTE is P2P then always add him to bcast list.
+ if ((TempNTE->nte_if)->if_flags & IF_FLAGS_P2P) {
+ j = NetsToSend ;
+ } else {
+ // Go through the list we've already build, looking for a match.
+ for (j = 0; j < NetsToSend; j++) {
+
+ // if P2P NTE then skip it - we want to send bcasts to all P2P interfaces in
+ // addition to 1 non P2P interface even if they are on the same subnet.
+ if ((SendList[j].bsl_if)->if_flags & IF_FLAGS_P2P)
+ continue ;
+
+ if (IP_ADDR_EQUAL(SendList[j].bsl_addr & TempNTE->nte_mask, TempAddr)
+ || SendList[j].bsl_if == TempNTE->nte_if) {
+
+ // He matches this send list element. Shrink the MSS if
+ // we need to, and then break out.
+ SendList[j].bsl_mtu = MIN(SendList[j].bsl_mtu, TempNTE->nte_mss);
+ if ((DataSize + OptionSize) > SendList[j].bsl_mtu)
+ NeedFragment = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (j == NetsToSend) {
+ // This is a new one. Fill him in, and bump NetsToSend.
+
+ SendList[j].bsl_addr = TempNTE->nte_addr;
+ SendList[j].bsl_if = TempNTE->nte_if;
+ SendList[j].bsl_mtu = TempNTE->nte_mss;
+ SendList[j].bsl_flags = TempNTE->nte_flags;
+ if ((DataSize + OptionSize) > SendList[j].bsl_mtu)
+ NeedFragment = TRUE;
+ NetsToSend++;
+ }
+
+ }
+
+ if (NetsToSend == 0) {
+ CTEFreeMem(SendList);
+ return IP_SUCCESS; // Nothing to send on.
+ }
+
+ // OK, we've got the list. If we've got more than one interface to send
+ // on or we need to fragment, get a BufferReference.
+ if (NetsToSend > 1 || NeedFragment) {
+ if ((BR = CTEAllocMem(sizeof(BufferReference))) ==
+ (BufferReference *)NULL) {
+ CTEFreeMem(SendList);
+ return IP_NO_RESOURCES;
+ }
+
+ BR->br_buffer = Buffer;
+ BR->br_refcount = 0;
+ CTEInitLock(&BR->br_lock);
+ PContext->pc_br = BR;
+ } else {
+ BR = NULL;
+ PContext->pc_br = NULL;
+ }
+
+ //
+ // We need to pass up the options and IP hdr in a contiguous buffer.
+ // Allocate the buffer once and re-use later.
+ //
+ if (ForwardFilterPtr != NULL) {
+ if (Options == NULL) {
+#if FWD_DBG
+ DbgPrint("Options==NULL\n");
+#endif
+ Temp = IPH;
+ } else {
+ Temp = CTEAllocMem(sizeof(IPHeader) + OptionSize);
+ if (Temp == NULL) {
+ CTEFreeMem(SendList);
+ return IP_NO_RESOURCES;
+ }
+
+ *Temp = *IPH;
+#if FWD_DBG
+ DbgPrint("Options!=NULL : alloced temp @ %lx\n", Temp);
+#endif
+
+ //
+ // done later...
+ // CTEMemCopy((uchar *)(Temp + 1), Options, OptionSize);
+ }
+ }
+
+ // Now, loop through the list. For each entry, send.
+
+ for (i = 0; i < NetsToSend; i++) {
+
+ // For all nets except the last one we're going to send on we need
+ // to make a copy of the header, packet, buffers, and any options.
+ // On the last net we'll use the user provided information.
+
+ if (i != (NetsToSend - 1)) {
+ if ((NewHeader = GetIPHeader(&NewPacket)) == (IPHeader *)NULL) {
+ IPSInfo.ipsi_outdiscards++;
+ continue; // Couldn't get a header, skip this
+ // send.
+ }
+
+ NewUserBuffer = IPCopyBuffer(Buffer, 0, DataSize);
+ if (NewUserBuffer == NULL) { // Couldn't get user buffer
+ // copied.
+ FreeIPPacket(NewPacket);
+ IPSInfo.ipsi_outdiscards++;
+ continue;
+ }
+
+ *(PacketContext *)NewPacket->ProtocolReserved = *PContext;
+ *NewHeader = *IPH;
+ (*(PacketContext*)NewPacket->ProtocolReserved).pc_common.pc_flags |= PACKET_FLAG_IPBUF;
+ (*(PacketContext*)NewPacket->ProtocolReserved).pc_common.pc_flags &= ~PACKET_FLAG_FW;
+ if (Options) {
+ // We have options, make a copy.
+ if ((NewOptions = CTEAllocMem(OptionSize)) == (uchar *)NULL) {
+ FreeIPBufferChain(NewUserBuffer);
+ FreeIPPacket(NewPacket);
+ IPSInfo.ipsi_outdiscards++;
+ continue;
+ }
+ CTEMemCopy(NewOptions, Options, OptionSize);
+ }
+ else {
+ NewOptions = NULL;
+ }
+ } else {
+ NewHeader = IPH;
+ NewPacket = Packet;
+ NewOptions = Options;
+ NewUserBuffer = Buffer;
+ }
+
+ UpdateOptions(NewOptions, Index, SendList[i].bsl_addr);
+
+ // See if we need to filter this packet. If we
+ // do, call the filter routine to see if it's
+ // OK to send it.
+ if (ForwardFilterPtr != NULL) {
+ //
+ // Copy over the options.
+ //
+ if (NewOptions) {
+ CTEMemCopy((uchar *)(Temp + 1), NewOptions, OptionSize);
+ }
+
+ Action = (*ForwardFilterPtr)(Temp,
+ NdisBufferVirtualAddress(NewUserBuffer),
+ NdisBufferLength(NewUserBuffer),
+ NULL, SendList[i].bsl_if->if_filtercontext);
+
+#if FWD_DBG
+ DbgPrint("ForwardFilterPtr: %lx, FORWARD is %lx\n", Action, FORWARD);
+#endif
+
+ if (Action != FORWARD) {
+ if (i != (NetsToSend - 1)) {
+ FreeIPBufferChain(NewUserBuffer);
+ if (NewOptions) {
+ CTEFreeMem(NewOptions);
+ }
+ }
+ continue;
+ }
+ }
+
+ if ((DataSize + OptionSize) > SendList[i].bsl_mtu) {// This is too big
+ // Don't need to update Sent when fragmenting, as IPFragment
+ // will update the br_refcount field itself. It will also free
+ // the option buffer.
+ Status = IPFragment(SendList[i].bsl_if, SendList[i].bsl_mtu,
+ Destination, NewPacket, NewHeader,NewUserBuffer, DataSize,
+ NewOptions, OptionSize, &Sent);
+
+ // IPFragment is done with the descriptor chain, so if this is
+ // a locally allocated chain free it now.
+ if (i != (NetsToSend - 1))
+ FreeIPBufferChain(NewUserBuffer);
+ }
+ else {
+ Status = SendIPPacket(SendList[i].bsl_if, Destination, NewPacket,
+ NewUserBuffer, NewHeader, NewOptions, OptionSize);
+ if (Status == IP_PENDING)
+ Sent++;
+ }
+ }
+
+ if (ForwardFilterPtr && Options) {
+ CTEFreeMem(Temp);
+ }
+
+ // Alright, we've sent everything we need to. We'll adjust the reference count
+ // by the number we've sent. IPFragment may also have put some references
+ // on it. If the reference count goes to 0, we're done and we'll free the
+ // BufferReference structure.
+
+ if (BR != NULL) {
+ if (!ReferenceBuffer(BR, Sent)) {
+ CTEFreeMem(SendList);
+ CTEFreeMem(BR); // Reference is 0, free the BR structure.
+ return IP_SUCCESS;
+ } else {
+ CTEFreeMem(SendList);
+ return IP_PENDING;
+ }
+ } else {
+ // Had only one I/F to send on. Just return the status.
+ CTEFreeMem(SendList);
+ return Status;
+ }
+
+}
+
+//** IPTransmit - Transmit a packet.
+//
+// This is the main transmit routine called by the upper layer. Conceptually,
+// we process any options, look up the route to the destination, fragment the
+// packet if needed, and send it. In reality, we use an RCE to cache the best
+// route, and we have special case code here for dealing with the common
+// case of no options, with everything fitting into one buffer.
+//
+// Entry: Context - Pointer to ProtInfo struc for protocol.
+// SendContext - User provided send context, passed back on send cmplt.
+// Protocol - Protocol field for packet.
+// Buffer - NDIS_BUFFER chain of data to be sent.
+// DataSize - Size in bytes of data to be sent.
+// OptInfo - Pointer to optinfo structure.
+// Dest - Destination to send to.
+// Source - Source address to use.
+// RCE - Pointer to an RCE structure that caches info. about path.
+//
+// Returns: Status of transmit command.
+//
+IP_STATUS
+IPTransmit(void *Context, void *SendContext, PNDIS_BUFFER Buffer, uint DataSize,
+ IPAddr Dest, IPAddr Source, IPOptInfo *OptInfo, RouteCacheEntry *RCE,
+ uchar Protocol)
+{
+ ProtInfo *PInfo = (ProtInfo *)Context;
+ PacketContext *pc;
+ Interface *DestIF; // Outgoing interface to use.
+ IPAddr FirstHop; // First hop address of
+ // destination.
+ uint MTU; // MTU of route.
+ NDIS_STATUS Status;
+ IPHeader *IPH;
+ PNDIS_PACKET Packet;
+ PNDIS_BUFFER HeaderBuffer;
+ CTELockHandle LockHandle;
+ uchar *Options;
+ uint OptionSize;
+ BufferReference *BR;
+ RouteTableEntry *RTE;
+ uchar DType;
+ IP_STATUS SendStatus;
+#ifdef _PNP_POWER
+ Interface *RoutedIF;
+#endif
+
+ IPSInfo.ipsi_outrequests++;
+
+ // Allocate a packet that we need for all cases, and fill
+ // in the common stuff. If everything goes well, we'll send it
+ // here. Otherwise we'll break out into special case code for
+ // broadcasts, fragments, etc.
+ if ((Packet = GetIPPacket()) != (PNDIS_PACKET)NULL) { // Got a packet.
+ pc = (PacketContext *)Packet->ProtocolReserved;
+ pc->pc_br = (BufferReference *)NULL;
+ pc->pc_pi = PInfo;
+ pc->pc_context = SendContext;
+#ifdef _PNP_POWER
+ CTEAssert(pc->pc_if == NULL);
+#endif
+
+ // Make sure that we have an RCE, that it's valid, etc.
+
+ if (RCE != NULL) {
+ // We have an RCE. Make sure it's valid.
+ CTEGetLock(&RCE->rce_lock, &LockHandle);
+ if (RCE->rce_flags == RCE_ALL_VALID) {
+
+ // The RTE is valid.
+ CTEInterlockedIncrementLong(&RCE->rce_usecnt);
+ RTE = RCE->rce_rte;
+ FirstHop = ADDR_FROM_RTE(RTE, Dest);
+ DestIF = IF_FROM_RTE(RTE);
+ MTU = MTU_FROM_RTE(RTE);
+
+ CTEFreeLock(&RCE->rce_lock, LockHandle);
+
+ // Check that we have no options, this isn't a broadcast, and
+ // that everything will fit into one link level MTU. If this
+ // is the case, we'll send it in a hurry.
+ if (OptInfo->ioi_options == (uchar *)NULL) {
+ if (RCE->rce_dtype != DEST_BCAST) {
+ if (DataSize <= MTU) {
+
+
+ NdisBufferLength(Buffer) += sizeof(IPHeader);
+ NdisChainBufferAtBack(Packet, Buffer);
+ IPH = (IPHeader *)NdisBufferVirtualAddress(Buffer);
+
+ IPH->iph_protocol = Protocol;
+ IPH->iph_xsum = 0;
+ IPH->iph_dest = Dest;
+ IPH->iph_src = Source;
+ IPH->iph_ttl = OptInfo->ioi_ttl;
+ IPH->iph_tos = OptInfo->ioi_tos;
+ IPH->iph_offset =
+ net_short(((OptInfo->ioi_flags & IP_FLAG_DF)
+ << 13));
+ IPH->iph_id =
+ (ushort)CTEInterlockedExchangeAdd(&IPID, 1);
+ IPH->iph_verlen = DEFAULT_VERLEN;
+ IPH->iph_length = net_short(DataSize+sizeof(IPHeader));
+ IPH->iph_xsum = ~xsum(IPH, sizeof(IPHeader));
+
+ // See if we need to filter this packet. If we
+ // do, call the filter routine to see if it's
+ // OK to send it.
+
+ if (ForwardFilterPtr == NULL) {
+ Status = (*(DestIF->if_xmit))(DestIF->if_lcontext,
+ Packet, FirstHop, RCE);
+
+ CTEInterlockedDecrementLong(&RCE->rce_usecnt);
+
+ if (Status != NDIS_STATUS_PENDING) {
+ FreeIPPacket(Packet);
+ return IP_SUCCESS; // BUGBUG - should map error
+ // code.
+ }
+ return IP_PENDING;
+
+ } else {
+ FORWARD_ACTION Action;
+
+ Action = (*ForwardFilterPtr)(IPH,
+ (uchar *)(IPH + 1),
+ NdisBufferLength(Buffer) -
+ sizeof(IPHeader),
+ NULL, DestIF->if_filtercontext);
+
+ if (Action == FORWARD) {
+ Status = (*(DestIF->if_xmit))(
+ DestIF->if_lcontext,
+ Packet, FirstHop, RCE);
+ } else {
+ Status = NDIS_STATUS_SUCCESS;
+ IPSInfo.ipsi_outdiscards++;
+ }
+
+ CTEInterlockedDecrementLong(&RCE->rce_usecnt);
+
+ if (Status != NDIS_STATUS_PENDING) {
+ FreeIPPacket(Packet);
+ return IP_SUCCESS; // BUGBUG - should map error
+ // code.
+ }
+ return IP_PENDING;
+ }
+ }
+ }
+ }
+ CTEInterlockedDecrementLong(&RCE->rce_usecnt);
+ DType = RCE->rce_dtype;
+ } else {
+ // We have an RCE, but there is no RTE for it. Call the
+ // routing code to fix this.
+ CTEFreeLock(&RCE->rce_lock, LockHandle);
+ if (!AttachRCEToRTE(RCE, PInfo->pi_protocol,
+ (uchar *)NdisBufferVirtualAddress(Buffer) + sizeof(IPHeader),
+ NdisBufferLength(Buffer))) {
+ IPSInfo.ipsi_outnoroutes++;
+ FreeIPPacket(Packet);
+ return IP_DEST_HOST_UNREACHABLE;
+ }
+
+ // See if the RCE is now valid.
+ CTEGetLock(&RCE->rce_lock, &LockHandle);
+ if (RCE->rce_flags == RCE_ALL_VALID) {
+
+ // The RCE is now valid, so use his info.
+ RTE = RCE->rce_rte;
+ FirstHop = ADDR_FROM_RTE(RTE, Dest);
+ DestIF = IF_FROM_RTE(RTE);
+ MTU = MTU_FROM_RTE(RTE);
+ DType = RCE->rce_dtype;
+ } else
+ FirstHop = NULL_IP_ADDR;
+ CTEFreeLock(&RCE->rce_lock, LockHandle);
+ }
+ } else {
+ // We had no RCE, so we'll have to look it up the hard way.
+ FirstHop = NULL_IP_ADDR;
+ }
+
+ // We bailed out of the fast path for some reason. Allocate a header
+ // buffer, and copy the data in the first buffer forward. Then figure
+ // out why we're off the fast path, and deal with it. If we don't have
+ // the next hop info, look it up now.
+
+ HeaderBuffer = GetIPHdrBuffer();
+ if (HeaderBuffer == NULL) {
+ FreeIPPacket(Packet);
+ IPSInfo.ipsi_outdiscards++;
+ return IP_NO_RESOURCES;
+ } else {
+ uchar *Temp1, *Temp2;
+
+ // Got a buffer, copy the upper layer data forward.
+
+ Temp1 = (uchar *)NdisBufferVirtualAddress(Buffer);
+ Temp2 = Temp1 + sizeof(IPHeader);
+ CTEMemCopy(Temp1, Temp2, NdisBufferLength(Buffer));
+ }
+
+ NdisChainBufferAtBack(Packet, HeaderBuffer);
+
+ IPH = (IPHeader *)NdisBufferVirtualAddress(HeaderBuffer);
+ IPH->iph_protocol = Protocol;
+ IPH->iph_xsum = 0;
+ IPH->iph_src = Source;
+ IPH->iph_ttl = OptInfo->ioi_ttl;
+ IPH->iph_tos = OptInfo->ioi_tos;
+ IPH->iph_offset = net_short(((OptInfo->ioi_flags & IP_FLAG_DF) << 13));
+ IPH->iph_id = (ushort)CTEInterlockedExchangeAdd(&IPID, 1);
+ pc = (PacketContext *)Packet->ProtocolReserved;
+ pc->pc_common.pc_flags |= PACKET_FLAG_IPHDR;
+
+ if (IP_ADDR_EQUAL(OptInfo->ioi_addr, NULL_IP_ADDR)) {
+ IPH->iph_dest = Dest;
+ }
+ else {
+ //
+ // We have a source route, so we need to redo the
+ // destination and first hop information.
+ //
+ Dest = OptInfo->ioi_addr;
+ IPH->iph_dest = Dest;
+
+ if (RCE != NULL) {
+ // We have an RCE. Make sure it's valid.
+ CTEGetLock(&RCE->rce_lock, &LockHandle);
+
+ if (RCE->rce_flags == RCE_ALL_VALID) {
+
+ // The RTE is valid.
+ RTE = RCE->rce_rte;
+ FirstHop = ADDR_FROM_RTE(RTE, Dest);
+ DestIF = IF_FROM_RTE(RTE);
+ MTU = MTU_FROM_RTE(RTE);
+ }
+ else {
+ FirstHop = NULL_IP_ADDR;
+ }
+
+ CTEFreeLock(&RCE->rce_lock, LockHandle);
+ }
+ }
+
+ if (IP_ADDR_EQUAL(FirstHop, NULL_IP_ADDR)) {
+ DestIF = LookupNextHopWithBuffer(Dest, Source, &FirstHop, &MTU,
+ PInfo->pi_protocol, (uchar *)NdisBufferVirtualAddress(Buffer),
+ NdisBufferLength(Buffer));
+#ifdef _PNP_POWER
+ pc->pc_if = DestIF;
+ RoutedIF = DestIF;
+#endif
+ if (DestIF == NULL) {
+ // Lookup failed. Return an error.
+ FreeIPPacket(Packet);
+ IPSInfo.ipsi_outnoroutes++;
+ return IP_DEST_HOST_UNREACHABLE;
+ }
+
+ DType = GetAddrType(Dest);
+#ifdef DEBUG
+ if (DType == DEST_INVALID)
+ DEBUGCHK;
+#endif
+ } else {
+#ifdef _PNP_POWER
+ RoutedIF = NULL;
+#endif
+ }
+
+ // See if we have any options. If we do, copy them now.
+ if (OptInfo->ioi_options != NULL) {
+ // If we have a SSRR, make sure that we're sending straight to the
+ // first hop.
+ if (OptInfo->ioi_flags & IP_FLAG_SSRR) {
+ if (!IP_ADDR_EQUAL(Dest, FirstHop)) {
+ FreeIPPacket(Packet);
+#ifdef _PNP_POWER
+ if (RoutedIF != NULL) {
+ DerefIF(RoutedIF);
+ }
+#endif
+ IPSInfo.ipsi_outnoroutes++;
+ return IP_DEST_HOST_UNREACHABLE;
+ }
+ }
+ Options = CTEAllocMem(OptionSize = OptInfo->ioi_optlength);
+ if (Options == (uchar *)NULL) {
+ FreeIPPacket(Packet);
+#ifdef _PNP_POWER
+ if (RoutedIF != NULL) {
+ DerefIF(RoutedIF);
+ }
+#endif
+ IPSInfo.ipsi_outdiscards++;
+ return IP_NO_RESOURCES;
+ }
+ CTEMemCopy(Options, OptInfo->ioi_options, OptionSize);
+ } else {
+ Options = (uchar *)NULL;
+ OptionSize = 0;
+ }
+
+ // The options have been taken care of. Now see if it's some sort
+ // of broadcast.
+ IPH->iph_verlen = IP_VERSION + ((OptionSize + sizeof(IPHeader)) >> 2);
+ IPH->iph_length = net_short(DataSize+OptionSize+sizeof(IPHeader));
+
+ // See if we need to filter this packet. If we
+ // do, call the filter routine to see if it's
+ // OK to send it.
+
+ if (ForwardFilterPtr != NULL) {
+ IPHeader *Temp;
+ FORWARD_ACTION Action;
+
+ if (Options == NULL) {
+ Temp = IPH;
+ } else {
+ Temp = CTEAllocMem(sizeof(IPHeader) + OptionSize);
+ if (Temp == NULL) {
+ FreeIPPacket(Packet);
+#ifdef _PNP_POWER
+ if (RoutedIF != NULL) {
+ DerefIF(RoutedIF);
+ }
+#endif
+ CTEFreeMem(Options);
+ IPSInfo.ipsi_outdiscards++;
+ return IP_NO_RESOURCES;
+ }
+
+ *Temp = *IPH;
+ CTEMemCopy((uchar *)(Temp + 1), Options, OptionSize);
+ }
+
+ Action = (*ForwardFilterPtr)(Temp,
+ NdisBufferVirtualAddress(Buffer),
+ NdisBufferLength(Buffer),
+ NULL, DestIF->if_filtercontext);
+
+ if (Options != NULL) {
+ CTEFreeMem(Temp);
+ }
+
+ if (Action != FORWARD) {
+ //
+ // If this is a bcast pkt, dont fail the send here since we might send this
+ // pkt over some other NTE; instead, let SendIPBCast deal with the Filtering
+ // for broadcast pkts.
+ //
+ // NOTE: We shd actually not call into ForwardFilterPtr here at all since we
+ // deal with it in BCast, but we do so in order to avoid a check above and hence
+ // take a double call hit in the bcast case.
+ //
+ if (DType != DEST_BCAST) {
+
+ if (Options)
+ CTEFreeMem(Options);
+ FreeIPPacket(Packet);
+
+#ifdef _PNP_POWER
+ if (RoutedIF != NULL) {
+ DerefIF(RoutedIF);
+ }
+#endif
+
+ IPSInfo.ipsi_outdiscards++;
+ return IP_DEST_HOST_UNREACHABLE;
+ }
+#if FWD_DBG
+ else {
+ DbgPrint("IPTransmit: ignoring return %lx\n", Action);
+ }
+#endif
+
+ }
+ }
+
+ // If this is a broadcast address, call our broadcast send handler
+ // to deal with this. The broadcast address handler will free the
+ // option buffer for us, if needed. Otherwise if it's a fragment, call
+ // the fragmentation handler.
+ if (DType == DEST_BCAST) {
+ if (IP_ADDR_EQUAL(Source, NULL_IP_ADDR)) {
+ SendStatus = SendDHCPPacket(Dest, Packet, Buffer, IPH);
+
+#ifdef _PNP_POWER
+ if (SendStatus != IP_PENDING && RoutedIF != NULL) {
+ DerefIF(RoutedIF);
+ }
+#endif
+
+ return SendStatus;
+ } else {
+ SendStatus= SendIPBCast(NULL, Dest, Packet, IPH, Buffer, DataSize,
+ Options, OptionSize, TRUE, NULL);
+
+#ifdef _PNP_POWER
+ if (SendStatus != IP_PENDING && RoutedIF != NULL) {
+ DerefIF(RoutedIF);
+ }
+#endif
+
+ return SendStatus;
+ }
+ }
+
+ // Not a broadcast. If it needs to be fragmented, call our
+ // fragmenter to do it. The fragmentation routine needs a
+ // BufferReference structure, so we'll need one of those first.
+ if ((DataSize + OptionSize) > MTU) {
+ BR = CTEAllocMem(sizeof(BufferReference));
+ if (BR == (BufferReference *)NULL) {
+ // Couldn't get a BufferReference
+ if (Options)
+ CTEFreeMem(Options);
+ FreeIPPacket(Packet);
+
+#ifdef _PNP_POWER
+ if (RoutedIF != NULL) {
+ DerefIF(RoutedIF);
+ }
+#endif
+
+ IPSInfo.ipsi_outdiscards++;
+ return IP_NO_RESOURCES;
+ }
+ BR->br_buffer = Buffer;
+ BR->br_refcount = 0;
+ CTEInitLock(&BR->br_lock);
+ pc->pc_br = BR;
+ SendStatus = IPFragment(DestIF, MTU, FirstHop, Packet, IPH, Buffer,
+ DataSize, Options, OptionSize, (int *)NULL);
+
+#ifdef _PNP_POWER
+ if (SendStatus != IP_PENDING && RoutedIF != NULL) {
+ DerefIF(RoutedIF);
+ }
+#endif
+
+ return SendStatus;
+ }
+
+ // If we've reached here, we aren't sending a broadcast and don't need to
+ // fragment anything. Presumably we got here because we have options.
+ // In any case, we're ready now.
+
+ SendStatus = SendIPPacket(DestIF, FirstHop, Packet, Buffer, IPH, Options,
+ OptionSize);
+
+#ifdef _PNP_POWER
+ if (SendStatus != IP_PENDING && RoutedIF != NULL) {
+ DerefIF(RoutedIF);
+ }
+#endif
+
+ return SendStatus;
+ }
+
+ // Couldn't get a buffer. Return 'no resources'
+ IPSInfo.ipsi_outdiscards++;
+ return IP_NO_RESOURCES;
+}
+
+
+
diff --git a/private/ntos/tdi/tcpip/ip/ipxmit.h b/private/ntos/tdi/tcpip/ip/ipxmit.h
new file mode 100644
index 000000000..65842967e
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/ipxmit.h
@@ -0,0 +1,34 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** IPXMIT.H - IP transmit definitions.
+//
+// This file contains all of the definitions for the transmit code visible
+// to modules outside IPXMIT.C
+extern IP_STATUS SendIPPacket(Interface *IF, IPAddr FirstHop,
+ PNDIS_PACKET Packet, PNDIS_BUFFER Buffer,
+ IPHeader *Header, uchar *Options,
+ uint OptionSize);
+extern IP_STATUS IPFragment(Interface *DestIF, uint MTU,
+ IPAddr FirstHop, PNDIS_PACKET Packet,
+ IPHeader *Header, PNDIS_BUFFER Buffer,
+ uint DataSize, uchar *Options,
+ uint OptionSize, int *SentCount);
+extern uchar UpdateOptions(uchar *Options, OptIndex *Index,
+ IPAddr Address);
+extern IP_STATUS SendIPBCast(NetTableEntry *SrcNTE, IPAddr Destination,
+ PNDIS_PACKET Packet, IPHeader *IPH,
+ PNDIS_BUFFER Buffer, uint DataSize,
+ uchar *Options, uint OptionSize,
+ uchar SendOnSource, OptIndex *Index);
+extern IP_STATUS IPTransmit(void *Context, void *SendContext,
+ PNDIS_BUFFER Buffer, uint DataSize,
+ IPAddr Dest, IPAddr Source,
+ IPOptInfo *OptInfo, RouteCacheEntry *RCE,
+ uchar Protocol);
+
+
+
diff --git a/private/ntos/tdi/tcpip/ip/mp/makefile b/private/ntos/tdi/tcpip/ip/mp/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/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/tcpip/ip/mp/sources b/private/ntos/tdi/tcpip/ip/mp/sources
new file mode 100644
index 000000000..301687c9a
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/mp/sources
@@ -0,0 +1,27 @@
+!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
+
+!include ..\sources.inc
diff --git a/private/ntos/tdi/tcpip/ip/ntip.c b/private/ntos/tdi/tcpip/ip/ntip.c
new file mode 100644
index 000000000..040f025e3
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/ntip.c
@@ -0,0 +1,3361 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ntip.c
+
+Abstract:
+
+ NT specific routines for loading and configuring the IP driver.
+
+Author:
+
+ Mike Massa (mikemas) Aug 13, 1993
+
+Revision History:
+
+ Who When What
+ -------- -------- ----------------------------------------------
+ mikemas 08-13-93 created
+
+Notes:
+
+--*/
+
+#define _CTYPE_DISABLE_MACROS
+
+#include <oscfg.h>
+#include <ndis.h>
+#include <cxport.h>
+#include <ip.h>
+#include "ipdef.h"
+#include "ipinit.h"
+#include <ntddip.h>
+#include <tdiinfo.h>
+#include <ipinfo.h>
+
+//
+// Debugging macros
+//
+#if DBG
+
+#define TCPTRACE(many_args) DbgPrint many_args
+
+#else // DBG
+
+#define TCPTRACE(many_args) DbgPrint many_args
+
+#endif // DBG
+
+
+//
+// definitions needed by inet_addr.
+//
+#define INADDR_NONE 0xffffffff
+#define INADDR_ANY 0
+#define htonl(x) net_long(x)
+
+//
+// Other local constants
+//
+#define WORK_BUFFER_SIZE 256
+
+//
+// Configuration defaults
+//
+#define DEFAULT_IGMP_LEVEL 2
+#define DEFAULT_IP_NETS 8
+
+
+//
+// Local types
+//
+typedef struct _PerNetConfigInfo {
+ uint UseZeroBroadcast;
+ uint Mtu;
+ uint NumberOfGateways;
+ uint MaxForwardPending; // max routing packets pending
+} PER_NET_CONFIG_INFO, *PPER_NET_CONFIG_INFO;
+
+
+//
+// Global variables.
+//
+PDRIVER_OBJECT IPDriverObject;
+PDEVICE_OBJECT IPDeviceObject;
+IPConfigInfo *IPConfiguration;
+uint ArpUseEtherSnap = FALSE;
+uint ArpAlwaysSourceRoute = FALSE;
+uint IPAlwaysSourceRoute = TRUE;
+uint ArpCacheLife = DEFAULT_ARP_CACHE_LIFE;
+PWCHAR TempAdapterName;
+
+#ifndef _PNP_POWER
+
+NameMapping *AdptNameTable;
+DriverRegMapping *DriverNameTable;
+uint NumRegDrivers = 0;
+uint NetConfigSize = DEFAULT_IP_NETS;
+
+#endif // _PNP_POWER
+
+// Used in the conversion of 100ns times to milliseconds.
+static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758};
+
+
+//
+// External variables
+//
+extern LIST_ENTRY PendingEchoList; // def needed for initialization
+extern LIST_ENTRY PendingIPSetNTEAddrList; // def needed for initialization
+extern IPSNMPInfo IPSInfo;
+EXTERNAL_LOCK(RouteTableLock)
+
+//
+// Macros
+//
+
+//++
+//
+// LARGE_INTEGER
+// CTEConvertMillisecondsTo100ns(
+// IN LARGE_INTEGER MsTime
+// );
+//
+// Routine Description:
+//
+// Converts time expressed in hundreds of nanoseconds to milliseconds.
+//
+// Arguments:
+//
+// MsTime - Time in milliseconds.
+//
+// Return Value:
+//
+// Time in hundreds of nanoseconds.
+//
+//--
+
+#define CTEConvertMillisecondsTo100ns(MsTime) \
+ RtlExtendedIntegerMultiply(MsTime, 10000)
+
+
+//++
+//
+// LARGE_INTEGER
+// CTEConvert100nsToMilliseconds(
+// IN LARGE_INTEGER HnsTime
+// );
+//
+// Routine Description:
+//
+// Converts time expressed in hundreds of nanoseconds to milliseconds.
+//
+// Arguments:
+//
+// HnsTime - Time in hundreds of nanoseconds.
+//
+// Return Value:
+//
+// Time in milliseconds.
+//
+//--
+
+#define SHIFT10000 13
+extern LARGE_INTEGER Magic10000;
+
+#define CTEConvert100nsToMilliseconds(HnsTime) \
+ RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000)
+
+
+//
+// External function prototypes
+//
+extern int
+IPInit(
+ void
+ );
+
+long
+IPSetInfo(
+ TDIObjectID *ID,
+ void *Buffer,
+ uint Size
+ );
+
+NTSTATUS
+IPDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+OpenRegKey(
+ PHANDLE HandlePtr,
+ PWCHAR KeyName
+ );
+
+NTSTATUS
+GetRegDWORDValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PULONG ValueData
+ );
+
+NTSTATUS
+SetRegDWORDValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PULONG ValueData
+ );
+
+NTSTATUS
+GetRegSZValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PUNICODE_STRING ValueData,
+ PULONG ValueType
+ );
+
+NTSTATUS
+GetRegMultiSZValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PUNICODE_STRING ValueData
+ );
+
+NTSTATUS
+InitRegDWORDParameter(
+ HANDLE RegKey,
+ PWCHAR ValueName,
+ ULONG *Value,
+ ULONG DefaultValue
+ );
+
+uint
+RTReadNext(
+ void *Context,
+ void *Buffer
+ );
+
+uint
+RTValidateContext(
+ void *Context,
+ uint *Valid
+ );
+
+//
+// Local funcion prototypes
+//
+NTSTATUS
+IPDriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ );
+
+NTSTATUS
+IPProcessConfiguration(
+ VOID
+ );
+
+NTSTATUS
+IPProcessAdapterSection(
+ WCHAR *DeviceName,
+ WCHAR *AdapterName
+ );
+
+#ifndef _PNP_POWER
+
+NTSTATUS
+IPProcessIPAddressList(
+ HANDLE AdapterKey,
+ WCHAR *DeviceName,
+ WCHAR *AdapterName,
+ WCHAR *IpAddressList,
+ WCHAR *SubnetMaskList,
+ NDIS_STRING *LowerInterfaceString,
+ uint LowerInterfaceType,
+ PPER_NET_CONFIG_INFO PerNetConfigInfo
+ );
+
+#else // _PNP_POWER
+
+uint
+GetGeneralIFConfig(
+ IFGeneralConfig *ConfigInfo,
+ NDIS_HANDLE Handle
+ );
+
+int
+IsLLInterfaceValueNull(
+ NDIS_HANDLE Handle
+ );
+
+IFAddrList *
+GetIFAddrList(
+ UINT *NumAddr,
+ NDIS_HANDLE Handle
+ );
+
+#endif // _PNP_POWER
+
+UINT
+OpenIFConfig(
+ PNDIS_STRING ConfigName,
+ NDIS_HANDLE *Handle
+ );
+
+VOID
+CloseIFConfig(
+ NDIS_HANDLE Handle
+ );
+
+IPConfigInfo *
+IPGetConfig(
+ void
+ );
+
+void
+IPFreeConfig(
+ IPConfigInfo *ConfigInfo
+ );
+
+ulong
+GetGMTDelta(
+ void
+ );
+
+ulong
+GetTime(
+ void
+ );
+
+BOOLEAN
+IPConvertStringToAddress(
+ IN PWCHAR AddressString,
+ OUT PULONG IpAddress
+ );
+
+uint
+UseEtherSNAP(
+ PNDIS_STRING Name
+ );
+
+void
+GetAlwaysSourceRoute(
+ uint *pArpAlwaysSourceRoute,
+ uint *pIPAlwaysSourceRoute
+ );
+
+uint
+GetArpCacheLife(
+ void
+ );
+
+ULONG
+RouteMatch(
+ IN WCHAR *RouteString,
+ IN IPAddr Address,
+ IN IPMask Mask,
+ OUT IPAddr *DestVal,
+ OUT IPMask *DestMask,
+ OUT IPAddr *GateVal,
+ OUT ULONG *Metric
+ );
+
+VOID
+SetPersistentRoutesForNTE(
+ IPAddr Address,
+ IPMask Mask,
+ ULONG IFIndex
+ );
+
+ULONG
+GetCurrentRouteTable(
+ IPRouteEntry **ppRouteTable
+ );
+
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(INIT, IPDriverEntry)
+#pragma alloc_text(INIT, IPProcessConfiguration)
+#pragma alloc_text(INIT, IPProcessAdapterSection)
+#pragma alloc_text(INIT, IPGetConfig)
+#pragma alloc_text(INIT, IPFreeConfig)
+#pragma alloc_text(INIT, GetGMTDelta)
+#pragma alloc_text(INIT, GetTime)
+
+#ifndef _PNP_POWER
+
+#pragma alloc_text(INIT, IPProcessIPAddressList)
+#pragma alloc_text(INIT, UseEtherSNAP)
+#pragma alloc_text(INIT, GetAlwaysSourceRoute)
+#pragma alloc_text(INIT, GetArpCacheLife)
+
+#else // _PNP_POWER
+
+#pragma alloc_text(PAGE, GetGeneralIFConfig)
+#pragma alloc_text(PAGE, IsLLInterfaceValueNull)
+#pragma alloc_text(PAGE, GetIFAddrList)
+#pragma alloc_text(PAGE, UseEtherSNAP)
+#pragma alloc_text(PAGE, GetAlwaysSourceRoute)
+#pragma alloc_text(PAGE, GetArpCacheLife)
+
+#endif // _PNP_POWER
+
+#pragma alloc_text(PAGE, OpenIFConfig)
+#pragma alloc_text(PAGE, CloseIFConfig)
+#pragma alloc_text(PAGE, RouteMatch)
+#pragma alloc_text(PAGE, SetPersistentRoutesForNTE)
+#pragma alloc_text(PAGE, IPConvertStringToAddress)
+
+#endif // ALLOC_PRAGMA
+
+//
+// Function definitions
+//
+NTSTATUS
+IPDriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+
+/*++
+
+Routine Description:
+
+ Initialization routine for the IP driver.
+
+Arguments:
+
+ DriverObject - Pointer to the IP driver object created by the system.
+ DeviceDescription - The name of IP's node in the registry.
+
+Return Value:
+
+ The final status from the initialization operation.
+
+--*/
+
+{
+ NTSTATUS status;
+ UNICODE_STRING deviceName;
+
+
+ IPDriverObject = DriverObject;
+
+ //
+ // Create the device object. IoCreateDevice zeroes the memory
+ // occupied by the object.
+ //
+
+ RtlInitUnicodeString(&deviceName, DD_IP_DEVICE_NAME);
+
+ status = IoCreateDevice(
+ DriverObject,
+ 0,
+ &deviceName,
+ FILE_DEVICE_NETWORK,
+ 0,
+ FALSE,
+ &IPDeviceObject
+ );
+
+ if (!NT_SUCCESS(status)) {
+ TCPTRACE((
+ "IP initialization failed: Unable to create device object %ws, status %lx.",
+ DD_IP_DEVICE_NAME,
+ status
+ ));
+
+ CTELogEvent(
+ DriverObject,
+ EVENT_TCPIP_CREATE_DEVICE_FAILED,
+ 1,
+ 1,
+ &deviceName.Buffer,
+ 0,
+ NULL
+ );
+
+ return(status);
+ }
+
+ //
+ // Intialize the device object.
+ //
+ IPDeviceObject->Flags |= DO_DIRECT_IO;
+
+ //
+ // Initialize the list of pending echo request IRPs.
+ //
+ InitializeListHead(&PendingEchoList);
+
+ //
+ // Initialize the list of pending SetAddr request IRPs.
+ //
+ InitializeListHead(&PendingIPSetNTEAddrList);
+
+ //
+ // Finally, read our configuration parameters from the registry.
+ //
+ status = IPProcessConfiguration();
+
+ if (status != STATUS_SUCCESS) {
+ IoDeleteDevice(IPDeviceObject);
+ }
+
+ return(status);
+}
+
+NTSTATUS
+IPProcessConfiguration(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Reads the IP configuration information from the registry and constructs
+ the configuration structure expected by the IP driver.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS or an error status if an operation fails.
+
+--*/
+
+{
+ NTSTATUS status;
+ HANDLE myRegKey = NULL;
+ UNICODE_STRING bindString;
+ WCHAR *aName,
+ *endOfString;
+ WCHAR IPParametersRegistryKey[] =
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters";
+ WCHAR IPLinkageRegistryKey[] =
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Linkage";
+ uint ArpTRSingleRoute;
+
+
+ bindString.Buffer = NULL;
+
+ IPConfiguration = CTEAllocMem(sizeof(IPConfigInfo));
+
+ if (IPConfiguration == NULL) {
+
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_RESOURCES_FOR_INIT,
+ 1,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ CTEMemSet(IPConfiguration, 0, sizeof(IPConfigInfo));
+
+#ifndef _PNP_POWER
+
+ IPConfiguration->ici_netinfo = CTEAllocMem(
+ sizeof(NetConfigInfo) * DEFAULT_IP_NETS
+ );
+
+ if (IPConfiguration->ici_netinfo == NULL) {
+
+ CTEFreeMem(IPConfiguration);
+
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_RESOURCES_FOR_INIT,
+ 2,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ CTEMemSet(
+ IPConfiguration->ici_netinfo,
+ 0,
+ sizeof(NetConfigInfo) * DEFAULT_IP_NETS
+ );
+
+#endif // _PNP_POWER
+
+ //
+ // Process the Ip\Parameters section of the registry
+ //
+ status = OpenRegKey(&myRegKey, IPParametersRegistryKey);
+
+ if (NT_SUCCESS(status)) {
+ //
+ // Expected configuration values. We use reasonable defaults if they
+ // aren't available for some reason.
+ //
+ status = GetRegDWORDValue(
+ myRegKey,
+ L"IpEnableRouter",
+ &(IPConfiguration->ici_gateway)
+ );
+
+ if (!NT_SUCCESS(status)) {
+ TCPTRACE((
+ "IP: Unable to read IpEnableRouter value from the registry.\n"
+ " Routing will be disabled.\n"
+ ));
+ IPConfiguration->ici_gateway = 0;
+ }
+
+ //
+ // Optional (hidden) values
+ //
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"ForwardBufferMemory",
+ &(IPConfiguration->ici_fwbufsize),
+ DEFAULT_FW_BUFSIZE
+ );
+
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"MaxForwardBufferMemory",
+ &(IPConfiguration->ici_maxfwbufsize),
+ DEFAULT_MAX_FW_BUFSIZE
+ );
+
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"ForwardBroadcasts",
+ &(IPConfiguration->ici_fwbcast),
+ FALSE
+ );
+
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"NumForwardPackets",
+ &(IPConfiguration->ici_fwpackets),
+ DEFAULT_FW_PACKETS
+ );
+
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"MaxNumForwardPackets",
+ &(IPConfiguration->ici_maxfwpackets),
+ DEFAULT_MAX_FW_PACKETS
+ );
+
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"IGMPLevel",
+ &(IPConfiguration->ici_igmplevel),
+ DEFAULT_IGMP_LEVEL
+ );
+
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"EnableDeadGWDetect",
+ &(IPConfiguration->ici_deadgwdetect),
+ TRUE
+ );
+
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"EnablePMTUDiscovery",
+ &(IPConfiguration->ici_pmtudiscovery),
+ TRUE
+ );
+
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"DefaultTTL",
+ &(IPConfiguration->ici_ttl),
+ DEFAULT_TTL
+ );
+
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"DefaultTOS",
+ &(IPConfiguration->ici_tos),
+ DEFAULT_TOS
+ );
+
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"ArpUseEtherSnap",
+ &ArpUseEtherSnap,
+ FALSE
+ );
+
+ //
+ // we check for the return status here because if this parameter was
+ // not defined, then we want the default behavior for both arp
+ // and ip broadcasts. For arp, the behavior is to not source route
+ // and source router alternately. For ip, it is to always source
+ // route. If the parameter is defined and is 0, then for arp the
+ // behavior does not change. For ip however, we do not source route
+ // at all. Ofcourse, when the parameter is set to a non-zero value,
+ // we always source route for both.
+ //
+ status = InitRegDWORDParameter(
+ myRegKey,
+ L"ArpAlwaysSourceRoute",
+ &ArpAlwaysSourceRoute,
+ FALSE
+ );
+
+ if (NT_SUCCESS(status))
+ {
+ IPAlwaysSourceRoute = ArpAlwaysSourceRoute;
+ }
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"ArpTRSingleRoute",
+ &ArpTRSingleRoute,
+ FALSE
+ );
+
+ if (ArpTRSingleRoute) {
+ TrRii = TR_RII_SINGLE;
+ } else {
+ TrRii = TR_RII_ALL;
+ }
+
+ (VOID)InitRegDWORDParameter(
+ myRegKey,
+ L"ArpCacheLife",
+ &ArpCacheLife,
+ DEFAULT_ARP_CACHE_LIFE
+ );
+
+ ZwClose(myRegKey);
+ myRegKey = NULL;
+ }
+ else {
+ //
+ // Use reasonable defaults.
+ //
+ IPConfiguration->ici_fwbcast = 0;
+ IPConfiguration->ici_gateway = 0;
+ IPConfiguration->ici_fwbufsize = DEFAULT_FW_BUFSIZE;
+ IPConfiguration->ici_fwpackets = DEFAULT_FW_PACKETS;
+ IPConfiguration->ici_maxfwbufsize = DEFAULT_MAX_FW_BUFSIZE;
+ IPConfiguration->ici_maxfwpackets = DEFAULT_MAX_FW_PACKETS;
+ IPConfiguration->ici_igmplevel = DEFAULT_IGMP_LEVEL;
+ IPConfiguration->ici_deadgwdetect = FALSE;
+ IPConfiguration->ici_pmtudiscovery = FALSE;
+ IPConfiguration->ici_ttl = DEFAULT_TTL;
+ IPConfiguration->ici_tos = DEFAULT_TOS;
+
+ TCPTRACE((
+ "IP: Unable to open Tcpip\\Parameters registry key. Using defaults.\n"
+ ));
+ }
+
+
+ //
+ // Process the Ip\Linkage section of the registry
+ //
+ status = OpenRegKey(&myRegKey, IPLinkageRegistryKey);
+
+ if (NT_SUCCESS(status)) {
+
+ bindString.Buffer = CTEAllocMem(WORK_BUFFER_SIZE * sizeof(WCHAR));
+
+ if (bindString.Buffer == NULL) {
+
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_RESOURCES_FOR_INIT,
+ 3,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto error_exit;
+ }
+
+ bindString.Buffer[0] = UNICODE_NULL;
+ bindString.Length = 0;
+ bindString.MaximumLength = WORK_BUFFER_SIZE * sizeof(WCHAR);
+
+ status = GetRegMultiSZValue(
+ myRegKey,
+ L"Bind",
+ &bindString
+ );
+
+ if (NT_SUCCESS(status)) {
+ aName = bindString.Buffer;
+
+ if (bindString.Length > 0) {
+ //
+ // bindString is a MULTI_SZ which is a series of strings separated
+ // by NULL's with a double NULL at the end.
+ //
+ while (*aName != UNICODE_NULL) {
+ PWCHAR deviceName;
+
+ deviceName = aName;
+
+ //
+ // Find the end of the current string in the MULTI_SZ.
+ //
+ while (*aName != UNICODE_NULL) {
+ aName++;
+ ASSERT(
+ aName <
+ (PWCHAR) ( ((PUCHAR)bindString.Buffer) +
+ bindString.MaximumLength
+ )
+ );
+ }
+
+ endOfString = aName;
+
+ //
+ // Backtrack to the first backslash.
+ //
+ while ((aName >= bindString.Buffer) && (*aName-- != L'\\'));
+
+ aName += 2;
+
+ status = IPProcessAdapterSection(
+ deviceName,
+ aName
+ );
+
+ aName = endOfString + 1;
+ }
+ }
+ }
+#ifndef _PNP_POWER
+ else {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_BINDINGS,
+ 1,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to open Tcpip\\Linkage\\Bind registry value.\n"
+ " Only the local loopback interface will be accessible.\n"
+ ));
+
+ }
+#endif _PNP_POWER
+ }
+ else {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_BINDINGS,
+ 2,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to open registry key Tcpip\\Linkage.\n"
+ " Only the local loopback interface will be accessible.\n"
+ ));
+ }
+
+
+#ifndef _PNP_POWER
+
+ //
+ // Allocate the Driver and Adapter name tables
+ //
+
+ DriverNameTable = (DriverRegMapping *) CTEAllocMem(
+ (IPConfiguration->ici_numnets + 1) *
+ sizeof(DriverRegMapping)
+ );
+
+ AdptNameTable = (NameMapping *) CTEAllocMem(
+ (IPConfiguration->ici_numnets + 1) *
+ sizeof(NameMapping)
+ );
+
+ if ((DriverNameTable != NULL) && (AdptNameTable != NULL)) {
+ CTEMemSet(
+ DriverNameTable,
+ 0,
+ sizeof(DriverRegMapping) * (IPConfiguration->ici_numnets + 1)
+ );
+ CTEMemSet(
+ AdptNameTable,
+ 0,
+ sizeof(NameMapping) * (IPConfiguration->ici_numnets + 1)
+ );
+
+#endif // _PNP_POWER
+
+ if (!IPInit()) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_IP_INIT_FAILED,
+ 1,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP initialization failed.\n"));
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ status = STATUS_SUCCESS;
+ }
+
+#ifndef _PNP_POWER
+
+ }
+ else {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_IP_INIT_FAILED,
+ 1,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP initialization failed.\n"));
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+#endif // _PNP_POWER
+
+error_exit:
+
+ if (bindString.Buffer != NULL) {
+ CTEFreeMem(bindString.Buffer);
+ }
+
+#ifndef _PNP_POWER
+
+ if (AdptNameTable != NULL) {
+ CTEFreeMem(AdptNameTable);
+ }
+
+ if (DriverNameTable != NULL) {
+ CTEFreeMem(DriverNameTable);
+ }
+
+#endif // _PNP_POWER
+
+ if (myRegKey != NULL) {
+ ZwClose(myRegKey);
+ }
+
+ if (IPConfiguration != NULL) {
+ IPFreeConfig(IPConfiguration);
+ }
+
+ return(status);
+}
+
+
+NTSTATUS
+IPProcessAdapterSection(
+ WCHAR *DeviceName,
+ WCHAR *AdapterName
+ )
+
+/*++
+
+Routine Description:
+
+ Reads all of the information needed under the Parameters\TCPIP section
+ of an adapter to which IP is bound.
+
+Arguments:
+
+ DeviceName - The name of the IP device.
+ AdapterName - The registry key for the adapter for this IP net.
+
+Return Value:
+
+ STATUS_SUCCESS or an error status if an operation fails.
+
+--*/
+
+{
+ HANDLE myRegKey;
+ UNICODE_STRING valueString;
+ NTSTATUS status;
+ ULONG valueType;
+ ulong invalidNetContext = 0xFFFF;
+ WCHAR TcpipParametersKey[] = L"\\Parameters\\TCPIP";
+ WCHAR ServicesRegistryKey[] =
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\";
+#ifndef _PNP_POWER
+ uint numberOfGateways = 0;
+ uint llInterfaceType;
+ WCHAR *ipAddressBuffer = NULL;
+ WCHAR *subnetMaskBuffer = NULL;
+ NDIS_STRING llInterfaceString;
+ PER_NET_CONFIG_INFO perNetConfigInfo;
+ NetConfigInfo *NetConfiguration;
+ PWCHAR temp;
+
+
+ RtlInitUnicodeString(&llInterfaceString, NULL);
+
+ NetConfiguration = IPConfiguration->ici_netinfo +
+ IPConfiguration->ici_numnets;
+
+#endif // _PNP_POWER
+
+ //
+ // Get the size of the AdapterName string the easy way.
+ //
+ RtlInitUnicodeString(&valueString, AdapterName);
+
+ valueString.MaximumLength += sizeof(ServicesRegistryKey) +
+ sizeof(TcpipParametersKey);
+
+ valueString.Buffer = CTEAllocMem(valueString.MaximumLength);
+
+ if (valueString.Buffer == NULL) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADAPTER_RESOURCES,
+ 4,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to allocate memory for reg key name\n"));
+
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ valueString.Length = 0;
+ valueString.Buffer[0] = UNICODE_NULL;
+
+ //
+ // Build the key name for the tcpip parameters section and open key.
+ //
+ status = RtlAppendUnicodeToString(&valueString, ServicesRegistryKey);
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_ADAPTER_REG_FAILURE,
+ 1,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to append services name to key string\n"));
+
+ goto exit2;
+ }
+
+ status = RtlAppendUnicodeToString(&valueString, AdapterName);
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_ADAPTER_REG_FAILURE,
+ 2,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to append adapter name to key string\n"));
+
+ goto exit2;
+ }
+
+ status = RtlAppendUnicodeToString(&valueString, TcpipParametersKey);
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_ADAPTER_REG_FAILURE,
+ 3,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to append parameters name to key string\n"));
+
+ goto exit2;
+ }
+
+ status = OpenRegKey(&myRegKey, valueString.Buffer);
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_ADAPTER_REG_FAILURE,
+ 4,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to open adapter registry key %ws\n",
+ valueString.Buffer
+ ));
+
+ goto exit2;
+ }
+
+ //
+ // Invalidate the interface context for DHCP.
+ // When the first net is successfully configured, we'll write in the
+ // proper values.
+ //
+ status = SetRegDWORDValue(
+ myRegKey,
+ L"IPInterfaceContext",
+ &(invalidNetContext)
+ );
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_DHCP_INIT_FAILED,
+ 1,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to Invalidate IPInterfaceContext value for adapter %ws.\n"
+ " DHCP may fail on this adapter.\n",
+ AdapterName
+ ));
+
+ goto exit1;
+ }
+
+#ifndef _PNP_POWER
+
+ //
+ // Process the gateway MultiSZ. The end is signified by a double NULL.
+ // This list currently only applies to the first IP address configured
+ // on this interface.
+ //
+ status = GetRegMultiSZValue(
+ myRegKey,
+ L"DefaultGateway",
+ &valueString
+ );
+
+ if (NT_SUCCESS(status)) {
+ PWCHAR addressString = valueString.Buffer;
+
+ while (*addressString != UNICODE_NULL) {
+ IPAddr addressValue;
+ BOOLEAN conversionStatus;
+
+ if (numberOfGateways >= MAX_DEFAULT_GWS) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_TOO_MANY_GATEWAYS,
+ 1,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ break;
+ }
+
+ conversionStatus = IPConvertStringToAddress(
+ addressString,
+ &addressValue
+ );
+
+ if (conversionStatus && (addressValue != 0xFFFFFFFF)) {
+ if (addressValue != INADDR_ANY) {
+ NetConfiguration->nci_gw[numberOfGateways++] = addressValue;
+ }
+ }
+ else {
+ PWCHAR stringList[2];
+
+ stringList[0] = addressString;
+ stringList[1] = AdapterName;
+
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_INVALID_DEFAULT_GATEWAY,
+ 1,
+ 2,
+ stringList,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Invalid default gateway address %ws specified for adapter %ws.\n"
+ " Remote networks may not be reachable as a result.\n",
+ addressString,
+ AdapterName
+ ));
+ }
+
+ //
+ // Walk over the entry we just processed.
+ //
+ while (*addressString++ != UNICODE_NULL);
+ }
+ }
+ else {
+ TCPTRACE((
+ "IP: Unable to read DefaultGateway value for adapter %ws.\n"
+ " Initialization will continue.\n",
+ AdapterName
+ ));
+ }
+
+ perNetConfigInfo.NumberOfGateways = numberOfGateways;
+
+ //
+ // Figure out which lower layer driver to bind.
+ //
+ status = GetRegSZValue(
+ myRegKey,
+ L"LLInterface",
+ &valueString,
+ &valueType
+ );
+
+ if (NT_SUCCESS(status) && (*(valueString.Buffer) != UNICODE_NULL)) {
+ llInterfaceType = NET_TYPE_WAN;
+
+ if (!CTEAllocateString(
+ &llInterfaceString,
+ CTELengthString(&valueString)
+ )
+ ) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADAPTER_RESOURCES,
+ 1,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP initialization failure: Unable to allocate memory "
+ "for LLInterface string for adapter %ws.\n",
+ AdapterName
+ ));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit1;
+ }
+
+ CTECopyString(
+ &llInterfaceString,
+ &valueString
+ );
+ }
+ else {
+ //
+ // If the key isn't present or is empty, we use ARP
+ //
+ llInterfaceType = NET_TYPE_LAN;
+ }
+
+ //
+ // Are we using zeros broadcasts?
+ //
+ status = GetRegDWORDValue(
+ myRegKey,
+ L"UseZeroBroadcast",
+ &(perNetConfigInfo.UseZeroBroadcast)
+ );
+
+ if (!NT_SUCCESS(status)) {
+ TCPTRACE((
+ "IP: Unable to read UseZeroBroadcast value for adapter %ws.\n"
+ " All-nets broadcasts will be addressed to 255.255.255.255.\n",
+ AdapterName
+ ));
+ perNetConfigInfo.UseZeroBroadcast = FALSE; // default to off
+ }
+
+ //
+ // Has anyone specified an MTU?
+ //
+ status = GetRegDWORDValue(
+ myRegKey,
+ L"MTU",
+ &(perNetConfigInfo.Mtu)
+ );
+
+ if (!NT_SUCCESS(status)) {
+ perNetConfigInfo.Mtu = 0xFFFFFFF; // The stack will pick one.
+ }
+
+ //
+ // Have we been configured for more routing packets?
+ //
+ status = GetRegDWORDValue(
+ myRegKey,
+ L"MaxForwardPending",
+ &(perNetConfigInfo.MaxForwardPending)
+ );
+
+ if (!NT_SUCCESS(status)) {
+ perNetConfigInfo.MaxForwardPending = DEFAULT_MAX_PENDING;
+ }
+
+ //
+ // Read the IP address and Subnet Mask lists
+ //
+ status = GetRegMultiSZValue(
+ myRegKey,
+ L"IpAddress",
+ &valueString
+ );
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADDRESS_LIST,
+ 1,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to read the IP address list for adapter %ws.\n"
+ " IP will not be operational on this adapter\n",
+ AdapterName
+ ));
+ goto exit1;
+ }
+
+ ipAddressBuffer = ExAllocatePool(NonPagedPool, valueString.Length);
+
+ if (ipAddressBuffer == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADAPTER_RESOURCES,
+ 2,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to allocate memory for IP address list\n"));
+ goto exit1;
+ }
+
+ RtlCopyMemory(ipAddressBuffer, valueString.Buffer, valueString.Length);
+
+ status = GetRegMultiSZValue(
+ myRegKey,
+ L"Subnetmask",
+ &valueString
+ );
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_MASK_LIST,
+ 1,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to read the subnet mask list for adapter %ws.\n"
+ " IP will not be operational on this adapter.\n",
+ AdapterName
+ ));
+ goto exit1;
+ }
+
+ subnetMaskBuffer = ExAllocatePool(NonPagedPool, valueString.Length);
+
+ if (subnetMaskBuffer == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADAPTER_RESOURCES,
+ 3,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to allocate memory for subnet mask list\n"));
+ goto exit1;
+ }
+
+ RtlCopyMemory(subnetMaskBuffer, valueString.Buffer, valueString.Length);
+
+ //
+ // Initialize each net in the list
+ //
+ status = IPProcessIPAddressList(
+ myRegKey,
+ DeviceName,
+ AdapterName,
+ ipAddressBuffer,
+ subnetMaskBuffer,
+ &llInterfaceString,
+ llInterfaceType,
+ &perNetConfigInfo
+ );
+
+ if (status == STATUS_SUCCESS) {
+ //
+ // We leave the registry key open. It will be closed when
+ // initialization is completed.
+ //
+ goto exit2;
+ }
+
+#endif // ndef _PNP_POWER
+
+
+exit1:
+
+ ZwClose(myRegKey);
+
+exit2:
+
+ if (valueString.Buffer != NULL) {
+ CTEFreeMem(valueString.Buffer);
+ }
+
+#ifndef _PNP_POWER
+
+ if (ipAddressBuffer != NULL) {
+ ExFreePool(ipAddressBuffer);
+ }
+
+ if (subnetMaskBuffer != NULL) {
+ ExFreePool(subnetMaskBuffer);
+ }
+
+ if (llInterfaceString.Buffer != NULL) {
+ CTEFreeString(&llInterfaceString);
+ }
+
+#endif // _PNP_POWER
+
+ return(status);
+}
+
+#ifndef _PNP_POWER
+
+NTSTATUS
+IPProcessIPAddressList(
+ HANDLE AdapterKey,
+ WCHAR *DeviceName,
+ WCHAR *AdapterName,
+ WCHAR *IpAddressList,
+ WCHAR *SubnetMaskList,
+ NDIS_STRING *LowerInterfaceString,
+ uint LowerInterfaceType,
+ PPER_NET_CONFIG_INFO PerNetConfigInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Processes the IP address string for an adapter and creates entries
+ in the IP configuration structure for each interface.
+
+Arguments:
+
+ AdapterKey - The registry key for the adapter for this IP net.
+ DeviceName - The name of the IP device.
+ AdapterName - The name of the adapter being configured.
+ IpAddressList - The REG_MULTI_SZ list of IP address strings for
+ this adapter.
+ SubnetMaskList - The REG_MULTI_SZ list of subnet masks to match the
+ the addresses in IpAddressList.
+ LowerInterfaceString - The name of the link layer interface driver
+ supporting this adapter.
+ LowerInterfaceType - The type of link layer interface (LAN, WAN, etc).
+ PerNetConfigInfo - Miscellaneous information that applies to all
+ network interfaces on an adapter.
+
+Return Value:
+
+ An error status if an error occurs which prevents configuration
+ from continuing, else STATUS_SUCCESS. Events will be logged for
+ non-fatal errors.
+
+--*/
+
+{
+ IPAddr addressValue;
+ BOOLEAN firstTime = TRUE;
+ BOOLEAN configuredOne = FALSE;
+ NetConfigInfo *NetConfiguration;
+ UNICODE_STRING adapterString;
+ UNICODE_STRING configString;
+ PWCHAR configName = L"\\Parameters\\Tcpip";
+
+
+ while (*IpAddressList != UNICODE_NULL) {
+ BOOLEAN conversionStatus;
+
+
+ if (IPConfiguration->ici_numnets >= ((int) (NetConfigSize - 1))) {
+ NetConfigInfo *NewInfo;
+
+ NewInfo = CTEAllocMem(
+ (NetConfigSize + DEFAULT_IP_NETS) *
+ sizeof(NetConfigInfo)
+ );
+
+ if (NewInfo == NULL) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_TOO_MANY_NETS,
+ 1,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: bound to too many nets. Further bindings, starting with\n"
+ " network %ws on adapter %ws cannot be made\n",
+ IpAddressList,
+ AdapterName
+ ));
+
+ break;
+ }
+
+ CTEMemCopy(
+ NewInfo,
+ IPConfiguration->ici_netinfo,
+ NetConfigSize * sizeof(NetConfigInfo)
+ );
+
+ CTEMemSet(
+ (NewInfo + NetConfigSize),
+ 0,
+ DEFAULT_IP_NETS
+ );
+
+ CTEFreeMem(IPConfiguration->ici_netinfo);
+ IPConfiguration->ici_netinfo = NewInfo;
+ NetConfigSize += DEFAULT_IP_NETS;
+ }
+
+ NetConfiguration = IPConfiguration->ici_netinfo +
+ IPConfiguration->ici_numnets;
+
+ if (*SubnetMaskList == UNICODE_NULL) {
+ PWCHAR stringList[2];
+
+ stringList[0] = IpAddressList;
+ stringList[1] = AdapterName;
+
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_MASK,
+ 1,
+ 2,
+ stringList,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: No subnet specified for IP address %ws and all\n"
+ " subsequent IP addresses on adapter %ws. These\n"
+ " interfaces will not be initialized.\n",
+ IpAddressList,
+ AdapterName
+ ));
+
+ break;
+ }
+
+ conversionStatus = IPConvertStringToAddress(
+ IpAddressList,
+ &addressValue
+ );
+
+ if (!conversionStatus || (addressValue == 0xFFFFFFFF)) {
+ PWCHAR stringList[2];
+
+ stringList[0] = IpAddressList;
+ stringList[1] = AdapterName;
+
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_INVALID_ADDRESS,
+ 1,
+ 2,
+ stringList,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Invalid IP address %ws specified for adapter %ws.\n"
+ " This interface will not be initialized.\n",
+ IpAddressList,
+ AdapterName
+ ));
+ firstTime = FALSE;
+ goto next_entry;
+ }
+
+ NetConfiguration->nci_addr = addressValue;
+
+ conversionStatus = IPConvertStringToAddress(
+ SubnetMaskList,
+ &addressValue
+ );
+
+ if (!conversionStatus || (addressValue == 0xFFFFFFFF)) {
+ PWCHAR stringList[3];
+
+ stringList[0] = SubnetMaskList;
+ stringList[1] = IpAddressList;
+ stringList[2] = AdapterName;
+
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_INVALID_MASK,
+ 1,
+ 3,
+ stringList,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Invalid subnet Mask %ws specified for IP address %ws "
+ "on adapter %ws\n"
+ " This interface will not be initialized\n",
+ SubnetMaskList,
+ IpAddressList,
+ AdapterName
+ ));
+ firstTime = FALSE;
+ goto next_entry;
+ }
+
+ NetConfiguration->nci_mask = addressValue;
+
+ NetConfiguration->nci_mtu = PerNetConfigInfo->Mtu;
+ NetConfiguration->nci_maxpending = PerNetConfigInfo->MaxForwardPending;
+ NetConfiguration->nci_zerobcast = PerNetConfigInfo->UseZeroBroadcast;
+ NetConfiguration->nci_type = LowerInterfaceType;
+
+ NetConfiguration->nci_numgws = PerNetConfigInfo->NumberOfGateways;
+ PerNetConfigInfo->NumberOfGateways = 0;
+ // this only applies to the first interface.
+
+ NetConfiguration->nci_type = LowerInterfaceType;
+
+ RtlInitUnicodeString(
+ &(NetConfiguration->nci_name),
+ DeviceName
+ );
+
+ RtlInitUnicodeString(&configString, configName);
+ RtlInitUnicodeString(&adapterString, AdapterName);
+
+ if (!CTEAllocateString(
+ &(NetConfiguration->nci_configname),
+ (adapterString.Length + configString.Length)
+ )
+ )
+ {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADAPTER_RESOURCES,
+ 4,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to allocate ConfigName string for interface\n"
+ " %ws on adapter %ws. This interface and all subsequent\n"
+ " interfaces on this adapter will be unavailable.\n",
+ IpAddressList,
+ AdapterName
+ ));
+ break;
+ }
+
+ CTECopyString(
+ &(NetConfiguration->nci_configname),
+ &adapterString
+ );
+
+ RtlAppendUnicodeStringToString(
+ &(NetConfiguration->nci_configname),
+ &configString
+ );
+
+ if (LowerInterfaceType != NET_TYPE_LAN) {
+
+ if (!CTEAllocateString(
+ &(NetConfiguration->nci_driver),
+ CTELengthString(LowerInterfaceString)
+ )
+ ) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADAPTER_RESOURCES,
+ 4,
+ 1,
+ &AdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to allocate LLInterface string for interface\n"
+ " %ws on adapter %ws. This interface and all subsequent\n"
+ " interfaces on this adapter will be unavailable.\n",
+ IpAddressList,
+ AdapterName
+ ));
+ break;
+ }
+
+ CTECopyString(
+ &(NetConfiguration->nci_driver),
+ LowerInterfaceString
+ );
+ }
+ else {
+ RtlInitUnicodeString(&(NetConfiguration->nci_driver), NULL);
+ }
+
+ if (firstTime) {
+ firstTime = FALSE;
+ NetConfiguration->nci_reghandle = AdapterKey;
+ }
+ else {
+ NetConfiguration->nci_reghandle = NULL;
+ }
+
+ IPConfiguration->ici_numnets++;
+ configuredOne = TRUE;
+
+next_entry:
+
+ while(*IpAddressList++ != UNICODE_NULL);
+ while(*SubnetMaskList++ != UNICODE_NULL);
+ }
+
+ if (configuredOne == FALSE) {
+ ZwClose(AdapterKey);
+ }
+
+ return(STATUS_SUCCESS);
+}
+
+
+#else // ndef _PNP_POWER
+
+
+uint
+GetGeneralIFConfig(
+ IFGeneralConfig *ConfigInfo,
+ NDIS_HANDLE Handle
+ )
+
+/*++
+
+ Routine Description:
+
+ A routine to get the general per-interface config info, such as MTU,
+ type of broadcast, etc. The caller gives us a structure to be filled in
+ and a handle, and we fill in the structure if we can.
+
+ Arguments:
+ ConfigInfo - Structure to be filled in.
+ Handle - Config handle from OpenIFConfig().
+
+ Return Value:
+ TRUE if we got all the required info, FALSE otherwise.
+
+--*/
+
+{
+ UNICODE_STRING valueString;
+ NTSTATUS status;
+ UINT numberOfGateways = 0;
+ UCHAR TempBuffer[WORK_BUFFER_SIZE];
+ ULONG ulAddGateway,ulTemp;
+
+ PAGED_CODE();
+
+ //
+ // Process the gateway MultiSZ. The end is signified by a double NULL.
+ // This list currently only applies to the first IP address configured
+ // on this interface.
+ //
+
+ ConfigInfo->igc_numgws = 0;
+
+ ulAddGateway = TRUE;
+
+ CTEMemSet(ConfigInfo->igc_gw, 0, sizeof(IPAddr) * MAX_DEFAULT_GWS);
+
+ valueString.Length = 0;
+ valueString.MaximumLength = WORK_BUFFER_SIZE;
+ valueString.Buffer = (PWCHAR)TempBuffer;
+
+ ulTemp = 0;
+
+ status = GetRegDWORDValue(Handle,
+ L"DontAddDefaultGateway",
+ &ulTemp);
+
+ if(NT_SUCCESS(status))
+ {
+ if(ulTemp == 1)
+ {
+ ulAddGateway = FALSE;
+ }
+ }
+
+ if(ulAddGateway)
+ {
+ status = GetRegMultiSZValue(
+ Handle,
+ L"DefaultGateway",
+ &valueString
+ );
+
+ if (NT_SUCCESS(status)) {
+ PWCHAR addressString = valueString.Buffer;
+
+ while (*addressString != UNICODE_NULL) {
+ IPAddr addressValue;
+ BOOLEAN conversionStatus;
+
+ if (numberOfGateways >= MAX_DEFAULT_GWS) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_TOO_MANY_GATEWAYS,
+ 1,
+ 1,
+ &TempAdapterName,
+ 0,
+ NULL
+ );
+
+ break;
+ }
+
+ conversionStatus = IPConvertStringToAddress(
+ addressString,
+ &addressValue
+ );
+
+ if (conversionStatus && (addressValue != 0xFFFFFFFF)) {
+ if (addressValue != INADDR_ANY) {
+ ConfigInfo->igc_gw[numberOfGateways++] = addressValue;
+ }
+ }
+ else {
+ PWCHAR stringList[2];
+
+ stringList[0] = addressString;
+ stringList[1] = TempAdapterName;
+
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_INVALID_DEFAULT_GATEWAY,
+ 1,
+ 2,
+ stringList,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Invalid default gateway address %ws specified for adapter %ws.\n"
+ " Remote networks may not be reachable as a result.\n",
+ addressString,
+ TempAdapterName
+ ));
+ }
+
+ //
+ // Walk over the entry we just processed.
+ //
+ while (*addressString++ != UNICODE_NULL);
+ }
+ }
+ else {
+ TCPTRACE((
+ "IP: Unable to read DefaultGateway value for adapter %ws.\n"
+ " Initialization will continue.\n",
+ TempAdapterName
+ ));
+ }
+
+ ConfigInfo->igc_numgws = numberOfGateways;
+ }
+
+ //
+ // Are we using zeros broadcasts?
+ //
+ status = GetRegDWORDValue(
+ Handle,
+ L"UseZeroBroadcast",
+ &(ConfigInfo->igc_zerobcast)
+ );
+
+ if (!NT_SUCCESS(status)) {
+ TCPTRACE((
+ "IP: Unable to read UseZeroBroadcast value for adapter %ws.\n"
+ " All-nets broadcasts will be addressed to 255.255.255.255.\n",
+ TempAdapterName
+ ));
+ ConfigInfo->igc_zerobcast = FALSE; // default to off
+ }
+
+ //
+ // Has anyone specified an MTU?
+ //
+ status = GetRegDWORDValue(
+ Handle,
+ L"MTU",
+ &(ConfigInfo->igc_mtu)
+ );
+
+ if (!NT_SUCCESS(status)) {
+ ConfigInfo->igc_mtu = 0xFFFFFFF; // The stack will pick one.
+ }
+
+ //
+ // Have we been configured for more routing packets?
+ //
+ status = GetRegDWORDValue(
+ Handle,
+ L"MaxForwardPending",
+ &(ConfigInfo->igc_maxpending)
+ );
+
+ if (!NT_SUCCESS(status)) {
+ ConfigInfo->igc_maxpending = DEFAULT_MAX_PENDING;
+ }
+ //
+ // Has Router Discovery been configured?
+ //
+
+ status = GetRegDWORDValue(
+ Handle,
+ L"PerformRouterDiscovery",
+ &(ConfigInfo->igc_rtrdiscovery)
+ );
+
+ if (!NT_SUCCESS(status)) {
+ ConfigInfo->igc_rtrdiscovery = 1;
+ }
+
+ //
+ // [BUGBUG] Only for 4.0 sp2 Turn off ICMP rtr discovery.
+ //
+ ConfigInfo->igc_rtrdiscovery = 0;
+
+ //
+ // Has Router Discovery Address been configured?
+ //
+
+ status = GetRegDWORDValue(
+ Handle,
+ L"SolicitationAddressBCast",
+ &ulTemp
+ );
+
+ if (!NT_SUCCESS(status)) {
+ ConfigInfo->igc_rtrdiscaddr = ALL_ROUTER_MCAST;
+ } else {
+ if (ulTemp == 1) {
+ ConfigInfo->igc_rtrdiscaddr = 0xffffffff;
+ } else {
+ ConfigInfo->igc_rtrdiscaddr = ALL_ROUTER_MCAST;
+ }
+ }
+
+ return TRUE;
+}
+
+
+int
+IsLLInterfaceValueNull(
+ NDIS_HANDLE Handle
+ )
+/*++
+
+ Routine Description:
+
+ Called to see if the LLInterface value in the registry key for which the
+ handle is provided, is NULL or not.
+
+ Arguments:
+ Handle - Handle to use for reading config.
+
+ Return Value:
+
+ FALSE if value is not null
+ TRUE if it is null
+
+--*/
+{
+ UNICODE_STRING valueString ;
+ ULONG valueType ;
+ NTSTATUS status ;
+
+
+ PAGED_CODE();
+
+ valueString.MaximumLength = 200 ;
+ valueString.Buffer = CTEAllocMem(valueString.MaximumLength) ;
+
+ status = GetRegSZValue(
+ Handle,
+ L"LLInterface",
+ &valueString,
+ &valueType
+ );
+
+ if (NT_SUCCESS(status) && (*(valueString.Buffer) != UNICODE_NULL)) {
+ CTEFreeMem (valueString.Buffer) ;
+ return FALSE ;
+ } else {
+ CTEFreeMem (valueString.Buffer) ;
+ return TRUE ;
+ }
+}
+
+
+IFAddrList *
+GetIFAddrList(
+ UINT *NumAddr,
+ NDIS_HANDLE Handle
+ )
+/*++
+
+ Routine Description:
+
+ Called to read the list of IF addresses and masks for an interface.
+ We'll get the address pointer first, then walk the list counting
+ to find out how many addresses we have. Then we allocate memory for the
+ list, and walk down the list converting them. After that we'll get
+ the mask list and convert it.
+
+ Arguments:
+ NumAddr - Where to return number of address we have.
+ Handle - Handle to use for reading config.
+
+ Return Value:
+
+ Pointer to IF address list if we get one, or NULL otherwise.
+
+--*/
+{
+ UNICODE_STRING ValueString;
+ NTSTATUS Status;
+ UINT AddressCount = 0;
+ UINT GoodAddresses = 0;
+ PWCHAR CurrentAddress;
+ PWCHAR CurrentMask;
+ PWCHAR AddressString;
+ PWCHAR MaskString;
+ IFAddrList *AddressList;
+ UINT i;
+ BOOLEAN ConversionStatus;
+ IPAddr AddressValue;
+ IPAddr MaskValue;
+ UCHAR TempBuffer[WORK_BUFFER_SIZE];
+
+
+ PAGED_CODE();
+
+ ValueString.Length = 0;
+ ValueString.MaximumLength = WORK_BUFFER_SIZE;
+ ValueString.Buffer = (PWCHAR)CTEAllocMem(WORK_BUFFER_SIZE);
+
+ if (ValueString.Buffer == NULL) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADAPTER_RESOURCES,
+ 2,
+ 1,
+ &TempAdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to allocate memory for IP address list WB\n"));
+ return NULL;
+ }
+
+ // First, try to read the IpAddress string.
+
+ Status = GetRegMultiSZValue(
+ Handle,
+ L"IpAddress",
+ &ValueString
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADDRESS_LIST,
+ 1,
+ 1,
+ &TempAdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to read the IP address list for adapter %ws.\n"
+ " IP will not be operational on this adapter\n",
+ TempAdapterName
+ ));
+ ExFreePool(ValueString.Buffer);
+ return NULL;
+ }
+
+ AddressString = ExAllocatePool(NonPagedPool, ValueString.MaximumLength);
+
+ if (AddressString == NULL) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADAPTER_RESOURCES,
+ 2,
+ 1,
+ &TempAdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to allocate memory for IP address list\n"));
+ ExFreePool(ValueString.Buffer);
+ return NULL;
+ }
+
+ RtlCopyMemory(AddressString, ValueString.Buffer, ValueString.MaximumLength);
+
+ Status = GetRegMultiSZValue(
+ Handle,
+ L"Subnetmask",
+ &ValueString
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_MASK_LIST,
+ 1,
+ 1,
+ &TempAdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to read the subnet mask list for adapter %ws.\n"
+ " IP will not be operational on this adapter.\n",
+ TempAdapterName
+ ));
+
+ ExFreePool(AddressString);
+ ExFreePool(ValueString.Buffer);
+ return NULL;
+ }
+
+ MaskString = ExAllocatePool(NonPagedPool, ValueString.MaximumLength);
+
+ if (MaskString == NULL) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADAPTER_RESOURCES,
+ 3,
+ 1,
+ &TempAdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to allocate memory for subnet mask list\n"));
+ ExFreePool(AddressString);
+ ExFreePool(ValueString.Buffer);
+ return NULL;
+ }
+
+ RtlCopyMemory(MaskString, ValueString.Buffer, ValueString.MaximumLength);
+
+
+ CurrentAddress = AddressString;
+ CurrentMask = MaskString;
+
+ while (*CurrentAddress != UNICODE_NULL &&
+ *CurrentMask != UNICODE_NULL) {
+
+ // We have a potential IP address.
+
+ AddressCount++;
+
+ // Skip this one.
+ while (*CurrentAddress++ != UNICODE_NULL);
+ while (*CurrentMask++ != UNICODE_NULL);
+ }
+
+ if (AddressCount == 0) {
+
+ ExFreePool(AddressString);
+ ExFreePool(MaskString);
+ ExFreePool(ValueString.Buffer);
+ return NULL;
+ }
+
+ // Allocate memory.
+ AddressList = CTEAllocMem(sizeof(IFAddrList) * AddressCount);
+
+ if (AddressList == NULL) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADAPTER_RESOURCES,
+ 2,
+ 1,
+ &TempAdapterName,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to allocate memory for IP address list\n"));
+ ExFreePool(AddressString);
+ ExFreePool(MaskString);
+ ExFreePool(ValueString.Buffer);
+ return NULL;
+ }
+
+ // Walk the list again, converting each address.
+
+ CurrentAddress = AddressString;
+ CurrentMask = MaskString;
+
+ for (i = 0; i < AddressCount; i++) {
+ ConversionStatus = IPConvertStringToAddress(
+ CurrentAddress,
+ &AddressValue
+ );
+
+ if (!ConversionStatus || (AddressValue == 0xFFFFFFFF)) {
+ PWCHAR stringList[2];
+
+ stringList[0] = CurrentAddress;
+ stringList[1] = TempAdapterName;
+
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_INVALID_ADDRESS,
+ 1,
+ 2,
+ stringList,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Invalid IP address %ws specified for adapter %ws.\n"
+ " This interface will not be initialized.\n",
+ CurrentAddress,
+ TempAdapterName
+ ));
+
+ goto nextone;
+
+ }
+
+ // Now do the current mask.
+
+ ConversionStatus = IPConvertStringToAddress(
+ CurrentMask,
+ &MaskValue
+ );
+
+ if (!ConversionStatus) {
+ PWCHAR stringList[3];
+
+ stringList[0] = CurrentMask;
+ stringList[1] = CurrentAddress;
+ stringList[2] = TempAdapterName;
+
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_INVALID_MASK,
+ 1,
+ 3,
+ stringList,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Invalid subnet Mask %ws specified for IP address %ws "
+ "on adapter %ws\n"
+ " This interface will not be initialized\n",
+ CurrentMask,
+ CurrentAddress,
+ TempAdapterName
+ ));
+ } else {
+ AddressList[GoodAddresses].ial_addr = AddressValue;
+ AddressList[GoodAddresses].ial_mask = MaskValue;
+ GoodAddresses++;
+ }
+
+nextone:
+ while(*CurrentAddress++ != UNICODE_NULL);
+ while(*CurrentMask++ != UNICODE_NULL);
+
+ }
+
+ ExFreePool(AddressString);
+ ExFreePool(MaskString);
+ ExFreePool(ValueString.Buffer);
+
+ *NumAddr = GoodAddresses;
+
+ if (GoodAddresses == 0) {
+ ExFreePool(AddressList);
+ AddressList = NULL;
+ }
+
+ return AddressList;
+}
+
+#endif // PNP_POWER
+
+
+UINT
+OpenIFConfig(
+ PNDIS_STRING ConfigName,
+ NDIS_HANDLE *Handle
+ )
+
+/*++
+
+ Routine Description:
+
+ Called when we want to open our per-info config info. We do so if we can,
+ otherwise we fail the request.
+
+ Arguments:
+ ConfigName - Name of interface to open.
+ Handle - Where to return the handle.
+
+ Return Value:
+ TRUE if we succeed, FALSE if we don't.
+
+
+--*/
+
+{
+ NTSTATUS status;
+ HANDLE myRegKey;
+ UNICODE_STRING valueString;
+ WCHAR ServicesRegistryKey[] =
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\";
+ UINT RetStatus = FALSE;
+
+
+ PAGED_CODE();
+
+ TempAdapterName = ConfigName->Buffer;
+
+ //
+ // Get the size of the ConfigName string the easy way.
+ //
+ RtlInitUnicodeString(&valueString, (PWCHAR)ConfigName->Buffer);
+
+ valueString.MaximumLength += sizeof(ServicesRegistryKey);
+
+ valueString.Buffer = ExAllocatePool(
+ NonPagedPool,
+ valueString.MaximumLength
+ );
+
+ if (valueString.Buffer == NULL) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_NO_ADAPTER_RESOURCES,
+ 4,
+ 1,
+ &ConfigName->Buffer,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to allocate memory for reg key name\n"));
+
+ return(FALSE);
+ }
+
+ valueString.Length = 0;
+ valueString.Buffer[0] = UNICODE_NULL;
+
+ //
+ // Build the key name for the tcpip parameters section and open key.
+ //
+ status = RtlAppendUnicodeToString(&valueString, ServicesRegistryKey);
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_ADAPTER_REG_FAILURE,
+ 1,
+ 1,
+ &ConfigName->Buffer,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to append services name to key string\n"));
+
+ goto done;
+ }
+
+ status = RtlAppendUnicodeToString(&valueString, ConfigName->Buffer);
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_ADAPTER_REG_FAILURE,
+ 2,
+ 1,
+ &ConfigName->Buffer,
+ 0,
+ NULL
+ );
+
+ TCPTRACE(("IP: Unable to append adapter name to key string\n"));
+
+ goto done;
+ }
+
+ status = OpenRegKey(&myRegKey, valueString.Buffer);
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_ADAPTER_REG_FAILURE,
+ 4,
+ 1,
+ &ConfigName->Buffer,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "IP: Unable to open adapter registry key %ws\n",
+ valueString.Buffer
+ ));
+
+ } else {
+ RetStatus = TRUE;
+ *Handle = myRegKey;
+ }
+
+done:
+ ExFreePool(valueString.Buffer);
+
+ return RetStatus;
+}
+
+
+VOID
+CloseIFConfig(
+ NDIS_HANDLE Handle
+ )
+
+/*++
+
+ Routine Description:
+
+ Close a per-interface config handle opened via OpenIFConfig().
+
+ Arguments:
+ Handle - Handle to be closed.
+
+ Return Value:
+
+
+--*/
+
+{
+ PAGED_CODE();
+
+ ZwClose(Handle);
+}
+
+
+IPConfigInfo *
+IPGetConfig(
+ void
+ )
+
+/*++
+
+Routine Description:
+
+ Provides IP configuration information for the NT environment.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ A pointer to a structure containing the configuration information.
+
+--*/
+
+{
+ return(IPConfiguration);
+}
+
+
+void
+IPFreeConfig(
+ IPConfigInfo *ConfigInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Frees the IP configuration structure allocated by IPGetConfig.
+
+Arguments:
+
+ ConfigInfo - Pointer to the IP configuration information structure to free.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int i;
+
+#ifndef _PNP_POWER
+
+ NetConfigInfo *netConfiguration;
+
+
+ if (IPConfiguration != NULL) {
+ for (i = 0; i < IPConfiguration->ici_numnets; i++ ) {
+ netConfiguration = &(IPConfiguration->ici_netinfo[i]);
+
+ if (netConfiguration->nci_driver.Buffer != NULL) {
+ CTEFreeString(&(netConfiguration->nci_driver));
+ }
+
+ if (netConfiguration->nci_configname.Buffer != NULL) {
+ CTEFreeString(&(netConfiguration->nci_configname));
+ }
+
+ if (netConfiguration->nci_reghandle != NULL) {
+ ZwClose(netConfiguration->nci_reghandle);
+ }
+ }
+
+ CTEFreeMem(IPConfiguration->ici_netinfo);
+ CTEFreeMem(IPConfiguration);
+ }
+
+#else // _PNP_POWER
+
+ if (IPConfiguration != NULL) {
+ CTEFreeMem(IPConfiguration);
+ }
+
+#endif // _PNP_POWER
+
+ IPConfiguration = NULL;
+
+ return;
+}
+
+ulong
+GetGMTDelta(
+ void
+ )
+
+/*++
+
+Routine Description:
+
+ Returns the offset in milliseconds of the time zone of this machine
+ from GMT.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Time in milliseconds between this time zone and GMT.
+
+--*/
+
+{
+ LARGE_INTEGER localTime, systemTime;
+
+ //
+ // Get time zone bias in 100ns.
+ //
+ localTime.LowPart = 0;
+ localTime.HighPart = 0;
+ ExLocalTimeToSystemTime(&localTime, &systemTime);
+
+ if ((localTime.LowPart != 0) || (localTime.HighPart != 0)) {
+ localTime = CTEConvert100nsToMilliseconds(systemTime);
+ }
+
+ ASSERT(localTime.HighPart == 0);
+
+ return(localTime.LowPart);
+}
+
+
+ulong
+GetTime(
+ void
+ )
+
+/*++
+
+Routine Description:
+
+ Returns the time in milliseconds since midnight.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Time in milliseconds since midnight.
+
+--*/
+
+{
+ LARGE_INTEGER ntTime;
+ TIME_FIELDS breakdownTime;
+ ulong returnValue;
+
+ KeQuerySystemTime(&ntTime);
+ RtlTimeToTimeFields(&ntTime, &breakdownTime);
+
+ returnValue = breakdownTime.Hour * 60;
+ returnValue = (returnValue + breakdownTime.Minute) * 60;
+ returnValue = (returnValue + breakdownTime.Second) * 1000;
+ returnValue = returnValue + breakdownTime.Milliseconds;
+
+ return(returnValue);
+}
+
+
+ulong
+GetUnique32BitValue(
+ void
+ )
+
+/*++
+
+Routine Description:
+
+ Returns a reasonably unique 32-bit number based on the system clock.
+ In NT, we take the current system time, convert it to milliseconds,
+ and return the low 32 bits.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ A reasonably unique 32-bit value.
+
+--*/
+
+{
+ LARGE_INTEGER ntTime, tmpTime;
+
+ KeQuerySystemTime(&ntTime);
+
+ tmpTime = CTEConvert100nsToMilliseconds(ntTime);
+
+ return(tmpTime.LowPart);
+}
+
+
+uint
+UseEtherSNAP(
+ PNDIS_STRING Name
+ )
+
+/*++
+
+Routine Description:
+
+ Determines whether the EtherSNAP protocol should be used on an interface.
+
+Arguments:
+
+ Name - The device name of the interface in question.
+
+Return Value:
+
+ Nonzero if SNAP is to be used on the interface. Zero otherwise.
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(Name);
+
+ //
+ // We currently set this on a global basis.
+ //
+ return(ArpUseEtherSnap);
+}
+
+
+void
+GetAlwaysSourceRoute(
+ uint *pArpAlwaysSourceRoute,
+ uint *pIPAlwaysSourceRoute
+ )
+
+/*++
+
+Routine Description:
+
+ Determines whether ARP should always turn on source routing in queries.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Nonzero if source routing is always to be used. Zero otherwise.
+
+--*/
+
+{
+ //
+ // We currently set this on a global basis.
+ //
+ *pArpAlwaysSourceRoute = ArpAlwaysSourceRoute;
+ *pIPAlwaysSourceRoute = IPAlwaysSourceRoute;
+ return;
+}
+
+
+uint
+GetArpCacheLife(
+ void
+ )
+
+/*++
+
+Routine Description:
+
+ Get ArpCacheLife in seconds.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Set to default if not found.
+
+--*/
+
+{
+ //
+ // We currently set this on a global basis.
+ //
+ return(ArpCacheLife);
+}
+
+
+#define IP_ADDRESS_STRING_LENGTH (16+2) // +2 for double NULL on MULTI_SZ
+
+
+BOOLEAN
+IPConvertStringToAddress(
+ IN PWCHAR AddressString,
+ OUT PULONG IpAddress
+ )
+
+/*++
+
+Routine Description
+
+ This function converts an Internet standard 4-octet dotted decimal
+ IP address string into a numeric IP address. Unlike inet_addr(), this
+ routine does not support address strings of less than 4 octets nor does
+ it support octal and hexadecimal octets.
+
+Arguments
+
+ AddressString - IP address in dotted decimal notation
+ IpAddress - Pointer to a variable to hold the resulting address
+
+Return Value:
+
+ TRUE if the address string was converted. FALSE otherwise.
+
+--*/
+
+{
+ UNICODE_STRING unicodeString;
+ STRING aString;
+ UCHAR dataBuffer[IP_ADDRESS_STRING_LENGTH];
+ NTSTATUS status;
+ PUCHAR addressPtr, cp, startPointer, endPointer;
+ ULONG digit, multiplier;
+ int i;
+
+
+ PAGED_CODE();
+
+ aString.Length = 0;
+ aString.MaximumLength = IP_ADDRESS_STRING_LENGTH;
+ aString.Buffer = dataBuffer;
+
+ RtlInitUnicodeString(&unicodeString, AddressString);
+
+ status = RtlUnicodeStringToAnsiString(
+ &aString,
+ &unicodeString,
+ FALSE
+ );
+
+ if (!NT_SUCCESS(status)) {
+ return(FALSE);
+ }
+
+ *IpAddress = 0;
+ addressPtr = (PUCHAR) IpAddress;
+ startPointer = dataBuffer;
+ endPointer = dataBuffer;
+ i = 3;
+
+ while (i >= 0) {
+ //
+ // Collect the characters up to a '.' or the end of the string.
+ //
+ while ((*endPointer != '.') && (*endPointer != '\0')) {
+ endPointer++;
+ }
+
+ if (startPointer == endPointer) {
+ return(FALSE);
+ }
+
+ //
+ // Convert the number.
+ //
+
+ for ( cp = (endPointer - 1), multiplier = 1, digit = 0;
+ cp >= startPointer;
+ cp--, multiplier *= 10
+ ) {
+
+ if ((*cp < '0') || (*cp > '9') || (multiplier > 100)) {
+ return(FALSE);
+ }
+
+ digit += (multiplier * ((ULONG) (*cp - '0')));
+ }
+
+ if (digit > 255) {
+ return(FALSE);
+ }
+
+ addressPtr[i] = (UCHAR) digit;
+
+ //
+ // We are finished if we have found and converted 4 octets and have
+ // no other characters left in the string.
+ //
+ if ( (i-- == 0) &&
+ ((*endPointer == '\0') || (*endPointer == ' '))
+ ) {
+ return(TRUE);
+ }
+
+ if (*endPointer == '\0') {
+ return(FALSE);
+ }
+
+ startPointer = ++endPointer;
+ }
+
+ return(FALSE);
+}
+
+
+ULONG
+RouteMatch(
+ IN WCHAR *RouteString,
+ IN IPAddr Address,
+ IN IPMask Mask,
+ OUT IPAddr *DestVal,
+ OUT IPMask *DestMask,
+ OUT IPAddr *GateVal,
+ OUT ULONG *Metric
+ )
+
+/*++
+
+Routine Description
+
+ This function checks if a perisitent route should be assigned to
+ a given interface based on the interface address & mask.
+
+Arguments
+
+ RouteString - A NULL-terminated route laid out as Dest,Mask,Gate.
+ Address - The IP address of the interface being processed.
+ Mask - The subnet mask of the interface being processed.
+ DestVal - A pointer to the decoded destination IP address.
+ DestVal - A pointer to the decoded destination subnet mask.
+ DestVal - A pointer to the decoded destination first hop gateway.
+ Metric - A pointer to the decoded route metric.
+
+Return Value:
+
+ The route type, IRE_TYPE_DIRECT or IRE_TYPE_INDIRECT, if the route
+ should be added to the interface, IRE_TYPE_INVALID otherwise.
+
+--*/
+
+{
+#define ROUTE_SEPARATOR L','
+
+ WCHAR *labelPtr;
+ WCHAR *indexPtr = RouteString;
+ ULONG i;
+ UNICODE_STRING ustring;
+ NTSTATUS status;
+ BOOLEAN noMetric = FALSE;
+
+
+ PAGED_CODE();
+
+ //
+ // The route is laid out in the string as "Dest,Mask,Gateway,Metric".
+ // The metric may not be there if this system was upgraded from
+ // NT 3.51.
+ //
+ // Parse the string and convert each label.
+ //
+
+ for (i=0; i<4; i++) {
+
+ labelPtr = indexPtr;
+
+ while (1) {
+
+ if (*indexPtr == UNICODE_NULL) {
+ if ((i < 2) || (indexPtr == labelPtr)) {
+ return(IRE_TYPE_INVALID);
+ }
+
+ if (i == 2) {
+ //
+ // Old route - no metric.
+ //
+ noMetric = TRUE;
+ }
+
+ break;
+ }
+
+ if (*indexPtr == ROUTE_SEPARATOR) {
+ *indexPtr = UNICODE_NULL;
+ break;
+ }
+
+ indexPtr++;
+ }
+
+ switch(i) {
+ case 0:
+ if (!IPConvertStringToAddress(labelPtr, DestVal)) {
+ return(IRE_TYPE_INVALID);
+ }
+ break;
+
+ case 1:
+ if (!IPConvertStringToAddress(labelPtr, DestMask)) {
+ return(IRE_TYPE_INVALID);
+ }
+ break;
+
+ case 2:
+ if (!IPConvertStringToAddress(labelPtr, GateVal)) {
+ return(IRE_TYPE_INVALID);
+ }
+ break;
+
+ case 3:
+ RtlInitUnicodeString(&ustring, labelPtr);
+
+ status = RtlUnicodeStringToInteger(
+ &ustring,
+ 0,
+ Metric
+ );
+
+ if (!NT_SUCCESS(status)) {
+ return(IRE_TYPE_INVALID);
+ }
+
+ break;
+
+ default:
+ ASSERT(0);
+ return(IRE_TYPE_INVALID);
+ }
+
+ if (noMetric) {
+ //
+ // Default to 1.
+ //
+ *Metric = 1;
+ break;
+ }
+
+ indexPtr++;
+ }
+
+ if (IP_ADDR_EQUAL(*GateVal, Address)) {
+ return(IRE_TYPE_DIRECT);
+ }
+
+ if ( IP_ADDR_EQUAL((*GateVal & Mask), (Address & Mask)) ) {
+ return(IRE_TYPE_INDIRECT);
+ }
+
+ return(IRE_TYPE_INVALID);
+}
+
+ULONG
+GetCurrentRouteTable(
+ IPRouteEntry **ppRouteTable
+ )
+/*++
+ Routine Description
+ Allocates memory from non paged pool and fills it with the current route table
+ The caller must free the memory to non-paged pool
+
+ Arguments
+ ppRouteTable Pointer to pointer to array of routes
+
+ Return Value:
+ Count of routes in the table. If memory is allocated, *ppRouteTable will be non NULL
+
+--*/
+{
+ ULONG ulTableCount,ulRouteCount;
+ uint uiValid,uiDataLeft;
+ IPRouteEntry routeEntry;
+ CTELockHandle Handle;
+ UCHAR ucContext[CONTEXT_SIZE];
+
+#define ROUTE_TABLE_OVERFLOW 20 // This MUST NOT be zero
+
+ ulTableCount = IPSInfo.ipsi_numroutes + ROUTE_TABLE_OVERFLOW;
+
+ *ppRouteTable = ExAllocatePoolWithTag(NonPagedPool,
+ ulTableCount * sizeof(IPRouteEntry),
+ 'pI');
+ ulRouteCount = 0;
+
+ if(*ppRouteTable == NULL)
+ {
+ TCPTRACE(("IP: Couldnt allocate memory for route table\n"));
+ }
+ else
+ {
+ CTEGetLock(&RouteTableLock, &Handle);
+
+ RtlZeroMemory((PVOID)ucContext,CONTEXT_SIZE);
+
+ uiDataLeft = RTValidateContext((PVOID)ucContext, &uiValid);
+
+ if(!uiValid)
+ {
+ CTEFreeLock(&RouteTableLock, Handle);
+ }
+ else
+ {
+ while(uiDataLeft)
+ {
+ if(ulRouteCount < ulTableCount)
+ {
+ uiDataLeft = RTReadNext((PVOID)ucContext, &routeEntry);
+
+ (*ppRouteTable)[ulRouteCount++] = routeEntry;
+ }
+ else
+ {
+ TCPTRACE(("IP: Couldnt read out all routes. Increase ROUTE_TABLE_OVERFLOW\n"));
+ }
+ }
+
+ CTEFreeLock(&RouteTableLock, Handle);
+ }
+ }
+
+ return ulRouteCount;
+}
+
+
+VOID
+SetPersistentRoutesForNTE(
+ IPAddr Address,
+ IPMask Mask,
+ ULONG IFIndex
+ )
+
+/*++
+
+Routine Description
+
+ Adds persistent routes that match an interface. The routes are read
+ from a list in the registry.
+
+Arguments
+
+ Address - The address of the new interface
+ Mask - The subnet mask of the new interface.
+ IFIndex - The index of the new interface.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+#define ROUTE_DATA_STRING_SIZE (51 * sizeof(WCHAR))
+#define BASIC_INFO_SIZE ( sizeof(KEY_VALUE_BASIC_INFORMATION) - \
+ sizeof(WCHAR) + ROUTE_DATA_STRING_SIZE )
+ IPAddr destVal;
+ IPMask destMask;
+ IPAddr gateVal;
+ ULONG metric;
+ ULONG enumIndex = 0;
+ UCHAR workbuf[BASIC_INFO_SIZE];
+ PKEY_VALUE_BASIC_INFORMATION basicInfo =
+ (PKEY_VALUE_BASIC_INFORMATION) workbuf;
+ ULONG resultLength;
+ HANDLE regKey;
+ WCHAR IPRoutesRegistryKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters\\PersistentRoutes";
+ TDIObjectID id;
+ IPRouteEntry routeEntry,*pRouteTable;
+ ULONG ulRouteCount,i;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ pRouteTable = NULL;
+
+ ulRouteCount = GetCurrentRouteTable(&pRouteTable);
+
+ id.toi_entity.tei_entity = CL_NL_ENTITY;
+ id.toi_entity.tei_instance = 0;
+ id.toi_class = INFO_CLASS_PROTOCOL;
+ id.toi_type = INFO_TYPE_PROVIDER;
+ id.toi_id = IP_MIB_RTTABLE_ENTRY_ID;
+
+ routeEntry.ire_index = IFIndex;
+ routeEntry.ire_metric2 = (ULONG) -1;
+ routeEntry.ire_metric3 = (ULONG) -1;
+ routeEntry.ire_metric4 = (ULONG) -1;
+ routeEntry.ire_proto = IRE_PROTO_LOCAL;
+ routeEntry.ire_age = 0;
+ routeEntry.ire_metric5 = (ULONG) -1;
+
+ status = OpenRegKey(&regKey, IPRoutesRegistryKey);
+
+ if (NT_SUCCESS(status)) {
+ ULONG type;
+
+ do {
+ status = ZwEnumerateValueKey(
+ regKey,
+ enumIndex,
+ KeyValueBasicInformation,
+ basicInfo,
+ BASIC_INFO_SIZE - sizeof(WCHAR),
+ &resultLength
+ );
+
+ if (!NT_SUCCESS(status)) {
+ if (status == STATUS_BUFFER_OVERFLOW) {
+ continue;
+ }
+
+ break;
+ }
+
+ if (basicInfo->Type != REG_SZ) {
+ continue;
+ }
+
+ //
+ // Ensure NULL termination
+ //
+ basicInfo->Name[basicInfo->NameLength/sizeof(WCHAR)] = UNICODE_NULL;
+ basicInfo->NameLength += sizeof(WCHAR);
+
+ type = RouteMatch(
+ basicInfo->Name,
+ Address,
+ Mask,
+ &destVal,
+ &destMask,
+ &gateVal,
+ &metric
+ );
+
+ if (type != IRE_TYPE_INVALID)
+ {
+ long setStatus;
+ ULONG ulFound;
+
+ routeEntry.ire_dest = net_long(destVal),
+ routeEntry.ire_mask = net_long(destMask),
+ routeEntry.ire_nexthop = net_long(gateVal);
+ routeEntry.ire_type = type;
+ routeEntry.ire_metric1 = metric;
+
+ ulFound = FALSE;
+
+ for(i = 0; i < ulRouteCount; i++)
+ {
+ if((routeEntry.ire_dest == pRouteTable[i].ire_dest) &&
+ (routeEntry.ire_mask == pRouteTable[i].ire_mask))
+ {
+ ulFound = TRUE;
+
+ break;
+ }
+ }
+
+ if(!ulFound)
+ {
+
+ setStatus = IPSetInfo(
+ &id,
+ &routeEntry,
+ sizeof(IPRouteEntry)
+ );
+#if DBG
+ if (setStatus != IP_SUCCESS)
+ {
+ TCPTRACE((
+ "IP: set of sticky route [%x, %x, %x] failed, status %d\n",
+ destVal,
+ destMask,
+ gateVal,
+ metric,
+ setStatus
+ ));
+ }
+#endif // DBG
+ }
+
+ }
+
+ } while (++enumIndex);
+
+ ZwClose(regKey);
+ }
+
+ if(pRouteTable)
+ {
+ ExFreePool(pRouteTable);
+ }
+}
+
diff --git a/private/ntos/tdi/tcpip/ip/ntirp.c b/private/ntos/tdi/tcpip/ip/ntirp.c
new file mode 100644
index 000000000..3dd34565b
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/ntirp.c
@@ -0,0 +1,1458 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ntirp.c
+
+Abstract:
+
+ NT specific routines for dispatching and handling IRPs.
+
+Author:
+
+ Mike Massa (mikemas) Aug 13, 1993
+
+Revision History:
+
+ Who When What
+ -------- -------- ----------------------------------------------
+ mikemas 08-13-93 created
+
+Notes:
+
+--*/
+
+#include <oscfg.h>
+#include <ndis.h>
+#include <cxport.h>
+#include <ip.h>
+#include "ipdef.h"
+#include "ipinit.h"
+#include "icmp.h"
+#include <ntddip.h>
+#include <llipif.h>
+#include <ipfilter.h>
+
+
+//
+// Local structures.
+//
+typedef struct pending_irp {
+ LIST_ENTRY Linkage;
+ PIRP Irp;
+ PFILE_OBJECT FileObject;
+ PVOID Context;
+} PENDING_IRP, *PPENDING_IRP;
+
+
+//
+// Global variables
+//
+LIST_ENTRY PendingEchoList;
+LIST_ENTRY PendingIPSetNTEAddrList;
+
+
+//
+// External prototypes
+//
+IP_STATUS
+ICMPEchoRequest(
+ void *InputBuffer,
+ uint InputBufferLength,
+ EchoControl *ControlBlock,
+ EchoRtn Callback
+ );
+
+ulong
+ICMPEchoComplete(
+ EchoControl *ControlBlock,
+ IP_STATUS Status,
+ void *Data,
+ uint DataSize,
+ struct IPOptInfo *OptionInfo
+ );
+
+IP_STATUS
+IPSetNTEAddr(
+ uint Index,
+ IPAddr Addr,
+ IPMask Mask,
+ SetAddrControl *ControlBlock,
+ SetAddrRtn Callback
+ );
+
+uint
+IPAddDynamicNTE(
+ ushort InterfaceContext,
+ IPAddr NewAddr,
+ IPMask NewMask,
+ ushort *NTEContext,
+ ulong *NTEInstance
+ );
+
+uint
+IPDeleteDynamicNTE(
+ ushort NTEContext
+ );
+
+uint
+IPGetNTEInfo(
+ ushort NTEContext,
+ ulong *NTEInstance,
+ IPAddr *Address,
+ IPMask *SubnetMask,
+ ushort *NTEFlags
+ );
+
+uint
+SetDHCPNTE(
+ uint Context
+ );
+
+//
+// Local prototypes
+//
+NTSTATUS
+IPDispatch (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+IPDispatchDeviceControl(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+IPDispatchInternalDeviceControl(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+IPCreate(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+IPCleanup(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+IPClose(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+DispatchEchoRequest(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+void
+CompleteEchoRequest(
+ void *Context,
+ IP_STATUS Status,
+ void *Data,
+ uint DataSize,
+ struct IPOptInfo *OptionInfo
+ );
+
+NTSTATUS
+DispatchIPSetNTEAddrRequest(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+void
+CompleteIPSetNTEAddrRequest(
+ void *Context,
+ IP_STATUS Status
+ );
+
+
+#ifdef _PNP_POWER
+extern IP_STATUS IPAddInterface(PNDIS_STRING ConfigName, void *PNPContext, void *Context, LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo) ;
+extern void IPDelInterface(void *Context) ;
+#endif
+
+
+//
+// All of this code is pageable.
+//
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, IPDispatch)
+#pragma alloc_text(PAGE, IPDispatchDeviceControl)
+#pragma alloc_text(PAGE, IPDispatchInternalDeviceControl)
+#pragma alloc_text(PAGE, IPCreate)
+#pragma alloc_text(PAGE, IPClose)
+#pragma alloc_text(PAGE, DispatchEchoRequest)
+
+#endif // ALLOC_PRAGMA
+
+
+//
+// Dispatch function definitions
+//
+NTSTATUS
+IPDispatch (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This is the dispatch routine for IP.
+
+Arguments:
+
+ DeviceObject - Pointer to device object for target device
+ Irp - Pointer to I/O request packet
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+ PAGED_CODE();
+
+ irpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ switch (irpSp->MajorFunction) {
+
+ case IRP_MJ_DEVICE_CONTROL:
+ return IPDispatchDeviceControl(Irp, irpSp);
+
+ case IRP_MJ_INTERNAL_DEVICE_CONTROL:
+ return IPDispatchDeviceControl(Irp, irpSp);
+
+ case IRP_MJ_CREATE:
+ status = IPCreate(Irp, irpSp);
+ break;
+
+ case IRP_MJ_CLEANUP:
+ status = IPCleanup(Irp, irpSp);
+ break;
+
+ case IRP_MJ_CLOSE:
+ status = IPClose(Irp, irpSp);
+ break;
+
+ default:
+ CTEPrint("IPDispatch: Invalid major function ");
+ CTEPrintNum(irpSp->MajorFunction );
+ CTEPrintCRLF();
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ Irp->IoStatus.Status = status;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(status);
+
+} // IPDispatch
+
+
+NTSTATUS
+IPDispatchDeviceControl(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ NTSTATUS status;
+ ULONG code;
+
+
+ PAGED_CODE();
+
+ Irp->IoStatus.Information = 0;
+
+ code = IrpSp->Parameters.DeviceIoControl.IoControlCode;
+
+ switch(code) {
+
+ case IOCTL_ICMP_ECHO_REQUEST:
+ return(DispatchEchoRequest(Irp, IrpSp));
+
+ case IOCTL_IP_SET_ADDRESS:
+ return(DispatchIPSetNTEAddrRequest(Irp, IrpSp));
+
+ case IOCTL_IP_ADD_NTE:
+ {
+ PIP_ADD_NTE_REQUEST request;
+ PIP_ADD_NTE_RESPONSE response;
+ BOOLEAN retval;
+
+
+ request = Irp->AssociatedIrp.SystemBuffer;
+ response = (PIP_ADD_NTE_RESPONSE) request;
+
+ //
+ // Validate input parameters
+ //
+ if ( (IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
+ sizeof(IP_ADD_NTE_REQUEST)
+ )
+ &&
+ (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >=
+ sizeof(IP_ADD_NTE_RESPONSE))
+
+ )
+ {
+ retval = IPAddDynamicNTE(
+ request->InterfaceContext,
+ request->Address,
+ request->SubnetMask,
+ &(response->Context),
+ &(response->Instance)
+ );
+
+ if (retval == FALSE) {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ Irp->IoStatus.Information = sizeof(IP_ADD_NTE_RESPONSE);
+ status = STATUS_SUCCESS;
+ }
+ }
+ else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ break;
+
+ case IOCTL_IP_DELETE_NTE:
+ {
+ PIP_DELETE_NTE_REQUEST request;
+ BOOLEAN retval;
+
+
+ request = Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Validate input parameters
+ //
+ if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
+ sizeof(IP_DELETE_NTE_REQUEST)
+ )
+ {
+ retval = IPDeleteDynamicNTE(
+ request->Context
+ );
+
+ if (retval == FALSE) {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ status = STATUS_SUCCESS;
+ }
+ }
+ else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ break;
+
+ case IOCTL_IP_GET_NTE_INFO:
+ {
+ PIP_GET_NTE_INFO_REQUEST request;
+ PIP_GET_NTE_INFO_RESPONSE response;
+ BOOLEAN retval;
+ ushort nteFlags;
+
+
+ request = Irp->AssociatedIrp.SystemBuffer;
+ response = (PIP_GET_NTE_INFO_RESPONSE) request;
+
+ //
+ // Validate input parameters
+ //
+ if ( (IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
+ sizeof(IP_GET_NTE_INFO_REQUEST)
+ )
+ &&
+ (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >=
+ sizeof(IP_GET_NTE_INFO_RESPONSE))
+
+ )
+ {
+ retval = IPGetNTEInfo(
+ request->Context,
+ &(response->Instance),
+ &(response->Address),
+ &(response->SubnetMask),
+ &nteFlags
+ );
+
+ if (retval == FALSE) {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information =
+ sizeof(IP_GET_NTE_INFO_RESPONSE);
+ response->Flags = 0;
+
+ if (nteFlags & NTE_DYNAMIC) {
+ response->Flags |= IP_NTE_DYNAMIC;
+ }
+ }
+ }
+ else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ break;
+
+ case IOCTL_IP_SET_DHCP_INTERFACE:
+ {
+ PIP_SET_DHCP_INTERFACE_REQUEST request;
+ BOOLEAN retval;
+
+ request = Irp->AssociatedIrp.SystemBuffer;
+ retval = SetDHCPNTE(
+ request->Context
+ );
+
+ if (retval == FALSE) {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ status = STATUS_SUCCESS;
+ }
+ }
+ break;
+
+ case IOCTL_IP_SET_IF_CONTEXT:
+ {
+ PIP_SET_IF_CONTEXT_INFO info;
+
+
+ info = Irp->AssociatedIrp.SystemBuffer;
+ status = (NTSTATUS) SetIFContext(info->Index, info->Context);
+
+ if (status != IP_SUCCESS) {
+ ASSERT(status != IP_PENDING);
+ //
+ // Map status
+ //
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ status = STATUS_SUCCESS;
+ }
+ }
+ break;
+
+ case IOCTL_IP_SET_FILTER_POINTER:
+ {
+ PIP_SET_FILTER_HOOK_INFO info;
+
+ if (Irp->RequestorMode != KernelMode) {
+ status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ info = Irp->AssociatedIrp.SystemBuffer;
+ status = (NTSTATUS) SetFilterPtr(info->FilterPtr);
+
+ if (status != IP_SUCCESS) {
+ ASSERT(status != IP_PENDING);
+ //
+ // Map status
+ //
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ status = STATUS_SUCCESS;
+ }
+ }
+ break;
+
+ case IOCTL_IP_SET_MAP_ROUTE_POINTER:
+ {
+ PIP_SET_MAP_ROUTE_HOOK_INFO info;
+
+ if (Irp->RequestorMode != KernelMode) {
+ status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ info = Irp->AssociatedIrp.SystemBuffer;
+ status = (NTSTATUS) SetMapRoutePtr(info->MapRoutePtr);
+
+ if (status != IP_SUCCESS) {
+ ASSERT(status != IP_PENDING);
+ //
+ // Map status
+ //
+ status = STATUS_UNSUCCESSFUL;
+ }
+ else {
+ status = STATUS_SUCCESS;
+ }
+ }
+ break;
+
+#ifdef _PNP_POWER
+
+ case IOCTL_IP_GET_PNP_ARP_POINTERS:
+ {
+ PIP_GET_PNP_ARP_POINTERS info = (PIP_GET_PNP_ARP_POINTERS) Irp->AssociatedIrp.SystemBuffer;
+
+ if (Irp->RequestorMode != KernelMode) {
+ status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ info->IPAddInterface = (IPAddInterfacePtr)IPAddInterface ;
+ info->IPDelInterface = (IPDelInterfacePtr)IPDelInterface ;
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(IP_GET_PNP_ARP_POINTERS);
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+ return STATUS_SUCCESS;;
+
+ }
+ break;
+#endif
+
+ default:
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ if (status != IP_PENDING) {
+ Irp->IoStatus.Status = status;
+ // Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+ }
+ return status;
+
+} // IPDispatchDeviceControl
+
+NTSTATUS
+IPDispatchInternalDeviceControl(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ NTSTATUS status;
+
+
+ PAGED_CODE();
+
+ status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return status;
+
+} // IPDispatchDeviceControl
+
+
+NTSTATUS
+IPCreate(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ return(STATUS_SUCCESS);
+
+} // IPCreate
+
+
+NTSTATUS
+IPCleanup(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ PPENDING_IRP pendingIrp;
+ PLIST_ENTRY entry, nextEntry;
+ KIRQL oldIrql;
+ LIST_ENTRY completeList;
+ PIRP cancelledIrp;
+
+
+ InitializeListHead(&completeList);
+
+ //
+ // Collect all of the pending IRPs on this file object.
+ //
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ entry = PendingEchoList.Flink;
+
+ while ( entry != &PendingEchoList ) {
+ pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
+
+ if (pendingIrp->FileObject == IrpSp->FileObject) {
+ nextEntry = entry->Flink;
+ RemoveEntryList(entry);
+ IoSetCancelRoutine(pendingIrp->Irp, NULL);
+ InsertTailList(&completeList, &(pendingIrp->Linkage));
+ entry = nextEntry;
+ }
+ else {
+ entry = entry->Flink;
+ }
+ }
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ //
+ // Complete them.
+ //
+ entry = completeList.Flink;
+
+ while ( entry != &completeList ) {
+ pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
+ cancelledIrp = pendingIrp->Irp;
+ entry = entry->Flink;
+
+ //
+ // Free the PENDING_IRP structure. The control block will be freed
+ // when the request completes.
+ //
+ CTEFreeMem(pendingIrp);
+
+ //
+ // Complete the IRP.
+ //
+ cancelledIrp->IoStatus.Information = 0;
+ cancelledIrp->IoStatus.Status = STATUS_CANCELLED;
+ IoCompleteRequest(cancelledIrp, IO_NETWORK_INCREMENT);
+ }
+
+ InitializeListHead(&completeList);
+
+ //
+ // Collect all of the pending IRPs on this file object.
+ //
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ entry = PendingIPSetNTEAddrList.Flink;
+
+ while ( entry != &PendingIPSetNTEAddrList ) {
+ pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
+
+ if (pendingIrp->FileObject == IrpSp->FileObject) {
+ nextEntry = entry->Flink;
+ RemoveEntryList(entry);
+ IoSetCancelRoutine(pendingIrp->Irp, NULL);
+ InsertTailList(&completeList, &(pendingIrp->Linkage));
+ entry = nextEntry;
+ }
+ else {
+ entry = entry->Flink;
+ }
+ }
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ //
+ // Complete them.
+ //
+ entry = completeList.Flink;
+
+ while ( entry != &completeList ) {
+ pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
+ cancelledIrp = pendingIrp->Irp;
+ entry = entry->Flink;
+
+ //
+ // Free the PENDING_IRP structure. The control block will be freed
+ // when the request completes.
+ //
+ CTEFreeMem(pendingIrp);
+
+ //
+ // Complete the IRP.
+ //
+ cancelledIrp->IoStatus.Information = 0;
+ cancelledIrp->IoStatus.Status = STATUS_CANCELLED;
+ IoCompleteRequest(cancelledIrp, IO_NETWORK_INCREMENT);
+ }
+
+ return(STATUS_SUCCESS);
+
+} // IPCleanup
+
+
+NTSTATUS
+IPClose(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ return(STATUS_SUCCESS);
+
+} // IPClose
+
+
+//
+// ICMP Echo function definitions
+//
+VOID
+CancelEchoRequest(
+ IN PDEVICE_OBJECT Device,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels an outstanding Echo request Irp.
+
+Arguments:
+
+ Device - The device on which the request was issued.
+ Irp - Pointer to I/O request packet to cancel.
+
+Return Value:
+
+ None.
+
+Notes:
+
+ This function is called with cancel spinlock held. It must be
+ released before the function returns.
+
+ The echo control block associated with this request cannot be
+ freed until the request completes. The completion routine will
+ free it.
+
+--*/
+
+{
+ PPENDING_IRP pendingIrp = NULL;
+ PPENDING_IRP item;
+ PLIST_ENTRY entry;
+
+
+ for ( entry = PendingEchoList.Flink;
+ entry != &PendingEchoList;
+ entry = entry->Flink
+ ) {
+ item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
+ if (item->Irp == Irp) {
+ pendingIrp = item;
+ RemoveEntryList(entry);
+ IoSetCancelRoutine(pendingIrp->Irp, NULL);
+ break;
+ }
+ }
+
+ IoReleaseCancelSpinLock(Irp->CancelIrql);
+
+ if (pendingIrp != NULL) {
+ //
+ // Free the PENDING_IRP structure. The control block will be freed
+ // when the request completes.
+ //
+ CTEFreeMem(pendingIrp);
+
+ //
+ // Complete the IRP.
+ //
+ Irp->IoStatus.Information = 0;
+ Irp->IoStatus.Status = STATUS_CANCELLED;
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+ }
+
+ return;
+
+} // CancelEchoRequest
+
+//
+// IP Set Addr function definitions
+//
+VOID
+CancelIPSetNTEAddrRequest(
+ IN PDEVICE_OBJECT Device,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels an outstanding IP Set Addr request Irp.
+
+Arguments:
+
+ Device - The device on which the request was issued.
+ Irp - Pointer to I/O request packet to cancel.
+
+Return Value:
+
+ None.
+
+Notes:
+
+ This function is called with cancel spinlock held. It must be
+ released before the function returns.
+
+ The IP Set Addr control block associated with this request cannot be
+ freed until the request completes. The completion routine will
+ free it.
+
+--*/
+
+{
+ PPENDING_IRP pendingIrp = NULL;
+ PPENDING_IRP item;
+ PLIST_ENTRY entry;
+
+
+ for ( entry = PendingIPSetNTEAddrList.Flink;
+ entry != &PendingIPSetNTEAddrList;
+ entry = entry->Flink
+ ) {
+ item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
+ if (item->Irp == Irp) {
+ pendingIrp = item;
+ RemoveEntryList(entry);
+ IoSetCancelRoutine(pendingIrp->Irp, NULL);
+ break;
+ }
+ }
+
+ IoReleaseCancelSpinLock(Irp->CancelIrql);
+
+ if (pendingIrp != NULL) {
+ //
+ // Free the PENDING_IRP structure. The control block will be freed
+ // when the request completes.
+ //
+ CTEFreeMem(pendingIrp);
+
+ //
+ // Complete the IRP.
+ //
+ Irp->IoStatus.Information = 0;
+ Irp->IoStatus.Status = STATUS_CANCELLED;
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+ }
+
+ return;
+
+} // CancelIPSetNTEAddrRequest
+
+
+void
+CompleteEchoRequest(
+ void *Context,
+ IP_STATUS Status,
+ void *Data, OPTIONAL
+ uint DataSize,
+ struct IPOptInfo *OptionInfo OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ Handles the completion of an ICMP Echo request
+
+Arguments:
+
+ Context - Pointer to the EchoControl structure for this request.
+ Status - The IP status of the transmission.
+ Data - A pointer to data returned in the echo reply.
+ DataSize - The length of the returned data.
+ OptionInfo - A pointer to the IP options in the echo reply.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ KIRQL oldIrql;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ EchoControl *controlBlock;
+ PPENDING_IRP pendingIrp = NULL;
+ PPENDING_IRP item;
+ PLIST_ENTRY entry;
+ ULONG bytesReturned;
+
+
+ controlBlock = (EchoControl *) Context;
+
+ //
+ // Find the echo request IRP on the pending list.
+ //
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ for ( entry = PendingEchoList.Flink;
+ entry != &PendingEchoList;
+ entry = entry->Flink
+ ) {
+ item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
+ if (item->Context == controlBlock) {
+ pendingIrp = item;
+ irp = pendingIrp->Irp;
+ IoSetCancelRoutine(irp, NULL);
+ RemoveEntryList(entry);
+ break;
+ }
+ }
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ if (pendingIrp == NULL) {
+ //
+ // IRP must have been cancelled. PENDING_IRP struct
+ // was freed by cancel routine. Free control block.
+ //
+ CTEFreeMem(controlBlock);
+ return;
+ }
+
+ irpSp = IoGetCurrentIrpStackLocation(irp);
+
+ bytesReturned = ICMPEchoComplete(
+ controlBlock,
+ Status,
+ Data,
+ DataSize,
+ OptionInfo
+ );
+
+ CTEFreeMem(pendingIrp);
+ CTEFreeMem(controlBlock);
+
+ //
+ // Complete the IRP.
+ //
+ irp->IoStatus.Information = (ULONG) bytesReturned;
+ irp->IoStatus.Status = STATUS_SUCCESS;
+ IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
+ return;
+
+} // CompleteEchoRequest
+
+void
+CompleteIPSetNTEAddrRequest(
+ void *Context,
+ IP_STATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ Handles the completion of an IP Set Addr request
+
+Arguments:
+
+ Context - Pointer to the SetAddrControl structure for this request.
+ Status - The IP status of the transmission.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ KIRQL oldIrql;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ SetAddrControl *controlBlock;
+ PPENDING_IRP pendingIrp = NULL;
+ PPENDING_IRP item;
+ PLIST_ENTRY entry;
+ ULONG bytesReturned;
+
+
+ controlBlock = (SetAddrControl *) Context;
+
+ //
+ // Find the echo request IRP on the pending list.
+ //
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ for ( entry = PendingIPSetNTEAddrList.Flink;
+ entry != &PendingIPSetNTEAddrList;
+ entry = entry->Flink
+ ) {
+ item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
+ if (item->Context == controlBlock) {
+ pendingIrp = item;
+ irp = pendingIrp->Irp;
+ IoSetCancelRoutine(irp, NULL);
+ RemoveEntryList(entry);
+ break;
+ }
+ }
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ if (pendingIrp == NULL) {
+ //
+ // IRP must have been cancelled. PENDING_IRP struct
+ // was freed by cancel routine. Free control block.
+ //
+ CTEFreeMem(controlBlock);
+ return;
+ }
+
+ CTEFreeMem(pendingIrp);
+ CTEFreeMem(controlBlock);
+
+ //
+ // Complete the IRP.
+ //
+ irp->IoStatus.Information = 0;
+ if (Status == IP_SUCCESS) {
+ irp->IoStatus.Status = STATUS_SUCCESS;
+ } else {
+ irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
+ }
+ IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
+ return;
+
+} // CompleteIPSetNTEAddrRequest
+
+
+BOOLEAN
+PrepareEchoIrpForCancel(
+ PIRP Irp,
+ PPENDING_IRP PendingIrp
+ )
+/*++
+
+Routine Description:
+
+ Prepares an Echo IRP for cancellation.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet to initialize for cancellation.
+ PendingIrp - Pointer to the PENDING_IRP structure for this IRP.
+
+Return Value:
+
+ TRUE if the IRP was cancelled before this routine was called.
+ FALSE otherwise.
+
+--*/
+
+{
+ BOOLEAN cancelled = TRUE;
+ KIRQL oldIrql;
+
+
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ ASSERT(Irp->CancelRoutine == NULL);
+
+ if (!Irp->Cancel) {
+ IoSetCancelRoutine(Irp, CancelEchoRequest);
+ InsertTailList(&PendingEchoList, &(PendingIrp->Linkage));
+ cancelled = FALSE;
+ }
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ return(cancelled);
+
+} // PrepareEchoIrpForCancel
+
+BOOLEAN
+PrepareIPSetNTEAddrIrpForCancel(
+ PIRP Irp,
+ PPENDING_IRP PendingIrp
+ )
+/*++
+
+Routine Description:
+
+ Prepares an IPSetNTEAddr IRP for cancellation.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet to initialize for cancellation.
+ PendingIrp - Pointer to the PENDING_IRP structure for this IRP.
+
+Return Value:
+
+ TRUE if the IRP was cancelled before this routine was called.
+ FALSE otherwise.
+
+--*/
+
+{
+ BOOLEAN cancelled = TRUE;
+ KIRQL oldIrql;
+
+
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ ASSERT(Irp->CancelRoutine == NULL);
+
+ if (!Irp->Cancel) {
+ IoSetCancelRoutine(Irp, CancelIPSetNTEAddrRequest);
+ InsertTailList(&PendingIPSetNTEAddrList, &(PendingIrp->Linkage));
+ cancelled = FALSE;
+ }
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ return(cancelled);
+
+} // PrepareIPSetNTEAddrIrpForCancel
+
+
+NTSTATUS
+DispatchEchoRequest(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Processes an ICMP request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether NT-specific processing of the request was
+ successful. The status of the actual request is returned in
+ the request buffers.
+
+--*/
+
+{
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+ IP_STATUS ipStatus;
+ PPENDING_IRP pendingIrp;
+ EchoControl *controlBlock;
+ PICMP_ECHO_REPLY replyBuffer;
+ BOOLEAN cancelled;
+
+
+ PAGED_CODE();
+
+ pendingIrp = CTEAllocMem(sizeof(PENDING_IRP));
+
+ if (pendingIrp == NULL) {
+ ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+ goto echo_error;
+ }
+
+ controlBlock = CTEAllocMem(sizeof(EchoControl));
+
+ if (controlBlock == NULL) {
+ ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+ CTEFreeMem(pendingIrp);
+ goto echo_error;
+ }
+
+ pendingIrp->Irp = Irp;
+ pendingIrp->FileObject = IrpSp->FileObject;
+ pendingIrp->Context = controlBlock;
+
+ controlBlock->ec_starttime = CTESystemUpTime();
+ controlBlock->ec_replybuf = Irp->AssociatedIrp.SystemBuffer;
+ controlBlock->ec_replybuflen =
+ IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+
+ IoMarkIrpPending(Irp);
+
+ cancelled = PrepareEchoIrpForCancel(Irp, pendingIrp);
+
+ if (!cancelled) {
+ ipStatus = ICMPEchoRequest(
+ Irp->AssociatedIrp.SystemBuffer, // request buf
+ IrpSp->Parameters.DeviceIoControl.InputBufferLength, // request len
+ controlBlock, // echo ctrl
+ CompleteEchoRequest // cmplt rtn
+ );
+
+ if (ipStatus == IP_PENDING) {
+ ntStatus = STATUS_PENDING;
+ }
+ else {
+ ASSERT(ipStatus != IP_SUCCESS);
+
+ //
+ // An internal error of some kind occurred. Complete the
+ // request.
+ //
+ CompleteEchoRequest(
+ controlBlock,
+ ipStatus,
+ NULL,
+ 0,
+ NULL
+ );
+
+ //
+ // The NT ioctl was successful, even if the request failed. The
+ // request status was passed back in the first reply block.
+ //
+ ntStatus = STATUS_SUCCESS;
+ }
+
+ return(ntStatus);
+ }
+
+ //
+ // Irp has already been cancelled.
+ //
+ ntStatus = STATUS_CANCELLED;
+ CTEFreeMem(pendingIrp);
+ CTEFreeMem(controlBlock);
+
+
+echo_error:
+
+ Irp->IoStatus.Information = 0;
+ Irp->IoStatus.Status = ntStatus;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(ntStatus);
+
+} // DispatchEchoRequest
+
+
+NTSTATUS
+DispatchIPSetNTEAddrRequest(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Processes an IP Set Addr request.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether NT-specific processing of the request was
+ successful. The status of the actual request is returned in
+ the request buffers.
+
+--*/
+
+{
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+ IP_STATUS ipStatus;
+ PPENDING_IRP pendingIrp;
+ SetAddrControl *controlBlock;
+ BOOLEAN cancelled;
+
+
+ PAGED_CODE();
+
+ pendingIrp = CTEAllocMem(sizeof(PENDING_IRP));
+
+ if (pendingIrp == NULL) {
+ ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+ goto setnteaddr_error;
+ }
+
+ controlBlock = CTEAllocMem(sizeof(SetAddrControl));
+
+ if (controlBlock == NULL) {
+ ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+ CTEFreeMem(pendingIrp);
+ goto setnteaddr_error;
+ }
+
+ pendingIrp->Irp = Irp;
+ pendingIrp->FileObject = IrpSp->FileObject;
+ pendingIrp->Context = controlBlock;
+
+ IoMarkIrpPending(Irp);
+
+ cancelled = PrepareIPSetNTEAddrIrpForCancel(Irp, pendingIrp);
+
+ if (!cancelled) {
+
+ PIP_SET_ADDRESS_REQUEST request;
+
+ request = Irp->AssociatedIrp.SystemBuffer;
+ ipStatus = IPSetNTEAddr(
+ request->Context,
+ request->Address,
+ request->SubnetMask,
+ controlBlock,
+ CompleteIPSetNTEAddrRequest
+ );
+
+ if (ipStatus == IP_PENDING) {
+ ntStatus = STATUS_PENDING;
+ }
+ else {
+
+ //
+ // A request completed which did not pend.
+ //
+ CompleteIPSetNTEAddrRequest(
+ controlBlock,
+ ipStatus
+ );
+
+ //
+ // The NT ioctl was successful, even if the request failed. The
+ // request status was passed back in the first reply block.
+ //
+ ntStatus = STATUS_SUCCESS;
+ }
+
+ return(ntStatus);
+ }
+
+ //
+ // Irp has already been cancelled.
+ //
+ ntStatus = STATUS_CANCELLED;
+ CTEFreeMem(pendingIrp);
+ CTEFreeMem(controlBlock);
+
+
+setnteaddr_error:
+
+ Irp->IoStatus.Information = 0;
+ Irp->IoStatus.Status = ntStatus;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(ntStatus);
+
+} // DispatchIPSetNTEAddrRequest
diff --git a/private/ntos/tdi/tcpip/ip/ntreg.c b/private/ntos/tdi/tcpip/ip/ntreg.c
new file mode 100644
index 000000000..e9c53c208
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/ntreg.c
@@ -0,0 +1,672 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ntreg.c
+
+Abstract:
+
+ This source file contains the routines to to access the NT Registry for
+ configuration info.
+
+
+Author:
+
+ Mike Massa (mikemas) September 3, 1993
+
+ (taken from routines by jballard)
+
+Revision History:
+
+--*/
+
+
+#include <oscfg.h>
+#include <ndis.h>
+#include <cxport.h>
+
+
+#define WORK_BUFFER_SIZE 512
+
+
+//
+// Local function prototypes
+//
+NTSTATUS
+OpenRegKey(
+ PHANDLE HandlePtr,
+ PWCHAR KeyName
+ );
+
+NTSTATUS
+GetRegDWORDValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PULONG ValueData
+ );
+
+NTSTATUS
+SetRegDWORDValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PULONG ValueData
+ );
+
+NTSTATUS
+GetRegStringValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PKEY_VALUE_PARTIAL_INFORMATION *ValueData,
+ PUSHORT ValueSize
+ );
+
+NTSTATUS
+GetRegSZValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PUNICODE_STRING ValueData,
+ PULONG ValueType
+ );
+
+NTSTATUS
+GetRegMultiSZValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PUNICODE_STRING ValueData
+ );
+
+VOID
+InitRegDWORDParameter(
+ HANDLE RegKey,
+ PWCHAR ValueName,
+ ULONG *Value,
+ ULONG DefaultValue
+ );
+
+
+#ifdef ALLOC_PRAGMA
+//
+// All of the init code can be discarded
+//
+
+#ifndef _PNP_POWER
+
+#pragma alloc_text(INIT, GetRegDWORDValue)
+#pragma alloc_text(INIT, SetRegDWORDValue)
+#pragma alloc_text(INIT, InitRegDWORDParameter)
+
+#else // _PNP_POWER
+
+#pragma alloc_text(PAGE, GetRegDWORDValue)
+#pragma alloc_text(PAGE, SetRegDWORDValue)
+#pragma alloc_text(PAGE, InitRegDWORDParameter)
+
+#endif // _PNP_POWER
+
+//
+// This code is pagable.
+//
+#pragma alloc_text(PAGE, OpenRegKey)
+#pragma alloc_text(PAGE, GetRegStringValue)
+#pragma alloc_text(PAGE, GetRegSZValue)
+#pragma alloc_text(PAGE, GetRegMultiSZValue)
+
+
+#endif // ALLOC_PRAGMA
+
+
+
+//
+// Function definitions
+//
+NTSTATUS
+OpenRegKey(
+ PHANDLE HandlePtr,
+ PWCHAR KeyName
+ )
+
+/*++
+
+Routine Description:
+
+ Opens a Registry key and returns a handle to it.
+
+Arguments:
+
+ HandlePtr - The varible into which to write the opened handle.
+ KeyName - The name of the Registry key to open.
+
+Return Value:
+
+ STATUS_SUCCESS or an appropriate failure code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING UKeyName;
+
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString(&UKeyName, KeyName);
+
+ memset(&ObjectAttributes, 0, sizeof(OBJECT_ATTRIBUTES));
+ InitializeObjectAttributes(&ObjectAttributes,
+ &UKeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ Status = ZwOpenKey(HandlePtr,
+ KEY_READ,
+ &ObjectAttributes);
+
+ return Status;
+}
+
+
+
+NTSTATUS
+GetRegDWORDValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PULONG ValueData
+ )
+
+/*++
+
+Routine Description:
+
+ Reads a REG_DWORD value from the registry into the supplied variable.
+
+Arguments:
+
+ KeyHandle - Open handle to the parent key of the value to read.
+ ValueName - The name of the value to read.
+ ValueData - The variable into which to read the data.
+
+Return Value:
+
+ STATUS_SUCCESS or an appropriate failure code.
+
+--*/
+
+{
+ NTSTATUS status;
+ ULONG resultLength;
+ PKEY_VALUE_FULL_INFORMATION keyValueFullInformation;
+ UCHAR keybuf[WORK_BUFFER_SIZE];
+ UNICODE_STRING UValueName;
+
+
+#ifdef _PNP_POWER
+ PAGED_CODE();
+#endif // _PNP_POWER
+
+ RtlInitUnicodeString(&UValueName, ValueName);
+
+ keyValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)keybuf;
+ RtlZeroMemory(keyValueFullInformation, sizeof(keyValueFullInformation));
+
+
+ status = ZwQueryValueKey(KeyHandle,
+ &UValueName,
+ KeyValueFullInformation,
+ keyValueFullInformation,
+ WORK_BUFFER_SIZE,
+ &resultLength);
+
+ if (NT_SUCCESS(status)) {
+ if (keyValueFullInformation->Type != REG_DWORD) {
+ status = STATUS_INVALID_PARAMETER_MIX;
+ } else {
+ *ValueData = *((ULONG UNALIGNED *)((PCHAR)keyValueFullInformation +
+ keyValueFullInformation->DataOffset));
+ }
+ }
+
+ return status;
+}
+
+
+
+NTSTATUS
+SetRegDWORDValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PULONG ValueData
+ )
+
+/*++
+
+Routine Description:
+
+ Writes the contents of a variable to a REG_DWORD value.
+
+Arguments:
+
+ KeyHandle - Open handle to the parent key of the value to write.
+ ValueName - The name of the value to write.
+ ValueData - The variable from which to write the data.
+
+Return Value:
+
+ STATUS_SUCCESS or an appropriate failure code.
+
+--*/
+
+{
+ NTSTATUS status;
+ UNICODE_STRING UValueName;
+
+
+#ifdef _PNP_POWER
+ PAGED_CODE();
+#endif // _PNP_POWER
+
+ RtlInitUnicodeString(&UValueName, ValueName);
+
+ status = ZwSetValueKey(KeyHandle,
+ &UValueName,
+ 0,
+ REG_DWORD,
+ ValueData,
+ sizeof(ULONG));
+
+ return status;
+}
+
+
+
+NTSTATUS
+GetRegStringValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PKEY_VALUE_PARTIAL_INFORMATION *ValueData,
+ PUSHORT ValueSize
+ )
+
+/*++
+
+Routine Description:
+
+ Reads a REG_*_SZ string value from the Registry into the supplied
+ key value buffer. If the buffer string buffer is not large enough,
+ it is reallocated.
+
+Arguments:
+
+ KeyHandle - Open handle to the parent key of the value to read.
+ ValueName - The name of the value to read.
+ ValueData - Destination for the read data.
+ ValueSize - Size of the ValueData buffer. Updated on output.
+
+Return Value:
+
+ STATUS_SUCCESS or an appropriate failure code.
+
+--*/
+
+{
+ NTSTATUS status;
+ ULONG resultLength;
+ UNICODE_STRING UValueName;
+
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString(&UValueName, ValueName);
+
+ status = ZwQueryValueKey(
+ KeyHandle,
+ &UValueName,
+ KeyValuePartialInformation,
+ *ValueData,
+ (ULONG) *ValueSize,
+ &resultLength
+ );
+
+ if ( (status == STATUS_BUFFER_OVERFLOW) ||
+ (status == STATUS_BUFFER_TOO_SMALL)
+ )
+ {
+ PVOID temp;
+
+ //
+ // Free the old buffer and allocate a new one of the
+ // appropriate size.
+ //
+
+ ASSERT(resultLength > (ULONG) *ValueSize);
+
+ if (resultLength <= 0xFFFF) {
+
+ temp = CTEAllocMem(resultLength);
+
+ if (temp != NULL) {
+
+ if (*ValueData != NULL) {
+ CTEFreeMem(*ValueData);
+ }
+
+ *ValueData = temp;
+ *ValueSize = (USHORT) resultLength;
+
+ status = ZwQueryValueKey(KeyHandle,
+ &UValueName,
+ KeyValuePartialInformation,
+ *ValueData,
+ *ValueSize,
+ &resultLength
+ );
+
+ ASSERT( (status != STATUS_BUFFER_OVERFLOW) &&
+ (status != STATUS_BUFFER_TOO_SMALL)
+ );
+ }
+ else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+ else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+
+ return(status);
+}
+
+
+
+NTSTATUS
+GetRegMultiSZValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PUNICODE_STRING ValueData
+ )
+
+/*++
+
+Routine Description:
+
+ Reads a REG_MULTI_SZ string value from the Registry into the supplied
+ Unicode string. If the Unicode string buffer is not large enough,
+ it is reallocated.
+
+Arguments:
+
+ KeyHandle - Open handle to the parent key of the value to read.
+ ValueName - The name of the value to read.
+ ValueData - Destination Unicode string for the value data.
+
+Return Value:
+
+ STATUS_SUCCESS or an appropriate failure code.
+
+--*/
+
+{
+ NTSTATUS status;
+ ULONG resultLength;
+ PKEY_VALUE_PARTIAL_INFORMATION keyValuePartialInformation;
+ UNICODE_STRING UValueName;
+
+
+ PAGED_CODE();
+
+ ValueData->Length = 0;
+
+ status = GetRegStringValue(
+ KeyHandle,
+ ValueName,
+ (PKEY_VALUE_PARTIAL_INFORMATION *) &(ValueData->Buffer),
+ &(ValueData->MaximumLength)
+ );
+
+ if (NT_SUCCESS(status)) {
+
+ keyValuePartialInformation =
+ (PKEY_VALUE_PARTIAL_INFORMATION) ValueData->Buffer;
+
+ if (keyValuePartialInformation->Type == REG_MULTI_SZ) {
+
+ ValueData->Length = (USHORT)
+ keyValuePartialInformation->DataLength;
+
+ RtlCopyMemory(
+ ValueData->Buffer,
+ &(keyValuePartialInformation->Data),
+ ValueData->Length
+ );
+ }
+ else {
+ status = STATUS_INVALID_PARAMETER_MIX;
+ }
+ }
+
+ return status;
+
+} // GetRegMultiSZValue
+
+
+
+NTSTATUS
+GetRegSZValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PUNICODE_STRING ValueData,
+ PULONG ValueType
+ )
+
+/*++
+
+Routine Description:
+
+ Reads a REG_SZ string value from the Registry into the supplied
+ Unicode string. If the Unicode string buffer is not large enough,
+ it is reallocated.
+
+Arguments:
+
+ KeyHandle - Open handle to the parent key of the value to read.
+ ValueName - The name of the value to read.
+ ValueData - Destination Unicode string for the value data.
+ ValueType - On return, contains the Registry type of the value read.
+
+Return Value:
+
+ STATUS_SUCCESS or an appropriate failure code.
+
+--*/
+
+{
+ NTSTATUS status;
+ ULONG resultLength;
+ PKEY_VALUE_PARTIAL_INFORMATION keyValuePartialInformation;
+ UNICODE_STRING UValueName;
+
+
+ PAGED_CODE();
+
+ ValueData->Length = 0;
+
+ status = GetRegStringValue(
+ KeyHandle,
+ ValueName,
+ (PKEY_VALUE_PARTIAL_INFORMATION *) &(ValueData->Buffer),
+ &(ValueData->MaximumLength)
+ );
+
+ if (NT_SUCCESS(status)) {
+
+ keyValuePartialInformation =
+ (PKEY_VALUE_PARTIAL_INFORMATION)ValueData->Buffer;
+
+ if ((keyValuePartialInformation->Type == REG_SZ) ||
+ (keyValuePartialInformation->Type == REG_EXPAND_SZ)
+ )
+ {
+ WCHAR *src;
+ WCHAR *dst;
+ ULONG dataLength;
+
+
+ *ValueType = keyValuePartialInformation->Type;
+ dataLength = keyValuePartialInformation->DataLength;
+
+ ASSERT(dataLength <= ValueData->MaximumLength);
+
+ dst = ValueData->Buffer;
+ src = (PWCHAR) &(keyValuePartialInformation->Data);
+
+ while (ValueData->Length <= dataLength) {
+
+ if ( (*dst++ = *src++) == UNICODE_NULL ) {
+ break;
+ }
+
+ ValueData->Length += sizeof(WCHAR);
+ }
+
+ if (ValueData->Length < (ValueData->MaximumLength - 1)) {
+ ValueData->Buffer[ValueData->Length/sizeof(WCHAR)] =
+ UNICODE_NULL;
+ }
+ }
+ else {
+ status = STATUS_INVALID_PARAMETER_MIX;
+ }
+ }
+
+ return status;
+}
+
+
+
+VOID
+InitRegDWORDParameter(
+ HANDLE RegKey,
+ PWCHAR ValueName,
+ ULONG *Value,
+ ULONG DefaultValue
+ )
+
+/*++
+
+Routine Description:
+
+ Reads a REG_DWORD parameter from the Registry into a variable. If the
+ read fails, the variable is initialized to a default.
+
+Arguments:
+
+ RegKey - Open handle to the parent key of the value to read.
+ ValueName - The name of the value to read.
+ Value - Destination variable into which to read the data.
+ DefaultValue - Default to assign if the read fails.
+
+Return Value:
+
+ STATUS_SUCCESS or an appropriate failure code.
+
+--*/
+
+{
+ NTSTATUS status;
+
+
+#ifdef _PNP_POWER
+ PAGED_CODE();
+#endif // _PNP_POWER
+
+ status = GetRegDWORDValue(
+ RegKey,
+ ValueName,
+ Value
+ );
+
+ if (!NT_SUCCESS(status)) {
+ //
+ // These registry parameters override the defaults, so their
+ // absence is not an error.
+ //
+ *Value = DefaultValue;
+ }
+
+ return;
+}
+
+
+PWCHAR
+EnumRegMultiSz(
+ IN PWCHAR MszString,
+ IN ULONG MszStringLength,
+ IN ULONG StringIndex
+ )
+/*++
+
+ Routine Description:
+
+ Parses a REG_MULTI_SZ string and returns the specified substring.
+
+ Arguments:
+
+ MszString - A pointer to the REG_MULTI_SZ string.
+
+ MszStringLength - The length of the REG_MULTI_SZ string, including the
+ terminating null character.
+
+ StringIndex - Index number of the substring to return. Specifiying
+ index 0 retrieves the first substring.
+
+ Return Value:
+
+ A pointer to the specified substring.
+
+ Notes:
+
+ This code is called at raised IRQL. It is not pageable.
+
+--*/
+{
+ PWCHAR string = MszString;
+
+ if ( MszStringLength < (2 * sizeof(WCHAR)) ) {
+ return(NULL);
+ }
+
+ //
+ // Find the start of the desired string.
+ //
+ while (StringIndex) {
+
+ while (MszStringLength >= sizeof(WCHAR)) {
+ MszStringLength -= sizeof(WCHAR);
+
+ if (*string++ == UNICODE_NULL) {
+ break;
+ }
+ }
+
+ //
+ // Check for index out of range.
+ //
+ if ( MszStringLength < (2 * sizeof(UNICODE_NULL)) ) {
+ return(NULL);
+ }
+
+ StringIndex--;
+ }
+
+ if ( MszStringLength < (2 * sizeof(UNICODE_NULL)) ) {
+ return(NULL);
+ }
+
+ return(string);
+}
+
+
diff --git a/private/ntos/tdi/tcpip/ip/sources.inc b/private/ntos/tdi/tcpip/ip/sources.inc
new file mode 100644
index 000000000..87a099a83
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/sources.inc
@@ -0,0 +1,59 @@
+!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=ip
+
+NTPROFILEINPUT=yes
+
+TARGETNAME=ip
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+TARGETLIBS=
+
+INCLUDES=..\..\..\..\inc;..\..\..\..\..\inc;..\..\h
+
+C_DEFINES=$(C_DEFINES) -DNT -D_NTDRIVER_ -D_PNP_POWER -DSECFLTR
+
+!IFDEF BUILD_FOR_3_51
+C_DEFINES= $(C_DEFINES) -D_NTIFS_
+!ENDIF
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ ..\arp.c \
+ ..\icmp.c \
+ ..\igmp.c \
+ ..\info.c \
+ ..\init.c \
+ ..\iploop.c \
+ ..\iprcv.c \
+ ..\iproute.c \
+ ..\ipstatus.c \
+ ..\ipxmit.c \
+ ..\ntip.c \
+ ..\ntirp.c \
+ ..\ntreg.c
diff --git a/private/ntos/tdi/tcpip/ip/up/makefile b/private/ntos/tdi/tcpip/ip/up/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/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/tcpip/ip/up/sources b/private/ntos/tdi/tcpip/ip/up/sources
new file mode 100644
index 000000000..91036a15a
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/up/sources
@@ -0,0 +1,27 @@
+!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
+
+!include ..\sources.inc
diff --git a/private/ntos/tdi/tcpip/tcp/addr.c b/private/ntos/tdi/tcpip/tcp/addr.c
new file mode 100644
index 000000000..eec1997db
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/addr.c
@@ -0,0 +1,2061 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** ADDR.C - TDI address object procedures
+//
+// This file contains the TDI address object related procedures,
+// including TDI open address, TDI close address, etc.
+//
+// The local address objects are stored in a hash table, protected
+// by the AddrObjTableLock. In order to insert or delete from the
+// hash table this lock must be held, as well as the address object
+// lock. The table lock must always be taken before the object lock.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "tdi.h"
+#include "tdistat.h"
+#include "cxport.h"
+#include "ip.h"
+#ifdef VXD
+#include "tdivxd.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "udp.h"
+#include "raw.h"
+#ifndef UDP_ONLY
+#include "tcp.h"
+#include "tcpconn.h"
+#else
+#include "tcpdeb.h"
+#endif
+#include "info.h"
+#include "tcpinfo.h"
+#include "tcpcfg.h"
+
+
+extern IPInfo LocalNetInfo; // Information about the local nets.
+EXTERNAL_LOCK(DGSendReqLock)
+
+#ifndef UDP_ONLY
+EXTERNAL_LOCK(ConnTableLock)
+#endif
+
+extern void FreeAORequest(AORequest *Request);
+
+//* Addess object hash table.
+AddrObj *AddrObjTable[AO_TABLE_SIZE];
+AddrObj *LastAO; // one element lookup cache.
+DEFINE_LOCK_STRUCTURE(AddrObjTableLock)
+
+//* AORequest free list.
+AORequest *AORequestFree;
+
+ushort NextUserPort = MIN_USER_PORT;
+
+#define NUM_AO_REQUEST 5
+DEFINE_LOCK_STRUCTURE(AORequestLock)
+
+#define AO_HASH(a, p) ((*(uint *)&(a) + (uint)(p)) % AO_TABLE_SIZE)
+
+#ifdef VXD
+
+#define DEFAULT_AO_INDEX_SIZE 32
+#define AO_INDEX_INCR 16 // How much to grow by.
+
+typedef AddrObj *AOIndexTbl[];
+
+ushort AOInstance; // Global AO instance count.
+
+uint AOIndexSize; // # of entries in AOIndex.
+uint NextAOIndex; // Next AO index to use.
+AOIndexTbl *AOIndex;
+
+#define AO_INDEX(i) ((i) & 0xffff)
+#define AO_INST(i) ((i) >> 16)
+#define MAKE_AO_INDEX(s, i) (uint)(((s) << 16) | ((i) & 0xffff))
+
+#define MAX_INDEX_SIZE 256
+
+#define INVALID_INDEX 0xffffffff
+
+#endif
+
+
+//
+// All of the init code can be discarded.
+//
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+
+int InitAddr();
+
+#pragma alloc_text(INIT, InitAddr)
+
+#endif // ALLOC_PRAGMA
+#endif
+
+
+#ifdef VXD
+
+//* GetIndexedAO - Get an AddrObj from an index.
+//
+// Called by the UDP routines that access an AO to find the AO by it's
+// index. We look it up in the table, and compare the high 16 bits against
+// the instance # in the AddrObj.
+//
+// Input: Index - Index of AddrObj.
+//
+// Returns: Pointer to AO, or NULL if it's not valid.
+//
+AddrObj *
+GetIndexedAO(uint Index)
+{
+ AddrObj *AO;
+
+ if (AO_INDEX(Index) < AOIndexSize) {
+ AO = (*AOIndex)[AO_INDEX(Index)];
+ if (AO != NULL && AO->ao_inst == AO_INST(Index))
+ return AO;
+ }
+ return NULL;
+
+}
+
+//* GetAOIndex - Get an index value for an AddrObj.
+//
+// Called when we're creating an index value for an AddrObj. We go through
+// the table, looking for a valid index. If we find one, we'll make an index
+// out of it and the AOInstance variable, and bump the instance field.
+// Otherwise we may grow the table.
+//
+// Input: AO - AddrObj to put into table.
+//
+// Returns: Index to use, or INVALID_INDEX if we can't find one.
+//
+uint
+GetAOIndex(AddrObj *AO)
+{
+ uint i; // Index variable.
+ uint CurrentIndex; // Current index being checked.
+
+ for (;;) {
+ CurrentIndex = NextAOIndex;
+ for (i = 0; i < AOIndexSize; i++) {
+ if (CurrentIndex == AOIndexSize)
+ CurrentIndex = 0; // Set it back to beginning.
+
+ if ((*AOIndex)[CurrentIndex] == NULL)
+ break; // Found the one we needed.
+
+ CurrentIndex++;
+ }
+
+ if (i < AOIndexSize) {
+ uint NewIndex;
+
+ // We came out because we found an empty slot.
+ AO->ao_inst = AOInstance;
+ AO->ao_index = (uchar)CurrentIndex;
+ (*AOIndex)[CurrentIndex] = AO;
+ NextAOIndex = CurrentIndex + 1; // Bump the next one to look at.
+ NewIndex = MAKE_AO_INDEX(AOInstance, CurrentIndex);
+ AOInstance++;
+ return NewIndex;
+ } else {
+ // Couldn't find a slot, so grow the table.
+ if (AOIndexSize != MAX_INDEX_SIZE) {
+ // Table isn't already at max size. Try and grow it.
+
+ uint NewIndexSize;
+ AOIndexTbl *NewIndexTbl, *OldIndexTbl;
+
+ NewIndexSize = MIN(MAX_INDEX_SIZE, AOIndexSize + AO_INDEX_INCR);
+ NewIndexTbl = CTEAllocMem(sizeof(AddrObj *) * NewIndexSize);
+ if (NewIndexTbl != NULL) {
+ // We allocated it.
+ CTEMemCopy(NewIndexTbl, AOIndex,
+ AOIndexSize * sizeof(AddrObj *));
+ OldIndexTbl = AOIndex;
+ AOIndex = NewIndexTbl;
+ AOIndexSize = NewIndexSize;
+ CTEFreeMem(OldIndexTbl); // Loop around, and try again.
+ } else
+ return INVALID_INDEX;
+ } else
+ return INVALID_INDEX;
+ }
+ }
+
+}
+
+#endif
+
+//* ReadNextAO - Read the next AddrObj in the table.
+//
+// Called to read the next AddrObj in the table. The needed information
+// is derived from the incoming context, which is assumed to be valid.
+// We'll copy the information, and then update the context value with
+// the next AddrObj to be read.
+//
+// Input: Context - Poiner to a UDPContext.
+// Buffer - Pointer to a UDPEntry structure.
+//
+// Returns: TRUE if more data is available to be read, FALSE is not.
+//
+uint
+ReadNextAO(void *Context, void *Buffer)
+{
+ UDPContext *UContext = (UDPContext *)Context;
+ UDPEntry *UEntry = (UDPEntry *)Buffer;
+ AddrObj *CurrentAO;
+ uint i;
+
+ CurrentAO = UContext->uc_ao;
+ CTEStructAssert(CurrentAO, ao);
+
+ UEntry->ue_localaddr = CurrentAO->ao_addr;
+ UEntry->ue_localport = CurrentAO->ao_port;
+
+ // We've filled it in. Now update the context.
+ CurrentAO = CurrentAO->ao_next;
+ if (CurrentAO != NULL && CurrentAO->ao_prot == PROTOCOL_UDP) {
+ UContext->uc_ao = CurrentAO;
+ return TRUE;
+ } else {
+ // The next AO is NULL, or not a UDP AO. Loop through the AddrObjTable
+ // looking for a new one.
+ i = UContext->uc_index;
+
+ for (;;) {
+ while (CurrentAO != NULL) {
+ if (CurrentAO->ao_prot == PROTOCOL_UDP)
+ break;
+ else
+ CurrentAO = CurrentAO->ao_next;
+ }
+
+ if (CurrentAO != NULL)
+ break; // Get out of for (;;) loop.
+
+ CTEAssert(CurrentAO == NULL);
+
+ // Didn't find one on this chain. Walk down the table, looking
+ // for the next one.
+ while (++i < AO_TABLE_SIZE) {
+ if (AddrObjTable[i] != NULL) {
+ CurrentAO = AddrObjTable[i];
+ break; // Out of while loop.
+ }
+ }
+
+ if (i == AO_TABLE_SIZE)
+ break; // Out of for (;;) loop.
+ }
+
+ // If we found one, return it.
+ if (CurrentAO != NULL) {
+ UContext->uc_ao = CurrentAO;
+ UContext->uc_index = i;
+ return TRUE;
+ } else {
+ UContext->uc_index = 0;
+ UContext->uc_ao = NULL;
+ return FALSE;
+ }
+ }
+
+}
+
+//* ValidateAOContext - Validate the context for reading the AddrObj table.
+//
+// Called to start reading the AddrObj table sequentially. We take in
+// a context, and if the values are 0 we return information about the
+// first AddrObj in the table. Otherwise we make sure that the context value
+// is valid, and if it is we return TRUE.
+// We assume the caller holds the AddrObjTable lock.
+//
+// Input: Context - Pointer to a UDPContext.
+// Valid - Where to return information about context being
+// valid.
+//
+// Returns: TRUE if data in table, FALSE if not. *Valid set to true if the
+// context is valid.
+//
+uint
+ValidateAOContext(void *Context, uint *Valid)
+{
+ UDPContext *UContext = (UDPContext *)Context;
+ uint i;
+ AddrObj *TargetAO;
+ AddrObj *CurrentAO;
+
+ i = UContext->uc_index;
+ TargetAO = UContext->uc_ao;
+
+ // If the context values are 0 and NULL, we're starting from the beginning.
+ if (i == 0 && TargetAO == NULL) {
+ *Valid = TRUE;
+ do {
+ if ((CurrentAO = AddrObjTable[i]) != NULL) {
+ CTEStructAssert(CurrentAO, ao);
+ while (CurrentAO != NULL && CurrentAO->ao_prot != PROTOCOL_UDP)
+ CurrentAO = CurrentAO->ao_next;
+
+ if (CurrentAO != NULL)
+ break;
+ }
+ i++;
+ } while (i < AO_TABLE_SIZE);
+
+ if (CurrentAO != NULL) {
+ UContext->uc_index = i;
+ UContext->uc_ao = CurrentAO;
+ return TRUE;
+ } else
+ return FALSE;
+
+ } else {
+
+ // We've been given a context. We just need to make sure that it's
+ // valid.
+
+ if (i < AO_TABLE_SIZE) {
+ CurrentAO = AddrObjTable[i];
+ while (CurrentAO != NULL) {
+ if (CurrentAO == TargetAO) {
+ if (CurrentAO->ao_prot == PROTOCOL_UDP) {
+ *Valid = TRUE;
+ return TRUE;
+ }
+ break;
+ } else {
+ CurrentAO = CurrentAO->ao_next;
+ }
+ }
+
+ }
+
+ // If we get here, we didn't find the matching AddrObj.
+ *Valid = FALSE;
+ return FALSE;
+
+ }
+
+}
+
+
+//** GetAddrObj - Find a local address object.
+//
+// This is the local address object lookup routine. We take as input the local
+// address and port and a pointer to a 'previous' address object. The hash
+// table entries in each bucket are sorted in order of increasing address, and
+// we skip over any object that has an address lower than the 'previous'
+// address. To get the first address object, pass in a previous value of NULL.
+//
+// We assume that the table lock is held while we're in this routine. We don't
+// take each object lock, since the local address and port can't change while
+// the entry is in the table and the table lock is held so nothing can be
+// inserted or deleted.
+//
+// Input: LocalAddr - Local IP address of object to find (may be NULL);
+// LocalPort - Local port of object to find.
+// Protocol - Protocol to find.
+// PreviousAO - Pointer to last address object found.
+//
+// Returns: A pointer to the Address object, or NULL if none.
+//
+AddrObj *
+GetAddrObj(IPAddr LocalAddr, ushort LocalPort, uchar Protocol,
+ AddrObj *PreviousAO)
+{
+ AddrObj *CurrentAO; // Current address object we're examining.
+
+
+#ifdef DEBUG
+ if (PreviousAO != NULL)
+ CTEStructAssert(PreviousAO, ao);
+#endif
+
+#if 0
+ //
+ // Check our 1-element cache for a match
+ //
+ if ((PreviousAO == NULL) && (LastAO != NULL)) {
+ CTEStructAssert(LastAO, ao);
+ if ( (LastAO->ao_prot == Protocol) &&
+ IP_ADDR_EQUAL(LastAO->ao_addr, LocalAddr) &&
+ (LastAO->ao_port == LocalPort)
+ )
+ {
+ return LastAO;
+ }
+ }
+#endif
+
+ // Find the appropriate bucket in the hash table, and search for a match.
+ // If we don't find one the first time through, we'll try again with a
+ // wildcard local address.
+
+ for (;;) {
+
+ CurrentAO = AddrObjTable[AO_HASH(LocalAddr, LocalPort)];
+ // While we haven't hit the end of the list, examine each element.
+
+ while (CurrentAO != NULL) {
+
+ CTEStructAssert(CurrentAO, ao);
+
+ // If the current one is greater than one we were given, check it.
+ //
+ // #62710: Return only valid AO's since we might have stale AO's lying
+ // around.
+ //
+ if ((CurrentAO > PreviousAO) &&
+ (AO_VALID(CurrentAO))) {
+ if (!(CurrentAO->ao_flags & AO_RAW_FLAG)) {
+ if ( IP_ADDR_EQUAL(CurrentAO->ao_addr, LocalAddr) &&
+ (CurrentAO->ao_port == LocalPort) &&
+ (CurrentAO->ao_prot == Protocol)
+ )
+ {
+ LastAO = CurrentAO;
+ return CurrentAO;
+ }
+ }
+ else {
+ if ( (Protocol != PROTOCOL_UDP)
+#ifndef UDP_ONLY
+ && (Protocol != PROTOCOL_TCP)
+#endif
+ )
+ {
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE((
+ "matching <p, a> <%u, %lx> ao %lx <%u, %lx>\n",
+ Protocol, LocalAddr, CurrentAO,
+ CurrentAO->ao_prot, CurrentAO->ao_addr
+ ));
+
+ }
+
+ if ( IP_ADDR_EQUAL(CurrentAO->ao_addr, LocalAddr) &&
+ ( (CurrentAO->ao_prot == Protocol) ||
+ (CurrentAO->ao_prot == 0)
+ )
+ )
+ {
+ LastAO = CurrentAO;
+ return CurrentAO;
+ }
+ }
+ }
+ }
+ // Either it was less than the previous one, or they didn't match.
+ CurrentAO = CurrentAO->ao_next;
+ }
+ // When we get here, we've hit the end of the list we were examining.
+ // If we weren't examining a wildcard address, look for a wild card
+ // address.
+ if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) {
+ LocalAddr = NULL_IP_ADDR;
+ PreviousAO = NULL;
+ } else
+ return NULL; // We looked for a wildcard and couldn't find
+ // one, so fail.
+ }
+}
+
+
+//* GetNextAddrObj - Get the next address object in a sequential search.
+//
+// This is the 'get next' routine, called when we are reading the address
+// object table sequentially. We pull the appropriate parameters from the
+// search context, call GetAddrObj, and update the search context with what
+// we find. This routine assumes the AddrObjTableLock is held by the caller.
+//
+// Input: SearchContext - Pointer to seach context for search taking place.
+//
+// Returns: Pointer to AddrObj, or NULL if search failed.
+//
+AddrObj *
+GetNextAddrObj(AOSearchContext *SearchContext)
+{
+ AddrObj *FoundAO; // Pointer to the address object we found.
+
+ CTEAssert(SearchContext != NULL);
+
+ // Try and find a match.
+ FoundAO = GetAddrObj(SearchContext->asc_addr, SearchContext->asc_port,
+ SearchContext->asc_prot, SearchContext->asc_previous);
+
+ // Found a match. Update the search context for next time.
+ if (FoundAO != NULL) {
+ SearchContext->asc_previous = FoundAO;
+ SearchContext->asc_addr = FoundAO->ao_addr;
+ // Don't bother to update port or protocol, they don't change.
+ }
+ return FoundAO;
+}
+
+//* GetFirstAddrObj - Get the first matching address object.
+//
+// The routine called to start a sequential read of the AddrObj table. We
+// initialize the provided search context and then call GetNextAddrObj to do
+// the actual read. We assume that the AddrObjTableLock is held by the caller.
+//
+// Input: LocalAddr - Local IP address of object to be found.
+// LocalPort - Local port of AO to be found.
+// Protocol - Protocol to be found.
+// SearchContext - Pointer to search context to be used during
+// search.
+//
+// Returns: Pointer to AO found, or NULL if we couldn't find any.
+//
+AddrObj *
+GetFirstAddrObj(IPAddr LocalAddr, ushort LocalPort, uchar Protocol,
+ AOSearchContext *SearchContext)
+{
+ CTEAssert(SearchContext != NULL);
+
+ // Fill in the search context.
+ SearchContext->asc_previous = NULL; // Haven't found one yet.
+ SearchContext->asc_addr = LocalAddr;
+ SearchContext->asc_port = LocalPort;
+ SearchContext->asc_prot = Protocol;
+ return GetNextAddrObj(SearchContext);
+}
+
+//* InsertAddrObj - Insert an address object into the AddrObj table.
+//
+// Called to insert an AO into the table, assuming the table lock is held. We
+// hash on the addr and port, and then insert in into the correct place
+// (sorted by address of the objects).
+//
+// Input: NewAO - Pointer to AddrObj to be inserted.
+//
+// Returns: Nothing.
+//
+void
+InsertAddrObj(AddrObj *NewAO)
+{
+ AddrObj *PrevAO; // Pointer to previous address object in hash
+ // chain.
+ AddrObj *CurrentAO; // Pointer to current AO in table.
+
+ CTEStructAssert(NewAO, ao);
+
+ PrevAO = STRUCT_OF(AddrObj,
+ &AddrObjTable[AO_HASH(NewAO->ao_addr, NewAO->ao_port)], ao_next);
+ CurrentAO = PrevAO->ao_next;
+
+ // Loop through the chain until we hit the end or until we find an entry
+ // whose address is greater than ours.
+
+ while (CurrentAO != NULL) {
+
+ CTEStructAssert(CurrentAO, ao);
+ CTEAssert(CurrentAO != NewAO); // Debug check to make sure we aren't
+ // inserting the same entry.
+ if (NewAO < CurrentAO)
+ break;
+ PrevAO = CurrentAO;
+ CurrentAO = CurrentAO->ao_next;
+ }
+
+ // At this point, PrevAO points to the AO before the new one. Insert it
+ // there.
+ CTEAssert(PrevAO != NULL);
+ CTEAssert(PrevAO->ao_next == CurrentAO);
+
+ NewAO->ao_next = CurrentAO;
+ PrevAO->ao_next = NewAO;
+ if (NewAO->ao_prot == PROTOCOL_UDP)
+ UStats.us_numaddrs++;
+}
+
+//* RemoveAddrObj - Remove an address object from the table.
+//
+// Called when we need to remove an address object from the table. We hash on
+// the addr and port, then walk the table looking for the object. We assume
+// that the table lock is held.
+//
+// The AddrObj may have already been removed from the table if it was
+// invalidated for some reason, so we need to check for the case of not
+// finding it.
+//
+// Input: DeletedAO - AddrObj to delete.
+//
+// Returns: Nothing.
+//
+void
+RemoveAddrObj(AddrObj *RemovedAO)
+{
+ AddrObj *PrevAO; // Pointer to previous address object in hash
+ // chain.
+ AddrObj *CurrentAO; // Pointer to current AO in table.
+
+ CTEStructAssert(RemovedAO, ao);
+
+ PrevAO = STRUCT_OF(AddrObj,
+ &AddrObjTable[AO_HASH(RemovedAO->ao_addr, RemovedAO->ao_port)],
+ ao_next);
+ CurrentAO = PrevAO->ao_next;
+
+ // Walk the table, looking for a match.
+ while (CurrentAO != NULL) {
+ CTEStructAssert(CurrentAO, ao);
+
+ if (CurrentAO == RemovedAO) {
+ PrevAO->ao_next = CurrentAO->ao_next;
+ if (CurrentAO->ao_prot == PROTOCOL_UDP) {
+ UStats.us_numaddrs--;
+ }
+ if (CurrentAO == LastAO) {
+ LastAO = NULL;
+ }
+ return;
+ } else {
+ PrevAO = CurrentAO;
+ CurrentAO = CurrentAO->ao_next;
+ }
+ }
+
+ // If we get here, we didn't find him. This is OK, but we should say
+ // something about it.
+ CTEPrint("RemoveAddrObj: Object not found.\r\n");
+}
+
+//* FindAnyAddrObj - Find an AO with matching port on any local address.
+//
+// Called for wildcard address opens. We go through the entire addrobj table,
+// and see if anyone has the specified port. We assume that the lock is
+// already held on the table.
+//
+// Input: Port - Port to be looked for.
+// Protocol - Protocol on which to look.
+//
+// Returns: Pointer to AO found, or NULL is noone has it.
+//
+AddrObj *
+FindAnyAddrObj(ushort Port, uchar Protocol)
+{
+ int i; // Index variable.
+ AddrObj *CurrentAO; // Current AddrObj being examined.
+
+ for (i = 0; i < AO_TABLE_SIZE; i++) {
+ CurrentAO = AddrObjTable[i];
+ while (CurrentAO != NULL) {
+ CTEStructAssert(CurrentAO, ao);
+
+ if (CurrentAO->ao_port == Port && CurrentAO->ao_prot == Protocol)
+ return CurrentAO;
+ else
+ CurrentAO = CurrentAO->ao_next;
+ }
+ }
+
+ return NULL;
+
+}
+
+//* GetAddress - Get an IP address and port from a TDI address structure.
+//
+// Called when we need to get our addressing information from a TDI
+// address structure. We go through the structure, and return what we
+// find.
+//
+// Input: AddrList - Pointer to TRANSPORT_ADDRESS structure to search.
+// Addr - Pointer to where to return IP address.
+// Port - Pointer to where to return Port.
+//
+// Return: TRUE if we find an address, FALSE if we don't.
+//
+uchar
+GetAddress(TRANSPORT_ADDRESS UNALIGNED *AddrList, IPAddr *Addr, ushort *Port)
+{
+ int i; // Index variable.
+ TA_ADDRESS UNALIGNED *CurrentAddr; // Address we're examining and may use.
+
+ // First, verify that someplace in Address is an address we can use.
+ CurrentAddr = (TA_ADDRESS UNALIGNED *)AddrList->Address;
+
+ for (i = 0; i < AddrList->TAAddressCount; i++) {
+ if (CurrentAddr->AddressType == TDI_ADDRESS_TYPE_IP) {
+ if (CurrentAddr->AddressLength >= TDI_ADDRESS_LENGTH_IP) {
+ TDI_ADDRESS_IP UNALIGNED *ValidAddr =
+ (TDI_ADDRESS_IP UNALIGNED *)CurrentAddr->Address;
+
+ *Port = ValidAddr->sin_port;
+ *Addr = ValidAddr->in_addr;
+ return TRUE;
+
+ } else
+ return FALSE; // Wrong length for address.
+ } else
+ CurrentAddr = (TA_ADDRESS UNALIGNED *)(CurrentAddr->Address +
+ CurrentAddr->AddressLength);
+ }
+
+ return FALSE; // Didn't find a match.
+
+
+}
+
+//* InvalidateAddrs - Invalidate all AOs for a specific address.
+//
+// Called when we need to invalidate all AOs for a specific address. Walk
+// down the table with the lock held, and take the lock on each AddrObj.
+// If the address matches, mark it as invalid, pull off all requests,
+// and continue. At the end we'll complete all requests with an error.
+//
+// Input: Addr - Addr to be invalidated.
+//
+// Returns: Nothing.
+//
+void
+InvalidateAddrs(IPAddr Addr)
+{
+ Queue SendQ;
+ Queue RcvQ;
+ AORequest *ReqList;
+ CTELockHandle TableHandle, AOHandle;
+ uint i;
+ AddrObj *AO;
+ DGSendReq *SendReq;
+ DGRcvReq *RcvReq;
+ AOMCastAddr *MA;
+
+ INITQ(&SendQ);
+ INITQ(&RcvQ);
+ ReqList = NULL;
+
+ CTEGetLock(&AddrObjTableLock, &TableHandle);
+ for (i = 0; i < AO_TABLE_SIZE; i++) {
+ // Walk down each hash bucket, looking for a match.
+ AO = AddrObjTable[i];
+ while (AO != NULL) {
+ CTEStructAssert(AO, ao);
+
+ CTEGetLock(&AO->ao_lock, &AOHandle);
+ if (IP_ADDR_EQUAL(AO->ao_addr, Addr) && AO_VALID(AO)) {
+ // This one matches. Mark as invalid, then pull his requests.
+ SET_AO_INVALID(AO);
+
+ // Free any IP options we have.
+ (*LocalNetInfo.ipi_freeopts)(&AO->ao_opt);
+
+ // If he has a request on him, pull him off.
+ if (AO->ao_request != NULL) {
+ AORequest *Temp;
+
+ Temp = STRUCT_OF(AORequest, &AO->ao_request, aor_next);
+ do {
+ Temp = Temp->aor_next;
+ } while (Temp->aor_next != NULL);
+
+ Temp->aor_next = ReqList;
+ ReqList = AO->ao_request;
+ AO->ao_request = NULL;
+ }
+
+ // Go down his send list, pulling things off the send q and
+ // putting them on our local queue.
+ while (!EMPTYQ(&AO->ao_sendq)) {
+ DEQUEUE(&AO->ao_sendq, SendReq, DGSendReq, dsr_q);
+ CTEStructAssert(SendReq, dsr);
+ ENQUEUE(&SendQ, &SendReq->dsr_q);
+ }
+
+ // Do the same for the receive queue.
+ while (!EMPTYQ(&AO->ao_rcvq)) {
+ DEQUEUE(&AO->ao_rcvq, RcvReq, DGRcvReq, drr_q);
+ CTEStructAssert(RcvReq, drr);
+ ENQUEUE(&RcvQ, &RcvReq->drr_q);
+ }
+
+ // Free any multicast addresses he may have. IP will have
+ // deleted them at that level before we get here, so all we need
+ // to do if free the memory.
+ MA = AO->ao_mcastlist;
+ while (MA != NULL) {
+ AOMCastAddr *Temp;
+
+ Temp = MA;
+ MA = MA->ama_next;
+ CTEFreeMem(Temp);
+ }
+ AO->ao_mcastlist = NULL;
+
+ }
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+ AO = AO->ao_next; // Go to the next one.
+ }
+ }
+ CTEFreeLock(&AddrObjTableLock, TableHandle);
+
+ // OK, now walk what we've collected, complete it, and free it.
+ while (ReqList != NULL) {
+ AORequest *Req;
+
+ Req = ReqList;
+ ReqList = Req->aor_next;
+ (*Req->aor_rtn)(Req->aor_context, (uint) TDI_ADDR_INVALID, 0);
+ FreeAORequest(Req);
+ }
+
+ // Walk down the rcv. q, completing and freeing requests.
+ while (!EMPTYQ(&RcvQ)) {
+
+ DEQUEUE(&RcvQ, RcvReq, DGRcvReq, drr_q);
+ CTEStructAssert(RcvReq, drr);
+
+ (*RcvReq->drr_rtn)(RcvReq->drr_context, (uint) TDI_ADDR_INVALID, 0);
+
+ FreeDGRcvReq(RcvReq);
+
+ }
+
+ // Now do the same for sends.
+ while (!EMPTYQ(&SendQ)) {
+
+ DEQUEUE(&SendQ, SendReq, DGSendReq, dsr_q);
+ CTEStructAssert(SendReq, dsr);
+
+ (*SendReq->dsr_rtn)(SendReq->dsr_context, (uint) TDI_ADDR_INVALID, 0);
+
+ CTEGetLock(&DGSendReqLock, &TableHandle);
+ if (SendReq->dsr_header != NULL)
+ FreeDGHeader(SendReq->dsr_header);
+ FreeDGSendReq(SendReq);
+ CTEFreeLock(&DGSendReqLock, TableHandle);
+
+ }
+}
+
+//* RequestEventProc - Handle a deferred request event.
+//
+// Called when the event scheduled by DelayDerefAO is called.
+// We just call ProcessAORequest.
+//
+// Input: Event - Event that fired.
+// Context - Pointer to AddrObj.
+//
+// Returns: Nothing.
+//
+void
+RequestEventProc(CTEEvent *Event, void *Context)
+{
+ AddrObj *AO = (AddrObj *)Context;
+
+ CTEStructAssert(AO, ao);
+ CTEAssert(AO_BUSY(AO));
+
+ ProcessAORequests(AO);
+
+}
+
+//* GetAddrOptions - Get the address options.
+//
+// Called when we're opening an address. We take in a pointer, and walk
+// down it looking for address options we know about.
+//
+// Input: Ptr - Ptr to search.
+// Reuse - Pointer to reuse variable.
+// DHCPAddr - Pointer to DHCP addr.
+//
+// Returns: Nothing.
+//
+void
+GetAddrOptions(void *Ptr, uchar *Reuse, uchar *DHCPAddr)
+{
+ uchar *OptPtr;
+
+ *Reuse = 0;
+ *DHCPAddr = 0;
+
+ if (Ptr == NULL)
+ return;
+
+ OptPtr = (uchar *)Ptr;
+
+ while (*OptPtr != TDI_OPTION_EOL) {
+ if (*OptPtr == TDI_ADDRESS_OPTION_REUSE)
+ *Reuse = 1;
+ else
+ if (*OptPtr == TDI_ADDRESS_OPTION_DHCP)
+ *DHCPAddr = 1;
+
+ OptPtr++;
+ }
+
+}
+
+//* TdiOpenAddress - Open a TDI address object.
+//
+// This is the external interface to open an address. The caller provides a
+// TDI_REQUEST structure and a TRANSPORT_ADDRESS structure, as well a pointer
+// to a variable identifying whether or not we are to allow reuse of an
+// address while it's still open.
+//
+// Input: Request - Pointer to a TDI request structure for this request.
+// AddrList - Pointer to TRANSPORT_ADDRESS structure describing
+// address to be opened.
+// Protocol - Protocol on which to open the address. Only the
+// least significant byte is used.
+// Ptr - Pointer to option buffer.
+//
+// Returns: TDI_STATUS code of attempt.
+//
+TDI_STATUS
+TdiOpenAddress(PTDI_REQUEST Request, TRANSPORT_ADDRESS UNALIGNED *AddrList,
+ uint Protocol, void *Ptr)
+{
+ uint i; // Index variable
+ ushort Port; // Local Port we'll use.
+ IPAddr LocalAddr; // Actual address we'll use.
+ AddrObj *NewAO; // New AO we'll use.
+ AddrObj *ExistingAO; // Pointer to existing AO, if any.
+ CTELockHandle Handle;
+ uchar Reuse, DHCPAddr;
+
+
+ if (!GetAddress(AddrList, &LocalAddr, &Port))
+ return TDI_BAD_ADDR;
+
+ // Find the address options we might need.
+ GetAddrOptions(Ptr, &Reuse, &DHCPAddr);
+
+ // Allocate the new addr obj now, assuming that
+ // we need it, so we don't have to do it with locks held later.
+ NewAO = CTEAllocMem(sizeof(AddrObj));
+
+ if (NewAO != NULL) {
+#ifdef VXD
+ uint NewAOIndex;
+#endif
+ CTEMemSet(NewAO, 0, sizeof(AddrObj));
+
+ // Check to make sure IP address is one of our local addresses. This
+ // is protected with the address table lock, so we can interlock an IP
+ // address going away through DHCP.
+ CTEGetLock(&AddrObjTableLock, &Handle);
+
+ if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) { // Not a wildcard.
+
+ // Call IP to find out if this is a local address.
+
+ if ((*LocalNetInfo.ipi_getaddrtype)(LocalAddr) != DEST_LOCAL) {
+ // Not a local address. Fail the request.
+ CTEFreeLock(&AddrObjTableLock, Handle);
+ CTEFreeMem(NewAO);
+ return TDI_BAD_ADDR;
+ }
+ }
+
+ // The specified IP address is a valid local address. Now we do
+ // protocol-specific processing.
+
+ switch (Protocol) {
+
+#ifndef UDP_ONLY
+ case PROTOCOL_TCP:
+#endif
+ case PROTOCOL_UDP:
+
+ // If no port is specified we have to assign one. If there is a
+ // port specified, we need to make sure that the IPAddress/Port
+ // combo isn't already open (unless Reuse is specified). If the
+ // input address is a wildcard, we need to make sure the address
+ // isn't open on any local ip address.
+
+ if (Port == WILDCARD_PORT) { // Have a wildcard port, need to assign an
+ // address.
+ Port = NextUserPort;
+
+ for (i = 0; i < NUM_USER_PORTS; i++, Port++) {
+ ushort NetPort; // Port in net byte order.
+
+ if (Port > MaxUserPort)
+ Port = MIN_USER_PORT;
+
+ NetPort = net_short(Port);
+
+ if (IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) // Wildcard IP
+ // address.
+ ExistingAO = FindAnyAddrObj(NetPort, (uchar)Protocol);
+ else
+ ExistingAO = GetBestAddrObj(LocalAddr, NetPort, (uchar)Protocol);
+
+ if (ExistingAO == NULL)
+ break; // Found an unused port.
+ }
+
+ if (i == NUM_USER_PORTS) { // Couldn't find a free port.
+ CTEFreeLock(&AddrObjTableLock, Handle);
+ CTEFreeMem(NewAO);
+ return TDI_NO_FREE_ADDR;
+ }
+ NextUserPort = Port + 1;
+ Port = net_short(Port);
+ } else { // Address was specificed
+
+ // Don't check if a DHCP address is specified.
+ if (!DHCPAddr) {
+ if (IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) // Wildcard IP
+ ExistingAO = FindAnyAddrObj(Port, (uchar)Protocol); // address.
+ else
+ ExistingAO = GetBestAddrObj(LocalAddr, Port, (uchar)Protocol);
+
+ if (ExistingAO != NULL) { // We already have this address open.
+ // If the caller hasn't asked for Reuse, fail the request.
+ if (!Reuse) {
+ CTEFreeLock(&AddrObjTableLock, Handle);
+ CTEFreeMem(NewAO);
+ return TDI_ADDR_IN_USE;
+ }
+ }
+ }
+ }
+
+ //
+ // We have a new AO. Set up the protocol specific portions
+ //
+ if (Protocol == PROTOCOL_UDP) {
+ NewAO->ao_dgsend = UDPSend;
+ NewAO->ao_maxdgsize = 0xFFFF - sizeof(UDPHeader);
+ }
+
+ SET_AO_XSUM(NewAO); // Checksumming defaults to on.
+
+ break;
+ // end case TCP & UDP
+
+ default:
+ //
+ // All other protocols are opened over Raw IP. For now we don't
+ // do any duplicate checks.
+ //
+
+ CTEAssert(!DHCPAddr);
+
+ //
+ // We must set the port to zero. This puts all the raw sockets
+ // in one hash bucket, which is necessary for GetAddrObj to
+ // work correctly. It wouldn't be a bad idea to come up with
+ // a better scheme...
+ //
+ Port = 0;
+ NewAO->ao_dgsend = RawSend;
+ NewAO->ao_maxdgsize = 0xFFFF;
+ NewAO->ao_flags |= AO_RAW_FLAG;
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("raw open protocol %u AO %lx\n", Protocol, NewAO));
+ }
+ break;
+ }
+
+ // When we get here, we know we're creating a brand new address object.
+ // Port contains the port in question, and NewAO points to the newly
+ // created AO.
+
+ (*LocalNetInfo.ipi_initopts)(&NewAO->ao_opt);
+
+ (*LocalNetInfo.ipi_initopts)(&NewAO->ao_mcastopt);
+
+ NewAO->ao_mcastopt.ioi_ttl = 1;
+ NewAO->ao_mcastaddr = NULL_IP_ADDR;
+
+ CTEInitLock(&NewAO->ao_lock);
+ CTEInitEvent(&NewAO->ao_event, RequestEventProc);
+ INITQ(&NewAO->ao_sendq);
+ INITQ(&NewAO->ao_rcvq);
+ INITQ(&NewAO->ao_activeq);
+ INITQ(&NewAO->ao_idleq);
+ INITQ(&NewAO->ao_listenq);
+ NewAO->ao_port = Port;
+ NewAO->ao_addr = LocalAddr;
+ NewAO->ao_prot = (uchar)Protocol;
+#ifdef DEBUG
+ NewAO->ao_sig = ao_signature;
+#endif
+ NewAO->ao_flags |= AO_VALID_FLAG; // AO is valid.
+
+ if (DHCPAddr)
+ NewAO->ao_flags |= AO_DHCP_FLAG;
+
+#ifdef VXD
+ NewAOIndex = GetAOIndex(NewAO);
+ if (NewAOIndex == INVALID_INDEX) {
+ CTEFreeLock(&AddrObjTableLock, Handle);
+ CTEFreeMem(NewAO);
+ return TDI_NO_RESOURCES;
+ }
+#endif
+
+ InsertAddrObj(NewAO);
+ CTEFreeLock(&AddrObjTableLock, Handle);
+#ifdef VXD
+ Request->Handle.AddressHandle = (PVOID)NewAOIndex;
+#else
+ Request->Handle.AddressHandle = NewAO;
+#endif
+ return TDI_SUCCESS;
+ } else { // Couldn't allocate an address object.
+ return TDI_NO_RESOURCES;
+ }
+
+
+}
+
+//* DeleteAO - Delete an address object.
+//
+// The internal routine to delete an address object. We complete any pending
+// requests with errors, and remove and free the address object.
+//
+// Input: DeletedAO - AddrObj to be deleted.
+//
+// Returns: Nothing.
+//
+void
+DeleteAO(AddrObj *DeletedAO)
+{
+ CTELockHandle TableHandle, AOHandle; // Lock handles we'll use here.
+ CTELockHandle HeaderHandle;
+#ifndef UDP_ONLY
+ CTELockHandle ConnHandle, TCBHandle;
+ TCB *TCBHead = NULL, *CurrentTCB;
+ TCPConn *Conn;
+ Queue *Temp;
+ Queue *CurrentQ;
+#endif
+ AOMCastAddr *AMA;
+
+ CTEStructAssert(DeletedAO, ao);
+ CTEAssert(!AO_VALID(DeletedAO));
+ CTEAssert(DeletedAO->ao_usecnt == 0);
+
+ CTEGetLock(&AddrObjTableLock, &TableHandle);
+#ifndef UDP_ONLY
+ CTEGetLock(&ConnTableLock, &ConnHandle);
+#endif
+ CTEGetLock(&DGSendReqLock, &HeaderHandle);
+ CTEGetLock(&DeletedAO->ao_lock, &AOHandle);
+
+ // If he's on an oor queue, remove him.
+ if (AO_OOR(DeletedAO))
+ REMOVEQ(&DeletedAO->ao_pendq);
+
+#ifdef VXD
+ (*AOIndex)[DeletedAO->ao_index] = NULL;
+#endif
+
+ RemoveAddrObj(DeletedAO);
+
+#ifndef UDP_ONLY
+ // Walk down the list of associated connections and zap their AO pointers.
+ // For each connection, we need to shut down the connection if it's active.
+ // If the connection isn't already closing, we'll put a reference on it
+ // so that it can't go away while we're dealing with the AO, and put it
+ // on a list. On our way out we'll walk down that list and zap each
+ // connection.
+ CurrentQ = &DeletedAO->ao_activeq;
+
+ for (;;) {
+ Temp = QHEAD(CurrentQ);
+ while (Temp != QEND(CurrentQ)) {
+ Conn = QSTRUCT(TCPConn, Temp, tc_q);
+
+ CTEStructAssert(Conn, tc);
+ CurrentTCB = Conn->tc_tcb;
+ if (CurrentTCB != NULL) {
+ // We have a TCB.
+ CTEStructAssert(CurrentTCB, tcb);
+ CTEGetLock(&CurrentTCB->tcb_lock, &TCBHandle);
+ if (CurrentTCB->tcb_state != TCB_CLOSED && !CLOSING(CurrentTCB)) {
+ // It's not closing. Put a reference on it and save it on the
+ // list.
+ CurrentTCB->tcb_refcnt++;
+ CurrentTCB->tcb_aonext = TCBHead;
+ TCBHead = CurrentTCB;
+ }
+ CurrentTCB->tcb_conn = NULL;
+ CurrentTCB->tcb_rcvind = NULL;
+ CTEFreeLock(&CurrentTCB->tcb_lock, TCBHandle);
+ }
+
+ // Destroy the pointers to the TCB and the AO.
+ Conn->tc_ao = NULL;
+ Conn->tc_tcb = NULL;
+ Temp = QNEXT(Temp);
+ }
+
+ if (CurrentQ == &DeletedAO->ao_activeq) {
+ CurrentQ = &DeletedAO->ao_idleq;
+ } else if (CurrentQ == &DeletedAO->ao_idleq) {
+ CurrentQ = &DeletedAO->ao_listenq;
+ } else {
+ CTEAssert(CurrentQ == &DeletedAO->ao_listenq);
+ break;
+ }
+ }
+#endif
+
+ // We've removed him from the queues, and he's marked as invalid. Return
+ // pending requests with errors.
+
+#ifndef UDP_ONLY
+ CTEFreeLock(&DGSendReqLock, AOHandle);
+ CTEFreeLock(&ConnTableLock, HeaderHandle);
+ CTEFreeLock(&AddrObjTableLock, ConnHandle);
+#else
+ CTEFreeLock(&DGSendReqLock, AOHandle);
+ CTEFreeLock(&AddrObjTableLock, HeaderHandle);
+#endif
+
+ // We still hold the lock on the AddrObj, although this may not be
+ // neccessary.
+
+ while (!EMPTYQ(&DeletedAO->ao_rcvq)) {
+ DGRcvReq *Rcv;
+
+ DEQUEUE(&DeletedAO->ao_rcvq, Rcv, DGRcvReq, drr_q);
+ CTEStructAssert(Rcv, drr);
+
+ CTEFreeLock(&DeletedAO->ao_lock, TableHandle);
+ (*Rcv->drr_rtn)(Rcv->drr_context, (uint) TDI_ADDR_DELETED, 0);
+
+ FreeDGRcvReq(Rcv);
+
+ CTEGetLock(&DeletedAO->ao_lock, &TableHandle);
+ }
+
+ // Now destroy any sends.
+ while (!EMPTYQ(&DeletedAO->ao_sendq)) {
+ DGSendReq *Send;
+
+ DEQUEUE(&DeletedAO->ao_sendq, Send, DGSendReq, dsr_q);
+ CTEStructAssert(Send, dsr);
+
+ CTEFreeLock(&DeletedAO->ao_lock, TableHandle);
+ (*Send->dsr_rtn)(Send->dsr_context, (uint) TDI_ADDR_DELETED, 0);
+
+ CTEGetLock(&DGSendReqLock, &HeaderHandle);
+ if (Send->dsr_header != NULL)
+ FreeDGHeader(Send->dsr_header);
+ FreeDGSendReq(Send);
+ CTEFreeLock(&DGSendReqLock, HeaderHandle);
+
+ CTEGetLock(&DeletedAO->ao_lock, &TableHandle);
+ }
+
+ CTEFreeLock(&DeletedAO->ao_lock, TableHandle);
+
+ // Free any IP options we have.
+ (*LocalNetInfo.ipi_freeopts)(&DeletedAO->ao_opt);
+
+ // Free any associated multicast addresses.
+
+ AMA = DeletedAO->ao_mcastlist;
+ while (AMA != NULL) {
+ AOMCastAddr *Temp;
+
+ (*LocalNetInfo.ipi_setmcastaddr)(AMA->ama_addr, AMA->ama_if, FALSE);
+ Temp = AMA;
+ AMA = AMA->ama_next;
+ CTEFreeMem(Temp);
+ }
+
+ CTEFreeMem(DeletedAO);
+
+#ifndef UDP_ONLY
+ // Now go down the TCB list, and destroy any we need to.
+ CurrentTCB = TCBHead;
+ while (CurrentTCB != NULL) {
+ TCB *NextTCB;
+ CTEGetLock(&CurrentTCB->tcb_lock, &TCBHandle);
+ CurrentTCB->tcb_refcnt--;
+ CurrentTCB->tcb_flags |= NEED_RST; // Make sure we send a RST.
+ NextTCB = CurrentTCB->tcb_aonext;
+ TryToCloseTCB(CurrentTCB, TCB_CLOSE_ABORTED, TCBHandle);
+ CurrentTCB = NextTCB;
+ }
+#endif
+
+
+}
+
+//* GetAORequest - Get an AO request structure.
+//
+// A routine to allocate a request structure from our free list.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to request structure, or NULL if we couldn't get one.
+//
+AORequest *
+GetAORequest()
+{
+ AORequest *NewRequest;
+ CTELockHandle Handle;
+
+ CTEGetLock(&AORequestLock, &Handle);
+
+ NewRequest = AORequestFree;
+ if (NewRequest != NULL) {
+ AORequestFree = (AORequest *)NewRequest->aor_rtn;
+ CTEStructAssert(NewRequest, aor);
+ }
+
+ CTEFreeLock(&AORequestLock, Handle);
+ return NewRequest;
+}
+
+//* FreeAORequest - Free an AO request structure.
+//
+// Called to free an AORequest structure.
+//
+// Input: Request - AORequest structure to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeAORequest(AORequest *Request)
+{
+ CTELockHandle Handle;
+
+ CTEStructAssert(Request, aor);
+
+ CTEGetLock(&AORequestLock, &Handle);
+
+ *(AORequest **)&Request->aor_rtn = AORequestFree;
+ AORequestFree = Request;
+
+ CTEFreeLock(&AORequestLock, Handle);
+}
+
+
+
+//* TDICloseAddress - Close an address.
+//
+// The user API to delete an address. Basically, we destroy the local address
+// object if we can.
+//
+// This routine is interlocked with the AO busy bit - if the busy bit is set,
+// we'll just flag the AO for later deletion.
+//
+// Input: Request - TDI_REQUEST structure for this request.
+//
+// Returns: Status of attempt to delete the address - either pending or
+// success.
+//
+TDI_STATUS
+TdiCloseAddress(PTDI_REQUEST Request)
+{
+ AddrObj *DeletingAO;
+ CTELockHandle AOHandle;
+
+#ifdef VXD
+ DeletingAO = GetIndexedAO((uint)Request->Handle.AddressHandle);
+ if (DeletingAO == NULL)
+ return TDI_ADDR_INVALID;
+#else
+ DeletingAO = Request->Handle.AddressHandle;
+#endif
+
+ CTEStructAssert(DeletingAO, ao);
+
+ CTEGetLock(&DeletingAO->ao_lock, &AOHandle);
+
+ if (!AO_BUSY(DeletingAO) && !(DeletingAO->ao_usecnt)) {
+ SET_AO_BUSY(DeletingAO);
+ SET_AO_INVALID(DeletingAO); // This address object is
+ // deleting.
+ CTEFreeLock(&DeletingAO->ao_lock, AOHandle);
+ DeleteAO(DeletingAO);
+ return TDI_SUCCESS;
+ } else {
+
+ AORequest *NewRequest, *OldRequest;
+ CTEReqCmpltRtn CmpltRtn;
+ PVOID ReqContext;
+ TDI_STATUS Status;
+
+ // Check and see if we already have a delete in progress. If we don't
+ // allocate and link up a delete request structure.
+ if (!AO_REQUEST(DeletingAO, AO_DELETE)) {
+
+ OldRequest = DeletingAO->ao_request;
+
+ NewRequest = GetAORequest();
+
+ if (NewRequest != NULL) { // Got a request.
+ NewRequest->aor_rtn = Request->RequestNotifyObject;
+ NewRequest->aor_context = Request->RequestContext;
+ CLEAR_AO_REQUEST(DeletingAO, AO_OPTIONS); // Clear the option
+ // request,
+ // if there is one.
+ SET_AO_REQUEST(DeletingAO, AO_DELETE);
+ SET_AO_INVALID(DeletingAO); // This address
+ // object is
+ // deleting.
+ DeletingAO->ao_request = NewRequest;
+ NewRequest->aor_next = NULL;
+ CTEFreeLock(&DeletingAO->ao_lock, AOHandle);
+
+ while (OldRequest != NULL) {
+ AORequest *Temp;
+
+ CmpltRtn = OldRequest->aor_rtn;
+ ReqContext = OldRequest->aor_context;
+
+ (*CmpltRtn)(ReqContext, (uint) TDI_ADDR_DELETED, 0);
+ Temp = OldRequest;
+ OldRequest = OldRequest->aor_next;
+ FreeAORequest(Temp);
+ }
+
+ return TDI_PENDING;
+ } else
+ Status = TDI_NO_RESOURCES;
+ } else // Delete already in progress.
+ Status = TDI_ADDR_INVALID;
+
+ CTEFreeLock(&DeletingAO->ao_lock, AOHandle);
+ return Status;
+ }
+
+}
+
+//* FindAOMCastAddr - Find a multicast address on an AddrObj.
+//
+// A utility routine to find a multicast address on an AddrObj. We also return
+// a pointer to it's predecessor, for use in deleting.
+//
+// Input: AO - AddrObj to search.
+// Addr - MCast address to search for.
+// IF - IPAddress of interface
+// PrevAMA - Pointer to where to return predecessor.
+//
+// Returns: Pointer to matching AMA structure, or NULL if there is none.
+//
+AOMCastAddr *
+FindAOMCastAddr(AddrObj *AO, IPAddr Addr, IPAddr IF, AOMCastAddr **PrevAMA)
+{
+ AOMCastAddr *FoundAMA, *Temp;
+
+ Temp = STRUCT_OF(AOMCastAddr, &AO->ao_mcastlist, ama_next);
+ FoundAMA = AO->ao_mcastlist;
+
+ while (FoundAMA != NULL) {
+ if (IP_ADDR_EQUAL(Addr, FoundAMA->ama_addr) &&
+ IP_ADDR_EQUAL(IF, FoundAMA->ama_if))
+ break;
+ Temp = FoundAMA;
+ FoundAMA = FoundAMA->ama_next;
+ }
+
+ *PrevAMA = Temp;
+ return FoundAMA;
+}
+
+//* MCastAddrOnAO - Test to see if a multicast address on an AddrObj.
+//
+// A utility routine to test to see if a multicast address is on an AddrObj.
+//
+// Input: AO - AddrObj to search.
+// Addr - MCast address to search for.
+//
+// Returns: TRUE is Addr is on AO.
+//
+uint
+MCastAddrOnAO(AddrObj *AO, IPAddr Addr)
+{
+ AOMCastAddr *FoundAMA;
+
+ FoundAMA = AO->ao_mcastlist;
+
+ while (FoundAMA != NULL) {
+ if (IP_ADDR_EQUAL(Addr, FoundAMA->ama_addr))
+ return(TRUE);
+ FoundAMA = FoundAMA->ama_next;
+ }
+ return(FALSE);
+}
+
+//* SetAOOptions - Set AddrObj options.
+//
+// The set options worker routine, called when we've validated the buffer
+// and know that the AddrObj isn't busy.
+//
+// Input: OptionAO - AddrObj for which options are being set.
+// Options - AOOption buffer of options.
+//
+// Returns: TDI_STATUS of attempt.
+//
+TDI_STATUS
+SetAOOptions(AddrObj *OptionAO, uint ID, uint Length, uchar *Options)
+{
+ IP_STATUS IPStatus; // Status of IP option set request.
+ CTELockHandle Handle;
+ TDI_STATUS Status;
+ AOMCastAddr *AMA, *PrevAMA;
+
+ CTEAssert(AO_BUSY(OptionAO));
+
+ // First, see if there are IP options.
+
+ if (ID == AO_OPTION_IPOPTIONS) {
+ IF_TCPDBG(TCP_DEBUG_OPTIONS) {
+ TCPTRACE(("processing IP_IOTIONS on AO %lx\n", OptionAO));
+ }
+ // These are IP options. Pass them down.
+ (*LocalNetInfo.ipi_freeopts)(&OptionAO->ao_opt);
+
+ IPStatus = (*LocalNetInfo.ipi_copyopts)(Options, Length,
+ &OptionAO->ao_opt);
+
+ if (IPStatus == IP_SUCCESS)
+ return TDI_SUCCESS;
+ else if (IPStatus == IP_NO_RESOURCES)
+ return TDI_NO_RESOURCES;
+ else
+ return TDI_BAD_OPTION;
+ }
+
+ // These are UDP/TCP options.
+ if (Length == 0)
+ return TDI_BAD_OPTION;
+
+ Status = TDI_SUCCESS;
+ CTEGetLock(&OptionAO->ao_lock, &Handle);
+
+ switch (ID) {
+
+ case AO_OPTION_XSUM:
+ if (Options[0])
+ SET_AO_XSUM(OptionAO);
+ else
+ CLEAR_AO_XSUM(OptionAO);
+ break;
+
+ case AO_OPTION_IP_DONTFRAGMENT:
+ IF_TCPDBG(TCP_DEBUG_OPTIONS) {
+ TCPTRACE((
+ "DF opt %u, initial flags %lx on AO %lx\n",
+ (int) Options[0], OptionAO->ao_opt.ioi_flags, OptionAO
+ ));
+ }
+
+ if (Options[0])
+ OptionAO->ao_opt.ioi_flags |= IP_FLAG_DF;
+ else
+ OptionAO->ao_opt.ioi_flags &= ~IP_FLAG_DF;
+
+ IF_TCPDBG(TCP_DEBUG_OPTIONS) {
+ TCPTRACE((
+ "New flags %lx on AO %lx\n",
+ OptionAO->ao_opt.ioi_flags, OptionAO
+ ));
+ }
+
+ break;
+
+ case AO_OPTION_TTL:
+ IF_TCPDBG(TCP_DEBUG_OPTIONS) {
+ TCPTRACE((
+ "setting TTL to %d on AO %lx\n", Options[0], OptionAO
+ ));
+ }
+ OptionAO->ao_opt.ioi_ttl = Options[0];
+ break;
+
+ case AO_OPTION_TOS:
+ IF_TCPDBG(TCP_DEBUG_OPTIONS) {
+ TCPTRACE((
+ "setting TOS to %d on AO %lx\n", Options[0], OptionAO
+ ));
+ }
+ OptionAO->ao_opt.ioi_tos = Options[0];
+ break;
+
+ case AO_OPTION_MCASTTTL:
+ OptionAO->ao_mcastopt.ioi_ttl = Options[0];
+ break;
+
+ case AO_OPTION_MCASTIF:
+ if (Length >= sizeof(UDPMCastIFReq)) {
+ UDPMCastIFReq *Req;
+ IPAddr Addr;
+
+ Req = (UDPMCastIFReq *)Options;
+ Addr = Req->umi_addr;
+ if (!IP_ADDR_EQUAL(Addr, NULL_IP_ADDR) &&
+ (*LocalNetInfo.ipi_getaddrtype)(Addr) != DEST_LOCAL)
+ {
+ Status = TDI_BAD_OPTION;
+ }
+ else {
+ OptionAO->ao_mcastaddr = Addr;
+ }
+ } else
+ Status = TDI_BAD_OPTION;
+ break;
+
+ case AO_OPTION_ADD_MCAST:
+ case AO_OPTION_DEL_MCAST:
+ if (Length >= sizeof(UDPMCastReq)) {
+ UDPMCastReq *Req = (UDPMCastReq *)Options;
+
+ AMA = FindAOMCastAddr(OptionAO, Req->umr_addr, Req->umr_if,
+ &PrevAMA);
+
+ if (ID == AO_OPTION_ADD_MCAST) {
+ if (AMA != NULL) {
+ Status = TDI_BAD_OPTION;
+ break;
+ }
+ AMA = CTEAllocMem(sizeof(AOMCastAddr));
+ if (AMA == NULL) {
+ // Couldn't get the resource we need.
+ Status = TDI_NO_RESOURCES;
+ break;
+ }
+
+ AMA->ama_next = OptionAO->ao_mcastlist;
+ OptionAO->ao_mcastlist = AMA;
+
+ AMA->ama_addr = Req->umr_addr;
+ AMA->ama_if = Req->umr_if;
+
+ } else {
+ // This is a delete request. Fail it if it's not there.
+ if (AMA == NULL) {
+ Status = TDI_BAD_OPTION;
+ break;
+ }
+
+ PrevAMA->ama_next = AMA->ama_next;
+ CTEFreeMem(AMA);
+ }
+
+ IPStatus = (*LocalNetInfo.ipi_setmcastaddr)(Req->umr_addr,
+ Req->umr_if, ID == AO_OPTION_ADD_MCAST ? TRUE : FALSE);
+
+ if (IPStatus != TDI_SUCCESS) {
+ // Some problem adding or deleting. If we were adding, we
+ // need to free the one we just added.
+ if (ID == AO_OPTION_ADD_MCAST) {
+ AMA = FindAOMCastAddr(OptionAO, Req->umr_addr,
+ Req->umr_if, &PrevAMA);
+ if (AMA != NULL) {
+ PrevAMA->ama_next = AMA->ama_next;
+ CTEFreeMem(AMA);
+ } else
+ DEBUGCHK;
+ }
+
+ Status = (IPStatus == IP_NO_RESOURCES ? TDI_NO_RESOURCES :
+ TDI_BAD_OPTION);
+ }
+
+ } else
+ Status = TDI_BAD_OPTION;
+ break;
+
+ default:
+ Status = TDI_BAD_OPTION;
+ break;
+ }
+
+ CTEFreeLock(&OptionAO->ao_lock, Handle);
+
+ return Status;
+
+}
+
+//* SetAddrOptions - Set options on an address object.
+//
+// Called to set options on an address object. We validate the buffer,
+// and if everything is OK we'll check the status of the AddrObj. If
+// it's OK then we'll set them, otherwise we'll mark it for later use.
+//
+// Input: Request - Request describing AddrObj for option set.
+// ID - ID for option to be set.
+// OptLength - Length of options.
+// Options - Pointer to options.
+//
+// Returns: TDI_STATUS of attempt.
+//
+TDI_STATUS
+SetAddrOptions(PTDI_REQUEST Request, uint ID, uint OptLength, void *Options)
+{
+ AddrObj *OptionAO;
+ TDI_STATUS Status;
+
+ CTELockHandle AOHandle;
+
+#ifdef VXD
+ OptionAO = GetIndexedAO((uint)Request->Handle.AddressHandle);
+ if (OptionAO == NULL)
+ return TDI_ADDR_INVALID;
+#else
+ OptionAO = Request->Handle.AddressHandle;
+#endif
+
+ CTEStructAssert(OptionAO, ao);
+
+ CTEGetLock(&OptionAO->ao_lock, &AOHandle);
+
+ if (AO_VALID(OptionAO)) {
+ if (!AO_BUSY(OptionAO) && OptionAO->ao_usecnt == 0) {
+ SET_AO_BUSY(OptionAO);
+ CTEFreeLock(&OptionAO->ao_lock, AOHandle);
+
+ Status = SetAOOptions(OptionAO, ID, OptLength, Options);
+
+ CTEGetLock(&OptionAO->ao_lock, &AOHandle);
+ if (!AO_PENDING(OptionAO)) {
+ CLEAR_AO_BUSY(OptionAO);
+ CTEFreeLock(&OptionAO->ao_lock, AOHandle);
+ return Status;
+ } else {
+ CTEFreeLock(&OptionAO->ao_lock, AOHandle);
+ ProcessAORequests(OptionAO);
+ return Status;
+ }
+ } else {
+ AORequest *NewRequest, *OldRequest;
+
+ // The AddrObj is busy somehow. We need to get a request, and link
+ // him on the request list.
+
+ NewRequest = GetAORequest();
+
+ if (NewRequest != NULL) { // Got a request.
+ NewRequest->aor_rtn = Request->RequestNotifyObject;
+ NewRequest->aor_context = Request->RequestContext;
+ NewRequest->aor_id = ID;
+ NewRequest->aor_length = OptLength;
+ NewRequest->aor_buffer = Options;
+ SET_AO_REQUEST(OptionAO, AO_OPTIONS); // Set the
+ // option request,
+ OldRequest = STRUCT_OF(AORequest, &OptionAO->ao_request,
+ aor_next);
+
+ while (OldRequest->aor_next != NULL)
+ OldRequest = OldRequest->aor_next;
+
+ OldRequest->aor_next = NewRequest;
+ CTEFreeLock(&OptionAO->ao_lock, AOHandle);
+
+ return TDI_PENDING;
+ } else
+ Status = TDI_NO_RESOURCES;
+
+ }
+ } else
+ Status = TDI_ADDR_INVALID;
+
+ CTEFreeLock(&OptionAO->ao_lock, AOHandle);
+ return Status;
+
+}
+
+//* TDISetEvent - Set a handler for a particular event.
+//
+// This is the user API to set an event. It's pretty simple, we just
+// grab the lock on the AddrObj and fill in the event.
+//
+//
+// Input: Handle - Pointer to address object.
+// Type - Event being set.
+// Handler - Handler to call for event.
+// Context - Context to pass to event.
+//
+// Returns: TDI_SUCCESS if it works, an error if it doesn't. This routine
+// never pends.
+//
+TDI_STATUS
+TdiSetEvent(PVOID Handle, int Type, PVOID Handler, PVOID Context)
+{
+ AddrObj *EventAO;
+ CTELockHandle AOHandle;
+ TDI_STATUS Status;
+
+#ifdef VXD
+ EventAO = GetIndexedAO((uint)Handle);
+
+ CTEStructAssert(EventAO, ao);
+ if (EventAO == NULL || !AO_VALID(EventAO))
+ return TDI_ADDR_INVALID;
+#else
+ EventAO = (AddrObj *)Handle;
+
+ CTEStructAssert(EventAO, ao);
+ if (!AO_VALID(EventAO))
+ return TDI_ADDR_INVALID;
+#endif
+
+
+ CTEGetLock(&EventAO->ao_lock, &AOHandle);
+
+ Status = TDI_SUCCESS;
+ switch (Type) {
+
+ case TDI_EVENT_CONNECT:
+ EventAO->ao_connect = Handler;
+ EventAO->ao_conncontext = Context;
+ break;
+ case TDI_EVENT_DISCONNECT:
+ EventAO->ao_disconnect = Handler;
+ EventAO->ao_disconncontext = Context;
+ break;
+ case TDI_EVENT_ERROR:
+ EventAO->ao_error = Handler;
+ EventAO->ao_errcontext = Context;
+ break;
+ case TDI_EVENT_RECEIVE:
+ EventAO->ao_rcv = Handler;
+ EventAO->ao_rcvcontext = Context;
+ break;
+ case TDI_EVENT_RECEIVE_DATAGRAM:
+ EventAO->ao_rcvdg = Handler;
+ EventAO->ao_rcvdgcontext = Context;
+ break;
+ case TDI_EVENT_RECEIVE_EXPEDITED:
+ EventAO->ao_exprcv = Handler;
+ EventAO->ao_exprcvcontext = Context;
+ break;
+ default:
+ Status = TDI_BAD_EVENT_TYPE;
+ break;
+ }
+
+ CTEFreeLock(&EventAO->ao_lock, AOHandle);
+ return Status;
+
+
+}
+
+//* ProcessAORequests - Process pending requests on an AddrObj.
+//
+// This is the delayed request processing routine, called when we've
+// done something that used the busy bit. We examine the pending
+// requests flags, and dispatch the requests appropriately.
+//
+// Input: RequestAO - AddrObj to be processed.
+//
+// Returns: Nothing.
+//
+void
+ProcessAORequests(AddrObj *RequestAO)
+{
+ CTELockHandle AOHandle;
+ AORequest *Request;
+
+ CTEStructAssert(RequestAO, ao);
+ CTEAssert(AO_BUSY(RequestAO));
+ CTEAssert(RequestAO->ao_usecnt == 0);
+
+ CTEGetLock(&RequestAO->ao_lock, &AOHandle);
+
+ while (AO_PENDING(RequestAO)) {
+ Request = RequestAO->ao_request;
+
+ if (AO_REQUEST(RequestAO, AO_DELETE)) {
+ CTEAssert(Request != NULL);
+ CTEAssert(!AO_REQUEST(RequestAO, AO_OPTIONS));
+ CTEFreeLock(&RequestAO->ao_lock, AOHandle);
+ DeleteAO(RequestAO);
+ (*Request->aor_rtn)(Request->aor_context, TDI_SUCCESS, 0);
+ FreeAORequest(Request);
+ return; // Deleted him, so get out.
+ }
+
+ // Now handle options request.
+ while (AO_REQUEST(RequestAO, AO_OPTIONS)) {
+ TDI_STATUS Status;
+
+ // Have an option request.
+ Request = RequestAO->ao_request;
+ RequestAO->ao_request = Request->aor_next;
+ if (RequestAO->ao_request == NULL)
+ CLEAR_AO_REQUEST(RequestAO, AO_OPTIONS);
+
+ CTEAssert(Request != NULL);
+ CTEFreeLock(&RequestAO->ao_lock, AOHandle);
+
+ Status = SetAOOptions(RequestAO, Request->aor_id,
+ Request->aor_length, Request->aor_buffer);
+ (*Request->aor_rtn)(Request->aor_context, Status, 0);
+ FreeAORequest(Request);
+
+ CTEGetLock(&RequestAO->ao_lock, &AOHandle);
+ }
+
+ // We've done options, now try sends.
+ if (AO_REQUEST(RequestAO, AO_SEND)) {
+ DGSendReq *SendReq;
+
+ // Need to send. Clear the busy flag, bump the send count, and
+ // get the send request.
+ if (!EMPTYQ(&RequestAO->ao_sendq)) {
+ DEQUEUE(&RequestAO->ao_sendq, SendReq, DGSendReq, dsr_q);
+ CLEAR_AO_BUSY(RequestAO);
+ RequestAO->ao_usecnt++;
+ CTEFreeLock(&RequestAO->ao_lock, AOHandle);
+ UDPSend(RequestAO, SendReq);
+ CTEGetLock(&RequestAO->ao_lock, &AOHandle);
+ // If there aren't any other pending sends, set the busy bit.
+ if (!(--RequestAO->ao_usecnt))
+ SET_AO_BUSY(RequestAO);
+ else
+ break; // Still sending, so get out.
+ } else {
+ // Had the send request set, but no send! Odd....
+ DEBUGCHK;
+ CLEAR_AO_REQUEST(RequestAO, AO_SEND);
+ }
+
+ }
+ }
+
+ // We're done here.
+ CLEAR_AO_BUSY(RequestAO);
+ CTEFreeLock(&RequestAO->ao_lock, AOHandle);
+
+}
+
+
+//* DelayDerefAO - Derefrence an AddrObj, and schedule an event.
+//
+// Called when we are done with an address object, and need to
+// derefrence it. We dec the usecount, and if it goes to 0 and
+// if there are pending actions we'll schedule an event to deal
+// with them.
+//
+// Input: RequestAO - AddrObj to be processed.
+//
+// Returns: Nothing.
+//
+void
+DelayDerefAO(AddrObj *RequestAO)
+{
+ CTELockHandle Handle;
+
+ CTEGetLock(&RequestAO->ao_lock, &Handle);
+
+ RequestAO->ao_usecnt--;
+
+ if (!RequestAO->ao_usecnt && !AO_BUSY(RequestAO)) {
+ if (AO_PENDING(RequestAO)) {
+ SET_AO_BUSY(RequestAO);
+ CTEFreeLock(&RequestAO->ao_lock, Handle);
+ CTEScheduleEvent(&RequestAO->ao_event, RequestAO);
+ return;
+ }
+ }
+ CTEFreeLock(&RequestAO->ao_lock, Handle);
+
+}
+
+//* DerefAO - Derefrence an AddrObj.
+//
+// Called when we are done with an address object, and need to
+// derefrence it. We dec the usecount, and if it goes to 0 and
+// if there are pending actions we'll call the process AO handler.
+//
+// Input: RequestAO - AddrObj to be processed.
+//
+// Returns: Nothing.
+//
+void
+DerefAO(AddrObj *RequestAO)
+{
+ CTELockHandle Handle;
+
+ CTEGetLock(&RequestAO->ao_lock, &Handle);
+
+ RequestAO->ao_usecnt--;
+
+ if (!RequestAO->ao_usecnt && !AO_BUSY(RequestAO)) {
+ if (AO_PENDING(RequestAO)) {
+ SET_AO_BUSY(RequestAO);
+ CTEFreeLock(&RequestAO->ao_lock, Handle);
+ ProcessAORequests(RequestAO);
+ return;
+ }
+ }
+
+ CTEFreeLock(&RequestAO->ao_lock, Handle);
+
+}
+
+#pragma BEGIN_INIT
+
+//* InitAddr - Initialize the address object stuff.
+//
+// Called during init time to initalize the address object stuff.
+//
+// Input: Nothing
+//
+// Returns: True if we succeed, False if we fail.
+//
+int
+InitAddr()
+{
+ AORequest *RequestPtr;
+ int i;
+
+ CTEInitLock(&AddrObjTableLock);
+ CTEInitLock(&AORequestLock);
+
+#ifdef VXD
+ AOInstance = 1;
+
+ AOIndexSize = DEFAULT_AO_INDEX_SIZE;
+ NextAOIndex = 0;
+
+ AOIndex = CTEAllocMem(sizeof(AddrObj *) * DEFAULT_AO_INDEX_SIZE);
+ if (AOIndex == NULL)
+ return FALSE;
+
+#endif
+
+ RequestPtr = CTEAllocMem(sizeof(AORequest)*NUM_AO_REQUEST);
+ if (RequestPtr == NULL) {
+#ifdef VXD
+ CTEFreeMem(AOIndex);
+#endif
+ return FALSE;
+ }
+
+ AORequestFree = NULL;
+
+ for (i = 0; i < NUM_AO_REQUEST; i++, RequestPtr++) {
+#ifdef DEBUG
+ RequestPtr->aor_sig = aor_signature;
+#endif
+ FreeAORequest(RequestPtr);
+ }
+
+ for (i = 0; i < AO_TABLE_SIZE; i++)
+ AddrObjTable[i] = NULL;
+
+ LastAO = NULL;
+
+ return TRUE;
+
+}
+#pragma END_INIT
diff --git a/private/ntos/tdi/tcpip/tcp/addr.h b/private/ntos/tdi/tcpip/tcp/addr.h
new file mode 100644
index 000000000..78ec23ce3
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/addr.h
@@ -0,0 +1,211 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** ADDR.H - TDI address object definitions.
+//
+// This file contains the definitions of TDI address objects and related
+// constants and structures.
+
+#define ao_signature 0x20204F41 // 'AO '
+
+#define AO_TABLE_SIZE 16 // Address object hash table size.
+
+#define WILDCARD_PORT 0 // 0 means assign a port.
+
+#define MIN_USER_PORT 1025 // Minimum value for a wildcard port
+#define MAX_USER_PORT 5000 // Maximim value for a user port.
+#define NUM_USER_PORTS (uint)(MaxUserPort - MIN_USER_PORT + 1)
+
+#define NETBT_SESSION_PORT 139
+
+typedef struct AddrObj AddrObj;
+
+//* Datagram transport-specific send function.
+typedef void (*DGSendProc)(AddrObj *SrcAO, void *SendReq);
+
+//* Definition of the structure of an address object. Each object represents
+// a local address, and the IP portion may be a wildcard.
+
+struct AddrObj {
+#ifdef DEBUG
+ ulong ao_sig;
+#endif
+ struct AddrObj *ao_next; // Pointer to next address object in chain.
+ DEFINE_LOCK_STRUCTURE(ao_lock) // Lock for this object.
+ struct AORequest *ao_request;// Pointer to pending request.
+ Queue ao_sendq; // Queue of sends waiting for transmission.
+ Queue ao_pendq; // Linkage for pending queue.
+ Queue ao_rcvq; // Receive queue.
+ IPOptInfo ao_opt; // Opt info for this address object.
+ IPAddr ao_addr; // IP address for this address object.
+ ushort ao_port; // Local port for this address object.
+ ushort ao_flags; // Flags for this object.
+ uchar ao_prot; // Protocol for this AO.
+ uchar ao_index; // Index into table of this AO.
+ uchar ao_pad[2]; // PAD PAD PAD
+ uint ao_listencnt; // Number of listening connections.
+ ushort ao_usecnt; // Count of 'uses' on AO.
+ ushort ao_inst; // 'Instance' number of this AO.
+ IPOptInfo ao_mcastopt;// MCast opt info.
+ IPAddr ao_mcastaddr; // Source address for MCast from
+ // this addr object..
+ Queue ao_activeq; // Queue of active connections.
+ Queue ao_idleq; // Queue of inactive (no TCB) connections.
+ Queue ao_listenq; // Queue of listening connections.
+ CTEEvent ao_event; // Event to use for this AO.
+ PConnectEvent ao_connect; // Connect event handle.
+ PVOID ao_conncontext; // Receive DG context.
+ PDisconnectEvent ao_disconnect; // Disconnect event routine.
+ PVOID ao_disconncontext; // Disconnect event context.
+ PErrorEvent ao_error; // Error event routine.
+ PVOID ao_errcontext; // Error event context.
+ PRcvEvent ao_rcv; // Receive event handler
+ PVOID ao_rcvcontext; // Receive context.
+ PRcvDGEvent ao_rcvdg; // Receive DG event handler
+ PVOID ao_rcvdgcontext; // Receive DG context.
+ PRcvEvent ao_exprcv; // Expedited receive event handler
+ PVOID ao_exprcvcontext; // Expedited receive context.
+ struct AOMCastAddr *ao_mcastlist; // List of active multicast
+ // addresses.
+ DGSendProc ao_dgsend; // Datagram transport send function.
+ ushort ao_maxdgsize; // maximum user datagram size.
+#ifdef SYN_ATTACK
+ BOOLEAN ConnLimitReached; //set when there are no
+ //connections left
+#endif
+}; /* AddrObj */
+
+typedef struct AddrObj AddrObj;
+
+#define AO_RAW_FLAG 0x0200 // AO is for a raw endpoint.
+#define AO_DHCP_FLAG 0x0100 // AO is bound to real 0 address.
+
+#define AO_VALID_FLAG 0x0080 // AddrObj is valid.
+#define AO_BUSY_FLAG 0x0040 // AddrObj is busy (i.e., has it
+ // exclusive).
+#define AO_OOR_FLAG 0x0020 // AddrObj is out of resources, and on
+ // either the pending or delayed queue.
+#define AO_QUEUED_FLAG 0x0010 // AddrObj is on the pending queue.
+
+
+#define AO_XSUM_FLAG 0x0008 // Xsums are used on this AO.
+#define AO_SEND_FLAG 0x0004 // Send is pending.
+#define AO_OPTIONS_FLAG 0x0002 // Option set pending.
+#define AO_DELETE_FLAG 0x0001 // Delete pending.
+
+
+#define AO_VALID(A) ((A)->ao_flags & AO_VALID_FLAG)
+#define SET_AO_INVALID(A) (A)->ao_flags &= ~AO_VALID_FLAG
+
+#define AO_BUSY(A) ((A)->ao_flags & AO_BUSY_FLAG)
+#define SET_AO_BUSY(A) (A)->ao_flags |= AO_BUSY_FLAG
+#define CLEAR_AO_BUSY(A) (A)->ao_flags &= ~AO_BUSY_FLAG
+
+#define AO_OOR(A) ((A)->ao_flags & AO_OOR_FLAG)
+#define SET_AO_OOR(A) (A)->ao_flags |= AO_OOR_FLAG
+#define CLEAR_AO_OOR(A) (A)->ao_flags &= ~AO_OOR_FLAG
+
+#define AO_QUEUED(A) ((A)->ao_flags & AO_QUEUED_FLAG)
+#define SET_AO_QUEUED(A) (A)->ao_flags |= AO_QUEUED_FLAG
+#define CLEAR_AO_QUEUED(A) (A)->ao_flags &= ~AO_QUEUED_FLAG
+
+#define AO_XSUM(A) ((A)->ao_flags & AO_XSUM_FLAG)
+#define SET_AO_XSUM(A) (A)->ao_flags |= AO_XSUM_FLAG
+#define CLEAR_AO_XSUM(A) (A)->ao_flags &= ~AO_XSUM_FLAG
+
+#define AO_REQUEST(A, f) ((A)->ao_flags & f##_FLAG)
+#define SET_AO_REQUEST(A, f) (A)->ao_flags |= f##_FLAG
+#define CLEAR_AO_REQUEST(A, f) (A)->ao_flags &= ~f##_FLAG
+#define AO_PENDING(A) ((A)->ao_flags & (AO_DELETE_FLAG | AO_OPTIONS_FLAG | AO_SEND_FLAG))
+
+//* Definition of an address object search context. This is a data structure used
+// when the address object table is to be read sequentially.
+
+struct AOSearchContext {
+ AddrObj *asc_previous; // Previous AO found.
+ IPAddr asc_addr; // IPAddress to be found.
+ ushort asc_port; // Port to be found.
+ uchar asc_prot; // Protocol
+ uchar asc_pad; // Pad to dword boundary.
+}; /* AOSearchContext */
+
+typedef struct AOSearchContext AOSearchContext;
+
+//* Definition of an AO request structure. There structures are used only for
+// queuing delete and option set requests.
+
+#define aor_signature 0x20524F41
+
+struct AORequest {
+#ifdef DEBUG
+ ulong aor_sig;
+#endif
+ struct AORequest *aor_next; // Next pointer in chain.
+ uint aor_id; // ID for the request.
+ uint aor_length; // Length of buffer.
+ void *aor_buffer; // Buffer for this request.
+ CTEReqCmpltRtn aor_rtn; // Request complete routine for this request.
+ PVOID aor_context; // Request context;
+}; /* AORequest */
+
+typedef struct AORequest AORequest;
+
+typedef struct AOMCastAddr {
+ struct AOMCastAddr *ama_next; // Next in list.
+ IPAddr ama_addr; // The address.
+ IPAddr ama_if; // The interface.
+} AOMCastAddr;
+
+//* External declarations for exported functions.
+
+extern AddrObj *GetAddrObj(IPAddr LocalAddr, ushort LocalPort, uchar Prot,
+ AddrObj *PreviousAO);
+extern AddrObj *GetNextAddrObj(AOSearchContext *SearchContext);
+extern AddrObj *GetFirstAddrObj(IPAddr LocalAddr, ushort LocalPort, uchar Prot,
+ AOSearchContext *SearchContext);
+extern TDI_STATUS TdiOpenAddress(PTDI_REQUEST Request,
+ TRANSPORT_ADDRESS UNALIGNED *AddrList, uint Protocol,
+ void *Reuse);
+extern TDI_STATUS TdiCloseAddress(PTDI_REQUEST Request);
+extern TDI_STATUS SetAddrOptions(PTDI_REQUEST Request, uint ID, uint OptLength,
+ void *Options);
+extern TDI_STATUS TdiSetEvent(PVOID Handle, int Type, PVOID Handler,
+ PVOID Context);
+extern uchar GetAddress(TRANSPORT_ADDRESS UNALIGNED *AddrList,
+ IPAddr *Addr, ushort *Port);
+extern int InitAddr(void);
+extern void ProcessAORequests(AddrObj *RequestAO);
+extern void DelayDerefAO(AddrObj *RequestAO);
+extern void DerefAO(AddrObj *RequestAO);
+extern void FreeAORequest(AORequest *FreedRequest);
+#ifdef VXD
+extern AddrObj *GetIndexedAO(uint Index);
+#endif
+extern uint ValidateAOContext(void *Context, uint *Valid);
+extern uint ReadNextAO(void *Context, void *OutBuf);
+extern void InvalidateAddrs(IPAddr Addr);
+
+extern uint MCastAddrOnAO(AddrObj *AO, IPAddr Addr);
+
+#define GetBestAddrObj(addr, port, prot) GetAddrObj(addr, port, prot, NULL)
+
+#define REF_AO(a) (a)->ao_usecnt++
+
+#define DELAY_DEREF_AO(a) DelayDerefAO((a))
+#define DEREF_AO(a) DerefAO((a))
+#define LOCKED_DELAY_DEREF_AO(a) (a)->ao_usecnt--; \
+\
+ if (!(a)->ao_usecnt && !AO_BUSY((a)) && AO_PENDING((a))) { \
+ SET_AO_BUSY((a)); \
+ CTEScheduleEvent(&(a)->ao_event, (a)); \
+ }
+
+
+
+
+
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/alpha/sources b/private/ntos/tdi/tcpip/tcp/alpha/sources
new file mode 100644
index 000000000..6a4917025
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/alpha/sources
@@ -0,0 +1,3 @@
+ALPHA_SOURCES= \
+ ..\alpha\xsum.s
+
diff --git a/private/ntos/tdi/tcpip/tcp/alpha/xsum.s b/private/ntos/tdi/tcpip/tcp/alpha/xsum.s
new file mode 100644
index 000000000..032d40747
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/alpha/xsum.s
@@ -0,0 +1,271 @@
+// TITLE("Compute Checksum")
+//++
+//
+// Copyright (c) 1994 Microsoft Corporation
+//
+// Module Name:
+//
+// xsum.s
+//
+// Abstract:
+//
+// This module implements a function to compute the checksum of a buffer.
+//
+// Author:
+//
+// John Vert (jvert) 11-Jul-1994
+//
+// Environment:
+//
+// Revision History:
+//
+//--
+
+#include "ksalpha.h"
+
+
+ SBTTL("Compute Checksum")
+//++
+//
+// ULONG
+// tcpxsum (
+// IN ULONG Checksum,
+// IN PUSHORT Source,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function computes the checksum of the specified buffer.
+//
+// Arguments:
+//
+// Checksum (a0) - Supplies the initial checksum value.
+//
+// Source (a1) - Supplies a pointer to the checksum buffer
+//
+// Length (a2) - Supplies the length of the buffer in words.
+//
+// Return Value:
+//
+// The computed checksum is returned as the function value.
+//
+//--
+
+ LEAF_ENTRY(tcpxsum)
+ zap a0, 0xf0, a0 // clear high half of a0
+ bis a1, zero, t6 // save initial buffer address
+ bis zero, zero, v0 // clear accumulated checksum
+
+//
+// Check if the buffer is quadword aligned.
+//
+// If the buffer is not quadword aligned, then add the leading words to the
+// checksum.
+//
+ ldq_u t0, 0(a1) // get containing quadword of first part
+ blbc a1, 10f // check for word alignment
+ beq a2, 65f // if zero bytes, don't do anything
+ extbl t0, a1, t1 // get leading byte
+ sll t1, 8, v0 // shift it to correct spot for later byte swap
+ addq a1, 1, a1 // increment buffer to first full word
+ subq a2, 1, a2 // decrement byte count
+
+10:
+ and a1, 6, t2 // check if buffer quadword aligned
+ beq t2, 20f // if eq, quadword aligned
+ extql t0, t2, t0 // extract bytes to checksum
+ and a1, 7, t3 // compute bytes summed
+ subq zero, t3, t3
+ addq t3, 8, t3
+ addq a1, 8, a1 // advance buffer address to next qword
+ bic a1, 7, a1 //
+ subq a2, t3, t2
+ blt t2, 55f // if ltz, too many, jump to residual code
+
+ addq v0, t0, v0 // add bytes to partial checksum
+ cmpult v0, t0, t1 // generate carry
+ addq t1, v0, v0 // add carry back into checksum
+
+ bis t2, zero, a2 // reduce count of bytes to checksum
+ beq t2, 60f // if eq, no more bytes
+
+20:
+//
+// Compute the checksum in 64-byte blocks
+//
+ bic a2, 7, t4 // subtract out residual bytes
+ beq t4, 40f // if eq, no quadwords to checksum
+ subq zero, t4, t2 // compute negative of byte count
+ and t2, 15 << 2, t3 // compute bytes in first iteration
+ ldq t0, 0(a1) // get first quadword to checksum
+ beq t3, 35f // if eq, full 64-byte block
+ subq a1, t3, a1 // bias buffer address by offset
+ bic t4, 64-1, t4 // subtract out bytes in first iteration
+ lda t2, 30f // get base address of code vector
+ addl t3, t3, t3 //
+ addq t3, t2, t2 // compute code vector offset
+ bis t0, zero, t1 // copy first quadword to checksum
+ jmp (t2) // dispatch
+
+
+30:
+//
+// The following code vector computes the checksum of a 64-byte block.
+//
+.set noreorder
+ ldq t1, 8(a1)
+ addq v0, t0, v0
+ cmpult v0, t0, t2
+ addq v0, t2, v0
+
+ ldq t0, 16(a1)
+ addq v0, t1, v0
+ cmpult v0, t1, t2
+ addq v0, t2, v0
+
+ ldq t1, 24(a1)
+ addq v0, t0, v0
+ cmpult v0, t0, t2
+ addq v0, t2, v0
+
+ ldq t0, 32(a1)
+ addq v0, t1, v0
+ cmpult v0, t1, t2
+ addq v0, t2, v0
+
+ ldq t1, 40(a1)
+ addq v0, t0, v0
+ cmpult v0, t0, t2
+ addq v0, t2, v0
+
+ ldq t0, 48(a1)
+ addq v0, t1, v0
+ cmpult v0, t1, t2
+ addq v0, t2, v0
+
+ ldq t1, 56(a1)
+ addq v0, t0, v0
+ cmpult v0, t0, t2
+ addq v0, t2, v0
+
+ addq a1, 64, a1
+ addq v0, t1, v0
+ cmpult v0, t1, t2
+ addq v0, t2, v0
+.set reorder
+
+ beq t4, 40f // if zero, end of block
+
+35:
+ ldq t0, 0(a1)
+//
+// The following loop is allowed to be reordered by the assembler for
+// optimal scheduling. It is never branched into.
+//
+ subq t4, 64, t4 // reduce byte count of longwords
+
+ ldq t1, 8(a1)
+ addq v0, t0, v0
+ cmpult v0, t0, t2
+ addq v0, t2, v0
+
+ ldq t0, 16(a1)
+ addq v0, t1, v0
+ cmpult v0, t1, t2
+ addq v0, t2, v0
+
+ ldq t1, 24(a1)
+ addq v0, t0, v0
+ cmpult v0, t0, t2
+ addq v0, t2, v0
+
+ ldq t0, 32(a1)
+ addq v0, t1, v0
+ cmpult v0, t1, t2
+ addq v0, t2, v0
+
+ ldq t1, 40(a1)
+ addq v0, t0, v0
+ cmpult v0, t0, t2
+ addq v0, t2, v0
+
+ ldq t0, 48(a1)
+ addq v0, t1, v0
+ cmpult v0, t1, t2
+ addq v0, t2, v0
+
+ ldq t1, 56(a1)
+ addq v0, t0, v0
+ cmpult v0, t0, t2
+ addq v0, t2, v0
+
+ addq a1, 64, a1
+ addq v0, t1, v0
+ cmpult v0, t1, t2
+ addq v0, t2, v0
+
+ bne t4, 35b // if ne zero, not end of block
+
+40:
+//
+// Check for any remaining bytes.
+//
+ and a2, 7, a2 // isolate residual bytes
+ beq a2, 60f // if eq, no residual bytes
+50:
+//
+// Checksum remaining bytes.
+//
+// The technique we use here is to load the final quadword, then
+// zero out the bytes that are not included.
+//
+ ldq t0, 0(a1) // get quadword surrounding remainder
+55:
+ ornot zero, zero, t1 // get FF mask
+ sll t1, a2, t2 // shift to produce byte mask
+ zap t0, t2, t0 // zero out bytes past end of buffer
+ addq v0, t0, v0 // add quadword to partial checksum
+ cmpult v0, t0, t1 // generate carry
+ addq t1, v0, v0 // add carry back into checksum
+60:
+//
+// Byte swap the 64-bit checksum if the start of the buffer was not word aligned
+//
+ blbc t6, 65f
+ zap v0, 0xAA, t0 // isolate even bytes
+ sll t0, 8, t0 // shift even bytes into odd positions
+ srl v0, 8, t1 // shift odd bytes into even positions
+ zap t1, 0xAA, t1 // isolate odd bytes
+ bis t0, t1, v0 // merge bytes back together
+
+65:
+//
+// add computed checksum to original checksum, and fold the 64-bit
+// result down to 16 bits.
+//
+ addq v0, a0, v0 // add computed checksum to original
+ cmpult v0, a0, t0 // generate carry
+ addq v0, t0, v0 // add carry back into checksum
+
+//
+// swap the longwords in order to sum two longwords and their carry in one add.
+//
+ sll v0, 32, t0 // shift low longword into high
+ srl v0, 32, t1 // shift high longword into low
+ bis t1, t0, t5 // merge back together
+
+ addq v0, t5, t0 // produce sum + carry in high longword
+ srl t0, 32, t1 // shift back down to low half
+//
+// swap the words in order to sum two words and their carry in one add
+//
+ sll t1, 16, t2 // shift high word into low
+ srl t1, 16, t3 // shift low word into high
+ bis t2, t3, t4 // merge back together
+ addq t4, t1, t2 // produce sum and carry in high word
+ extwl t2, 2, v0 // extract result.
+ ret zero, (ra) // return
+
+ .end tcpxsum
+
diff --git a/private/ntos/tdi/tcpip/tcp/dgram.c b/private/ntos/tdi/tcpip/tcp/dgram.c
new file mode 100644
index 000000000..c0ada18c5
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/dgram.c
@@ -0,0 +1,990 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** DGRAM.C - Common datagram protocol code.
+//
+// This file contains the code common to both UDP and Raw IP.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#include "tdistat.h"
+#ifdef VXD
+#include "tdivxd.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "dgram.h"
+#include "tlcommon.h"
+#include "info.h"
+
+#define NO_TCP_DEFS 1
+#include "tcpdeb.h"
+
+#ifdef NT
+
+#ifdef POOL_TAGGING
+
+#ifdef ExAllocatePool
+#undef ExAllocatePool
+#endif
+
+#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'dPCT')
+
+#ifndef CTEAllocMem
+#error "CTEAllocMem is not already defined - will override tagging"
+#else
+#undef CTEAllocMem
+#endif
+
+#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'dPCT')
+
+#endif // POOL_TAGGING
+
+#endif // NT
+
+#define NUM_DG_HEADERS 5
+
+#ifdef NT
+#define DG_MAX_HDRS 0xffff
+#else
+#define DG_MAX_HDRS 100
+#endif
+
+ulong DGCurrentSendFree = 0;
+ulong DGMaxSendFree = DG_MAX_HDRS;
+
+EXTERNAL_LOCK(AddrObjTableLock)
+
+DGSendReq *DGSendReqFree;
+DEFINE_LOCK_STRUCTURE(DGSendReqLock)
+
+#ifndef NT
+DGRcvReq *DGRcvReqFree;
+#else
+SLIST_HEADER DGRcvReqFree;
+#endif
+
+DEFINE_LOCK_STRUCTURE(DGRcvReqFreeLock)
+
+#ifdef DEBUG
+uint NumSendReq = 0;
+uint NumRcvReq = 0;
+#endif
+
+// Information for maintaining the DG Header structures and
+// pending queue.
+uint DGHeaderSize;
+PNDIS_BUFFER DGHeaderList;
+Queue DGHeaderPending;
+Queue DGDelayed;
+
+CTEEvent DGDelayedEvent;
+
+extern IPInfo LocalNetInfo;
+
+typedef struct DGHdrBPoolEntry {
+ struct DGHdrBPoolEntry *uhe_next;
+ NDIS_HANDLE uhe_handle;
+ uchar *uhe_buffer;
+} DGHdrBPoolEntry;
+
+DGHdrBPoolEntry *DGHdrBPoolList = NULL;
+
+//
+// All of the init code can be discarded.
+//
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+
+int InitDG(uint MaxHeaderSize);
+
+#pragma alloc_text(INIT, InitDG)
+
+#endif // ALLOC_PRAGMA
+#endif
+
+#ifdef CHICAGO
+extern int RegisterAddrChangeHndlr(void *Handler, uint Add);
+extern void AddrChange(IPAddr Addr, IPMask Mask, void *Context,
+ uint Added);
+#endif
+
+
+
+//* GrowDGHeaderList - Try to grow the DG header list.
+//
+// Called when we run out of buffers on the DG header list, and need
+// to grow it. We look to see if we're already at the maximum size, and
+// if not we'll allocate the need structures and free them to the list.
+// This routine must be called with the SendReq lock held.
+//
+// Input: Nothing.
+//
+// Returns: A pointer to a new DG header buffer if we have one, or NULL.
+//
+PNDIS_BUFFER
+GrowDGHeaderList(void)
+{
+ DGHdrBPoolEntry *NewEntry;
+ NDIS_STATUS Status;
+ uint HeaderSize;
+ uchar *DGSendHP;
+ uint i;
+ PNDIS_BUFFER Buffer;
+ PNDIS_BUFFER ReturnBuffer = NULL;
+
+ if (DGCurrentSendFree < DGMaxSendFree) {
+
+ // Still room to grow the list.
+ NewEntry = CTEAllocMem(sizeof(DGHdrBPoolEntry));
+
+ if (NewEntry == NULL) {
+ // Couldn't get the memory.
+ return NULL;
+ }
+
+ NdisAllocateBufferPool(&Status, &NewEntry->uhe_handle,
+ NUM_DG_HEADERS);
+
+ if (Status != NDIS_STATUS_SUCCESS) {
+ // Couldn't get a new set of buffers. Fail.
+ CTEFreeMem(NewEntry);
+ return NULL;
+ }
+
+ HeaderSize = DGHeaderSize + LocalNetInfo.ipi_hsize;
+
+ DGSendHP = CTEAllocMem(HeaderSize * NUM_DG_HEADERS);
+
+ if (DGSendHP == NULL) {
+ NdisFreeBufferPool(NewEntry->uhe_handle);
+ CTEFreeMem(NewEntry);
+ return NULL;
+ }
+
+ NewEntry->uhe_buffer = DGSendHP;
+
+ for (i = 0; i < NUM_DG_HEADERS; i++) {
+ NdisAllocateBuffer(&Status, &Buffer, NewEntry->uhe_handle,
+ DGSendHP + (i * HeaderSize), HeaderSize);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ NdisFreeBufferPool(NewEntry->uhe_handle);
+ CTEFreeMem(NewEntry);
+ CTEFreeMem(DGSendHP);
+ return NULL;
+ }
+ if (i != 0)
+ FreeDGHeader(Buffer);
+ else
+ ReturnBuffer = Buffer;
+ }
+
+ DGCurrentSendFree += NUM_DG_HEADERS;
+ NewEntry->uhe_next = DGHdrBPoolList;
+ DGHdrBPoolList = NewEntry;
+
+ } else {
+ // At the limit already.
+ ReturnBuffer = NULL;
+ }
+
+ return ReturnBuffer;
+
+
+}
+//* GetDGHeader - Get a DG header buffer.
+//
+// The get header buffer routine. Called with the SendReqLock held.
+//
+// Input: Nothing.
+//
+// Output: A pointer to an NDIS buffer, or NULL.
+//
+_inline PNDIS_BUFFER
+GetDGHeader(void)
+{
+ PNDIS_BUFFER NewBuffer;
+
+ NewBuffer = DGHeaderList;
+ if (NewBuffer != NULL)
+ DGHeaderList = NDIS_BUFFER_LINKAGE(NewBuffer);
+ else
+ NewBuffer = GrowDGHeaderList();
+
+ return NewBuffer;
+}
+
+//* FreeDGHeader - Free a DG header buffer.
+//
+// The free header buffer routine. Called with the SendReqLock held.
+//
+// Input: Buffer to be freed.
+//
+// Output: Nothing.
+//
+void
+FreeDGHeader(PNDIS_BUFFER FreedBuffer)
+{
+ NDIS_BUFFER_LINKAGE(FreedBuffer) = DGHeaderList;
+ DGHeaderList = FreedBuffer;
+}
+
+//* PutPendingQ - Put an address object on the pending queue.
+//
+// Called when we've experienced a header buffer out of resources condition,
+// and want to queue an AddrObj for later processing. We put the specified
+// address object on the DGHeaderPending queue, set the OOR flag and clear
+// the 'send request' flag. It is invariant in the system that the send
+// request flag and the OOR flag are not set at the same time.
+//
+// This routine assumes that the caller holds the DGSendReqLock and the
+// lock on the particular AddrObj.
+//
+// Input: QueueingAO - Pointer to address object to be queued.
+//
+// Returns: Nothing.
+//
+void
+PutPendingQ(AddrObj *QueueingAO)
+{
+ CTEStructAssert(QueueingAO, ao);
+
+ if (!AO_OOR(QueueingAO)) {
+ CLEAR_AO_REQUEST(QueueingAO, AO_SEND);
+ SET_AO_OOR(QueueingAO);
+
+ ENQUEUE(&DGHeaderPending, &QueueingAO->ao_pendq);
+ }
+}
+
+//* GetDGSendReq - Get a DG send request.
+//
+// Called when someone wants to allocate a DG send request. We assume
+// the send request lock is held when we are called.
+//
+// Note: This routine and the corresponding free routine might
+// be good candidates for inlining.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to the SendReq, or NULL if none.
+//
+DGSendReq *
+GetDGSendReq()
+{
+ DGSendReq *NewReq;
+
+
+ NewReq = DGSendReqFree;
+ if (NewReq != NULL) {
+ CTEStructAssert(NewReq, dsr);
+ DGSendReqFree = (DGSendReq *)NewReq->dsr_q.q_next;
+ } else {
+ // Couldn't get a request, grow it. This is one area where we'll try
+ // to allocate memory with a lock held. Because of this, we've
+ // got to be careful about where we call this routine from.
+
+ NewReq = CTEAllocMem(sizeof(DGSendReq));
+ if (NewReq != NULL) {
+#ifdef DEBUG
+ NewReq->dsr_sig = dsr_signature;
+ NumSendReq++;
+#endif
+ }
+ }
+
+ return NewReq;
+}
+
+//* FreeDGSendReq - Free a DG send request.
+//
+// Called when someone wants to free a DG send request. It's assumed
+// that the caller holds the SendRequest lock.
+//
+// Input: SendReq - SendReq to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeDGSendReq(DGSendReq *SendReq)
+{
+ CTEStructAssert(SendReq, dsr);
+
+ *(DGSendReq **)&SendReq->dsr_q.q_next = DGSendReqFree;
+ DGSendReqFree = SendReq;
+}
+
+//* GetDGRcvReq - Get a DG receive request.
+//
+// Called when we need to get a DG receive request.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to new request, or NULL if none.
+//
+DGRcvReq *
+GetDGRcvReq()
+{
+ DGRcvReq *NewReq;
+
+#ifdef VXD
+ NewReq = DGRcvReqFree;
+ if (NewReq != NULL) {
+ CTEStructAssert(NewReq, drr);
+ DGRcvReqFree = (DGRcvReq *)NewReq->drr_q.q_next;
+ } else {
+ // Couldn't get a request, grow it.
+ NewReq = CTEAllocMem(sizeof(DGRcvReq));
+ if (NewReq != NULL) {
+#ifdef DEBUG
+ NewReq->drr_sig = drr_signature;
+ NumRcvReq++;
+#endif
+ }
+ }
+
+#endif // VXD
+
+#ifdef NT
+ PSINGLE_LIST_ENTRY BufferLink;
+ Queue *QueuePtr;
+
+ BufferLink = ExInterlockedPopEntrySList(
+ &DGRcvReqFree,
+ &DGRcvReqFreeLock
+ );
+
+ if (BufferLink != NULL) {
+ QueuePtr = STRUCT_OF(Queue, BufferLink, q_next);
+ NewReq = STRUCT_OF(DGRcvReq, QueuePtr, drr_q);
+ CTEStructAssert(NewReq, drr);
+ }
+ else {
+ // Couldn't get a request, grow it.
+ NewReq = CTEAllocMem(sizeof(DGRcvReq));
+ if (NewReq != NULL) {
+#ifdef DEBUG
+ NewReq->drr_sig = drr_signature;
+ ExInterlockedAddUlong(&NumRcvReq, 1, &DGRcvReqFreeLock);
+#endif
+ }
+ }
+
+#endif // NT
+
+ return NewReq;
+}
+
+//* FreeDGRcvReq - Free a DG rcv request.
+//
+// Called when someone wants to free a DG rcv request.
+//
+// Input: RcvReq - RcvReq to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeDGRcvReq(DGRcvReq *RcvReq)
+{
+#ifdef VXD
+
+ CTEStructAssert(RcvReq, drr);
+
+ *(DGRcvReq **)&RcvReq->drr_q.q_next = DGRcvReqFree;
+ DGRcvReqFree = RcvReq;
+
+#endif // VXD
+
+#ifdef NT
+
+ PSINGLE_LIST_ENTRY BufferLink;
+
+ CTEStructAssert(RcvReq, drr);
+
+ BufferLink = STRUCT_OF(SINGLE_LIST_ENTRY, &(RcvReq->drr_q.q_next), Next);
+ ExInterlockedPushEntrySList(
+ &DGRcvReqFree,
+ BufferLink,
+ &DGRcvReqFreeLock
+ );
+
+#endif // NT
+}
+
+
+//* DGDelayedEventProc - Handle a delayed event.
+//
+// This is the delayed event handler, used for out-of-resources conditions
+// on AddrObjs. We pull from the delayed queue, and is the addr obj is
+// not already busy we'll send the datagram.
+//
+// Input: Event - Pointer to the event structure.
+// Context - Nothing.
+//
+// Returns: Nothing
+//
+void
+DGDelayedEventProc(CTEEvent *Event, void *Context)
+{
+ CTELockHandle HeaderHandle, AOHandle;
+ AddrObj *SendingAO;
+ DGSendProc SendProc;
+
+ CTEGetLock(&DGSendReqLock, &HeaderHandle);
+ while (!EMPTYQ(&DGDelayed)) {
+ DEQUEUE(&DGDelayed, SendingAO, AddrObj, ao_pendq);
+ CTEStructAssert(SendingAO, ao);
+
+ CTEGetLock(&SendingAO->ao_lock, &AOHandle);
+
+ CLEAR_AO_OOR(SendingAO);
+ if (!AO_BUSY(SendingAO)) {
+ DGSendReq *SendReq;
+
+ if (!EMPTYQ(&SendingAO->ao_sendq)) {
+ DEQUEUE(&SendingAO->ao_sendq, SendReq, DGSendReq, dsr_q);
+
+ CTEStructAssert(SendReq, dsr);
+ CTEAssert(SendReq->dsr_header != NULL);
+
+ SendingAO->ao_usecnt++;
+ SendProc = SendingAO->ao_dgsend;
+ CTEFreeLock(&SendingAO->ao_lock, AOHandle);
+ CTEFreeLock(&DGSendReqLock, HeaderHandle);
+
+ (*SendProc)(SendingAO, SendReq);
+ DEREF_AO(SendingAO);
+ CTEGetLock(&DGSendReqLock, &HeaderHandle);
+ } else {
+ CTEAssert(FALSE);
+ CTEFreeLock(&SendingAO->ao_lock, AOHandle);
+ }
+
+ } else {
+ SET_AO_REQUEST(SendingAO, AO_SEND);
+ CTEFreeLock(&SendingAO->ao_lock, AOHandle);
+ }
+ }
+
+ CTEFreeLock(&DGSendReqLock, HeaderHandle);
+
+}
+
+//* DGSendComplete - DG send complete handler.
+//
+// This is the routine called by IP when a send completes. We
+// take the context passed back as a pointer to a SendRequest
+// structure, and complete the caller's send.
+//
+// Input: Context - Context we gave on send (really a
+// SendRequest structure).
+// BufferChain - Chain of buffers sent.
+//
+// Returns: Nothing.
+void
+DGSendComplete(void *Context, PNDIS_BUFFER BufferChain)
+{
+ DGSendReq *FinishedSR = (DGSendReq *)Context;
+ CTELockHandle HeaderHandle, AOHandle;
+ CTEReqCmpltRtn Callback; // Completion routine.
+ PVOID CallbackContext; // User context.
+ ushort SentSize;
+ AddrObj *AO;
+
+ CTEStructAssert(FinishedSR, dsr);
+ CTEGetLock(&DGSendReqLock, &HeaderHandle);
+
+ Callback = FinishedSR->dsr_rtn;
+ CallbackContext = FinishedSR->dsr_context;
+ SentSize = FinishedSR->dsr_size;
+
+ // If there's nothing on the header pending queue, just free the
+ // header buffer. Otherwise pull from the pending queue, give him the
+ // resource, and schedule an event to deal with him.
+ if (EMPTYQ(&DGHeaderPending)) {
+ FreeDGHeader(BufferChain);
+ } else {
+ DEQUEUE(&DGHeaderPending, AO, AddrObj, ao_pendq);
+ CTEStructAssert(AO, ao);
+ CTEGetLock(&AO->ao_lock, &AOHandle);
+ if (!EMPTYQ(&AO->ao_sendq)) {
+ DGSendReq *SendReq;
+
+ PEEKQ(&AO->ao_sendq, SendReq, DGSendReq, dsr_q);
+ SendReq->dsr_header = BufferChain; // Give him this buffer.
+
+ ENQUEUE(&DGDelayed, &AO->ao_pendq);
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+ CTEScheduleEvent(&DGDelayedEvent, NULL);
+ } else {
+ // On the pending queue, but no sends!
+ DEBUGCHK;
+ CLEAR_AO_OOR(AO);
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+ }
+
+ }
+
+ FreeDGSendReq(FinishedSR);
+ CTEFreeLock(&DGSendReqLock, HeaderHandle);
+ if (Callback != NULL)
+ (*Callback)(CallbackContext, TDI_SUCCESS, (uint)SentSize);
+
+}
+
+
+#ifdef NT
+//
+// NT supports cancellation of DG send/receive requests.
+//
+
+#define TCP_DEBUG_SEND_DGRAM 0x00000100
+#define TCP_DEBUG_RECEIVE_DGRAM 0x00000200
+
+extern ULONG TCPDebug;
+
+
+VOID
+TdiCancelSendDatagram(
+ AddrObj *SrcAO,
+ PVOID Context
+ )
+{
+ CTELockHandle lockHandle;
+ DGSendReq *sendReq = NULL;
+ Queue *qentry;
+ BOOLEAN found = FALSE;
+
+
+ CTEStructAssert(SrcAO, ao);
+
+ CTEGetLock(&SrcAO->ao_lock, &lockHandle);
+
+ // Search the send list for the specified request.
+ for ( qentry = QNEXT(&(SrcAO->ao_sendq));
+ qentry != &(SrcAO->ao_sendq);
+ qentry = QNEXT(qentry)
+ ) {
+
+ sendReq = STRUCT_OF(DGSendReq, qentry, dsr_q);
+
+ CTEStructAssert(sendReq, dsr);
+
+ if (sendReq->dsr_context == Context) {
+ //
+ // Found it. Dequeue
+ //
+ REMOVEQ(qentry);
+ found = TRUE;
+
+ IF_TCPDBG(TCP_DEBUG_SEND_DGRAM) {
+ TCPTRACE((
+ "TdiCancelSendDatagram: Dequeued item %lx\n",
+ Context
+ ));
+ }
+
+ break;
+ }
+ }
+
+ CTEFreeLock(&SrcAO->ao_lock, lockHandle);
+
+ if (found) {
+ //
+ // Complete the request and free its resources.
+ //
+ (*sendReq->dsr_rtn)(sendReq->dsr_context, (uint) TDI_CANCELLED, 0);
+
+ CTEGetLock(&DGSendReqLock, &lockHandle);
+
+ if (sendReq->dsr_header != NULL) {
+ FreeDGHeader(sendReq->dsr_header);
+ }
+
+ FreeDGSendReq(sendReq);
+
+ CTEFreeLock(&DGSendReqLock, lockHandle);
+ }
+
+} // TdiCancelSendDatagram
+
+
+VOID
+TdiCancelReceiveDatagram(
+ AddrObj *SrcAO,
+ PVOID Context
+ )
+{
+ CTELockHandle lockHandle;
+ DGRcvReq *rcvReq = NULL;
+ Queue *qentry;
+ BOOLEAN found = FALSE;
+
+
+ CTEStructAssert(SrcAO, ao);
+
+ CTEGetLock(&SrcAO->ao_lock, &lockHandle);
+
+ // Search the send list for the specified request.
+ for ( qentry = QNEXT(&(SrcAO->ao_rcvq));
+ qentry != &(SrcAO->ao_rcvq);
+ qentry = QNEXT(qentry)
+ ) {
+
+ rcvReq = STRUCT_OF(DGRcvReq, qentry, drr_q);
+
+ CTEStructAssert(rcvReq, drr);
+
+ if (rcvReq->drr_context == Context) {
+ //
+ // Found it. Dequeue
+ //
+ REMOVEQ(qentry);
+ found = TRUE;
+
+ IF_TCPDBG(TCP_DEBUG_SEND_DGRAM) {
+ TCPTRACE((
+ "TdiCancelReceiveDatagram: Dequeued item %lx\n",
+ Context
+ ));
+ }
+
+ break;
+ }
+ }
+
+ CTEFreeLock(&SrcAO->ao_lock, lockHandle);
+
+ if (found) {
+ //
+ // Complete the request and free its resources.
+ //
+ (*rcvReq->drr_rtn)(rcvReq->drr_context, (uint) TDI_CANCELLED, 0);
+
+ FreeDGRcvReq(rcvReq);
+ }
+
+} // TdiCancelReceiveDatagram
+
+
+#endif // NT
+
+
+//** TdiSendDatagram - TDI send datagram function.
+//
+// This is the user interface to the send datagram function. The
+// caller specified a request structure, a connection info
+// structure containing the address, and data to be sent.
+// This routine gets a DG Send request structure to manage the
+// send, fills the structure in, and calls DGSend to deal with
+// it.
+//
+// Input: Request - Pointer to request structure.
+// ConnInfo - Pointer to ConnInfo structure which points to
+// remote address.
+// DataSize - Size in bytes of data to be sent.
+// BytesSent - Pointer to where to return size sent.
+// Buffer - Pointer to buffer chain.
+//
+// Returns: Status of attempt to send.
+//
+TDI_STATUS
+TdiSendDatagram(PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION ConnInfo,
+ uint DataSize, uint *BytesSent, PNDIS_BUFFER Buffer)
+{
+ AddrObj *SrcAO; // Pointer to AddrObj for src.
+ DGSendReq *SendReq; // Pointer to send req for this request.
+ CTELockHandle Handle, SRHandle; // Lock handles for the AO and the
+ // send request.
+ TDI_STATUS ReturnValue;
+ DGSendProc SendProc;
+
+ // First, get a send request. We do this first because of MP issues
+ // if we port this to NT. We need to take the SendRequest lock before
+ // we take the AddrObj lock, to prevent deadlock and also because
+ // GetDGSendReq might yield, and the state of the AddrObj might
+ // change on us, so we don't want to yield after we've validated
+ // it.
+
+ CTEGetLock(&DGSendReqLock, &SRHandle);
+ SendReq = GetDGSendReq();
+
+ // Now get the lock on the AO, and make sure it's valid. We do this
+ // to make sure we return the correct error code.
+
+#ifdef VXD
+ SrcAO = GetIndexedAO((uint)Request->Handle.AddressHandle);
+
+ if (SrcAO != NULL) {
+#else
+ SrcAO = Request->Handle.AddressHandle;
+#endif
+
+ CTEStructAssert(SrcAO, ao);
+
+ CTEGetLock(&SrcAO->ao_lock, &Handle);
+
+ if (AO_VALID(SrcAO)) {
+
+ // Make sure the size is reasonable.
+ if (DataSize <= SrcAO->ao_maxdgsize) {
+
+ // The AddrObj is valid. Now fill the address into the send request,
+ // if we've got one. If this works, we'll continue with the
+ // send.
+
+ if (SendReq != NULL) { // Got a send request.
+ if (GetAddress(ConnInfo->RemoteAddress, &SendReq->dsr_addr,
+ &SendReq->dsr_port)) {
+
+ SendReq->dsr_rtn = Request->RequestNotifyObject;
+ SendReq->dsr_context = Request->RequestContext;
+ SendReq->dsr_buffer = Buffer;
+ SendReq->dsr_size = (ushort)DataSize;
+
+ // We've filled in the send request. If the AO isn't
+ // already busy, try to get a DG header buffer and send
+ // this. If the AO is busy, or we can't get a buffer, queue
+ // until later. We try to get the header buffer here, as
+ // an optimazation to avoid having to retake the lock.
+
+ if (!AO_OOR(SrcAO)) { // AO isn't out of resources
+ if (!AO_BUSY(SrcAO)) { // or or busy
+
+ if ((SendReq->dsr_header = GetDGHeader()) != NULL) {
+ REF_AO(SrcAO); // Lock out exclusive
+ // activities.
+ SendProc = SrcAO->ao_dgsend;
+
+ CTEFreeLock(&SrcAO->ao_lock, Handle);
+ CTEFreeLock(&DGSendReqLock, SRHandle);
+
+ // Allright, just send it.
+ (*SendProc)(SrcAO, SendReq);
+
+ // See if any pending requests occured during
+ // the send. If so, call the request handler.
+ DEREF_AO(SrcAO);
+
+ return TDI_PENDING;
+ } else {
+ // We couldn't get a header buffer. Put this
+ // guy on the pending queue, and then fall
+ // through to the 'queue request' code.
+ PutPendingQ(SrcAO);
+ }
+ } else {
+ // AO is busy, set request for later
+ SET_AO_REQUEST(SrcAO, AO_SEND);
+ }
+ }
+
+ // AO is busy, or out of resources. Queue the send request
+ // for later.
+ SendReq->dsr_header = NULL;
+ ENQUEUE(&SrcAO->ao_sendq, &SendReq->dsr_q);
+ SendReq = NULL;
+ ReturnValue = TDI_PENDING;
+ }
+ else {
+ // The remote address was invalid.
+ ReturnValue = TDI_BAD_ADDR;
+ }
+ }
+ else {
+ // Send request was null, return no resources.
+ ReturnValue = TDI_NO_RESOURCES;
+ }
+ }
+ else {
+ // Buffer was too big, return an error.
+ ReturnValue = TDI_BUFFER_TOO_BIG;
+ }
+ }
+ else {
+ // The addr object is invalid, possibly because it's deleting.
+ ReturnValue = TDI_ADDR_INVALID;
+ }
+
+ CTEFreeLock(&SrcAO->ao_lock, Handle);
+
+#ifdef VXD
+ }
+ else {
+ ReturnValue = TDI_ADDR_INVALID;
+ }
+#endif
+
+ if (SendReq != NULL)
+ FreeDGSendReq(SendReq);
+
+ CTEFreeLock(&DGSendReqLock, SRHandle);
+
+ return TDI_ADDR_INVALID;
+}
+
+//** TdiReceiveDatagram - TDI receive datagram function.
+//
+// This is the user interface to the receive datagram function. The
+// caller specifies a request structure, a connection info
+// structure that acts as a filter on acceptable datagrams, a connection
+// info structure to be filled in, and other parameters. We get a DGRcvReq
+// structure, fill it in, and hang it on the AddrObj, where it will be removed
+// later by incomig datagram handler.
+//
+// Input: Request - Pointer to request structure.
+// ConnInfo - Pointer to ConnInfo structure which points to
+// remote address.
+// ReturnInfo - Pointer to ConnInfo structure to be filled in.
+// RcvSize - Total size in bytes receive buffer.
+// BytesRcvd - Pointer to where to return size received.
+// Buffer - Pointer to buffer chain.
+//
+// Returns: Status of attempt to receive.
+//
+TDI_STATUS
+TdiReceiveDatagram(PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION ConnInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo, uint RcvSize, uint *BytesRcvd,
+ PNDIS_BUFFER Buffer)
+{
+ AddrObj *RcvAO; // AddrObj that is receiving.
+ DGRcvReq *RcvReq; // Receive request structure.
+ CTELockHandle AOHandle;
+ uchar AddrValid;
+
+ RcvReq = GetDGRcvReq();
+
+#ifdef VXD
+ RcvAO = GetIndexedAO((uint)Request->Handle.AddressHandle);
+
+ if (RcvAO != NULL) {
+ CTEStructAssert(RcvAO, ao);
+
+#else
+ RcvAO = Request->Handle.AddressHandle;
+ CTEStructAssert(RcvAO, ao);
+#endif
+
+ CTEGetLock(&RcvAO->ao_lock, &AOHandle);
+ if (AO_VALID(RcvAO)) {
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("posting receive on AO %lx\n", RcvAO));
+ }
+
+ if (RcvReq != NULL) {
+ if (ConnInfo != NULL && ConnInfo->RemoteAddressLength != 0)
+ AddrValid = GetAddress(ConnInfo->RemoteAddress,
+ &RcvReq->drr_addr, &RcvReq->drr_port);
+ else {
+ AddrValid = TRUE;
+ RcvReq->drr_addr = NULL_IP_ADDR;
+ RcvReq->drr_port = 0;
+ }
+
+ if (AddrValid) {
+
+ // Everything'd valid. Fill in the receive request and queue it.
+ RcvReq->drr_conninfo = ReturnInfo;
+ RcvReq->drr_rtn = Request->RequestNotifyObject;
+ RcvReq->drr_context = Request->RequestContext;
+ RcvReq->drr_buffer = Buffer;
+ RcvReq->drr_size = RcvSize;
+ ENQUEUE(&RcvAO->ao_rcvq, &RcvReq->drr_q);
+ CTEFreeLock(&RcvAO->ao_lock, AOHandle);
+
+ return TDI_PENDING;
+ } else {
+ // Have an invalid filter address.
+ CTEFreeLock(&RcvAO->ao_lock, AOHandle);
+ FreeDGRcvReq(RcvReq);
+ return TDI_BAD_ADDR;
+ }
+ } else {
+ // Couldn't get a receive request.
+ CTEFreeLock(&RcvAO->ao_lock, AOHandle);
+ return TDI_NO_RESOURCES;
+ }
+ } else {
+ // The AddrObj isn't valid.
+ CTEFreeLock(&RcvAO->ao_lock, AOHandle);
+ }
+
+#ifdef VXD
+ }
+#endif
+
+ // The AddrObj is invalid or non-existent.
+ if (RcvReq != NULL)
+ FreeDGRcvReq(RcvReq);
+
+ return TDI_ADDR_INVALID;
+}
+
+
+#pragma BEGIN_INIT
+
+//* InitDG - Initialize the DG stuff.
+//
+// Called during init time to initalize the DG code. We initialize
+// our locks and request lists.
+//
+// Input: MaxHeaderSize - The maximum size of a datagram transport header,
+// not including the IP header.
+//
+// Returns: True if we succeed, False if we fail.
+//
+int
+InitDG(uint MaxHeaderSize)
+{
+ PNDIS_BUFFER Buffer;
+ CTELockHandle Handle;
+
+
+ DGHeaderSize = MaxHeaderSize;
+
+ CTEInitLock(&DGSendReqLock);
+ CTEInitLock(&DGRcvReqFreeLock);
+
+ DGSendReqFree = NULL;
+
+#ifndef NT
+ DGRcvReqFree = NULL;
+#else
+ ExInitializeSListHead(&DGRcvReqFree);
+#endif
+
+
+ CTEGetLock(&DGSendReqLock, &Handle);
+
+ Buffer = GrowDGHeaderList();
+
+ if (Buffer != NULL) {
+ FreeDGHeader(Buffer);
+ CTEFreeLock(&DGSendReqLock, Handle);
+ } else {
+ CTEFreeLock(&DGSendReqLock, Handle);
+ return FALSE;
+ }
+
+ INITQ(&DGHeaderPending);
+ INITQ(&DGDelayed);
+
+ CTEInitEvent(&DGDelayedEvent, DGDelayedEventProc);
+
+ return TRUE;
+}
+
+#pragma END_INIT
diff --git a/private/ntos/tdi/tcpip/tcp/dgram.h b/private/ntos/tdi/tcpip/tcp/dgram.h
new file mode 100644
index 000000000..a637a75e3
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/dgram.h
@@ -0,0 +1,89 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** DGRAM.H - Common datagram protocol definitions.
+//
+// This file contains definitions for the functions common to
+// both UDP and Raw IP.
+//
+
+#ifndef _DGRAM_INCLUDED_
+#define _DGRAM_INCLUDED_ 1
+
+
+//* Structure used for maintaining DG send requests.
+
+#define dsr_signature 0x20525338
+
+struct DGSendReq {
+#ifdef DEBUG
+ ulong dsr_sig;
+#endif
+ Queue dsr_q; // Queue linkage when pending.
+ IPAddr dsr_addr; // Remote IPAddr.
+ PNDIS_BUFFER dsr_buffer; // Buffer of data to send.
+ PNDIS_BUFFER dsr_header; // Pointer to header buffer.
+ CTEReqCmpltRtn dsr_rtn; // Completion routine.
+ PVOID dsr_context; // User context.
+ ushort dsr_size; // Size of buffer.
+ ushort dsr_port; // Remote port.
+}; /* DGSendReq */
+
+typedef struct DGSendReq DGSendReq;
+
+//* Structure used for maintaining DG receive requests.
+
+#define drr_signature 0x20525238
+
+struct DGRcvReq {
+#ifdef DEBUG
+ ulong drr_sig;
+#endif
+ Queue drr_q; // Queue linkage on AddrObj.
+ IPAddr drr_addr; // Remote IPAddr acceptable.
+ PNDIS_BUFFER drr_buffer; // Buffer to be filled in.
+ PTDI_CONNECTION_INFORMATION drr_conninfo; // Pointer to conn. info.
+ CTEReqCmpltRtn drr_rtn; // Completion routine.
+ PVOID drr_context; // User context.
+ ushort drr_size; // Size of buffer.
+ ushort drr_port; // Remote port acceptable.
+}; /* DGRcvReq */
+
+typedef struct DGRcvReq DGRcvReq;
+
+
+//* External definition of exported variables.
+EXTERNAL_LOCK(DGSendReqLock)
+EXTERNAL_LOCK(DGRcvReqFreeLock)
+extern CTEEvent DGDelayedEvent;
+
+
+//* External definition of exported functions.
+extern void DGSendComplete(void *Context, PNDIS_BUFFER BufferChain);
+
+extern TDI_STATUS TdiSendDatagram(PTDI_REQUEST Request,
+ PTDI_CONNECTION_INFORMATION ConnInfo, uint DataSize,
+ uint *BytesSent, PNDIS_BUFFER Buffer);
+
+extern TDI_STATUS TdiReceiveDatagram(PTDI_REQUEST Request,
+ PTDI_CONNECTION_INFORMATION ConnInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo, uint RcvSize,
+ uint *BytesRcvd, PNDIS_BUFFER Buffer);
+
+extern IP_STATUS DGRcv(void *IPContext, IPAddr Dest, IPAddr Src, IPAddr LocalAddr,
+ IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, uchar Protocol,
+ IPOptInfo *OptInfo);
+
+extern void FreeDGRcvReq(DGRcvReq *RcvReq);
+extern void FreeDGSendReq(DGSendReq *SendReq);
+extern int InitDG(uint MaxHeaderSize);
+extern _inline PNDIS_BUFFER GetDGHeader(void);
+extern void FreeDGHeader(PNDIS_BUFFER FreedBuffer);
+extern void PutPendingQ(AddrObj *QueueingAO);
+
+
+#endif // ifndef _DGRAM_INCLUDED_
+
diff --git a/private/ntos/tdi/tcpip/tcp/dirs b/private/ntos/tdi/tcpip/tcp/dirs
new file mode 100644
index 000000000..0dab2f056
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/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/tcpip/tcp/i386/sources b/private/ntos/tdi/tcpip/tcp/i386/sources
new file mode 100644
index 000000000..9c19718ea
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/i386/sources
@@ -0,0 +1,4 @@
+i386_SOURCES= \
+ ..\i386\xsum.asm
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/i386/xsum.asm b/private/ntos/tdi/tcpip/tcp/i386/xsum.asm
new file mode 100644
index 000000000..7cb03bce2
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/i386/xsum.asm
@@ -0,0 +1,259 @@
+ title "Compute Checksum"
+
+;/*++
+;
+; Copyright (c) 1992 Microsoft Corporation
+;
+; Module Name:
+;
+; cksy.asm
+;
+; Abstract:
+;
+; This module implements a function to compute the checksum of a buffer.
+;
+; Author:
+;
+; David N. Cutler (davec) 27-Jan-1992
+;
+; Revision History:
+;
+; Who When What
+; -------- -------- ----------------------------------------------
+; mikeab 01-22-94 Pentium optimization
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+;--*/
+
+LOOP_UNROLLING_BITS equ 5
+LOOP_UNROLLING equ (1 SHL LOOP_UNROLLING_BITS)
+
+ .386
+ .model small,c
+
+ assume cs:FLAT,ds:FLAT,es:FLAT,ss:FLAT
+ assume fs:nothing,gs:nothing
+
+ .xlist
+ include callconv.inc
+ include ks386.inc
+ .list
+
+ .code
+
+;++
+;
+; ULONG
+; tcpxsum(
+; IN ULONG cksum,
+; IN PUCHAR buf,
+; IN ULONG len
+; )
+;
+; Routine Description:
+;
+; This function computes the checksum of the specified buffer.
+;
+; Arguments:
+;
+; cksum - Suppiles the initial checksum value, in 16-bit form,
+; with the high word set to 0.
+;
+; buf - Supplies a pointer to the buffer to the checksum buffer.
+;
+; len - Supplies the length of the buffer in bytes.
+;
+; Return Value:
+;
+; The computed checksum in 32-bit two-partial-accumulators form, added to
+; the initial checksum, is returned as the function value.
+;
+;--
+
+cksum equ 12 ; stack offset to initial checksum
+buf equ 16 ; stack offset to source address
+len equ 20 ; stack offset to length in words
+
+to_checksum_last_word:
+ jmp checksum_last_word
+
+to_checksum_done:
+ jmp checksum_done
+
+to_checksum_dword_loop_done:
+ jmp checksum_dword_loop_done
+
+cPublicProc tcpxsum,3
+
+ push ebx ; save nonvolatile register
+ push esi ; save nonvolatile register
+
+ mov ecx,[esp + len] ; get length in bytes
+ sub eax,eax ; clear computed checksum
+ test ecx,ecx ; any bytes to checksum at all?
+ jz short to_checksum_done ; no bytes to checksum
+
+;
+; if the checksum buffer is not word aligned, then add the first byte of
+; the buffer to the input checksum.
+;
+
+ mov esi,[esp + buf] ; get source address
+ sub edx,edx ; set up to load word into EDX below
+ test esi,1 ; check if buffer word aligned
+ jz short checksum_word_aligned ; if zf, buffer word aligned
+ mov ah,[esi] ; get first byte (we know we'll have
+ ; to swap at the end)
+ inc esi ; increment buffer address
+ dec ecx ; decrement number of bytes
+ jz short to_checksum_done ; if zf set, no more bytes
+
+;
+; If the buffer is not an even number of of bytes, then initialize
+; the computed checksum with the last byte of the buffer.
+;
+
+checksum_word_aligned: ;
+ shr ecx,1 ; convert to word count
+ jnc short checksum_start ; if nc, even number of bytes
+ mov al,[esi+ecx*2] ; initialize the computed checksum
+ jz short to_checksum_done ; if zf set, no more bytes
+
+;
+; Compute checksum in large blocks of dwords, with one partial word up front if
+; necessary to get dword alignment, and another partial word at the end if
+; needed.
+;
+
+;
+; Compute checksum on the leading word, if that's necessary to get dword
+; alignment.
+;
+
+checksum_start: ;
+ test esi,02h ; check if source dword aligned
+ jz short checksum_dword_aligned ; source is already dword aligned
+ mov dx,[esi] ; get first word to checksum
+ add esi,2 ; update source address
+ add eax,edx ; update partial checksum
+ ; (no carry is possible, because EAX
+ ; and EDX are both 16-bit values)
+ dec ecx ; count off this word (zero case gets
+ ; picked up below)
+
+;
+; Checksum as many words as possible by processing a dword at a time.
+;
+
+checksum_dword_aligned:
+ push ecx ; so we can tell if there's a trailing
+ ; word later
+ shr ecx,1 ; # of dwords to checksum
+ jz short to_checksum_last_word ; no dwords to checksum
+
+ mov edx,[esi] ; preload the first dword
+ add esi,4 ; point to the next dword
+ dec ecx ; count off the dword we just loaded
+ jz short to_checksum_dword_loop_done
+ ; skip the loop if that was the only
+ ; dword
+ mov ebx,ecx ; EBX = # of dwords left to checksum
+ add ecx,LOOP_UNROLLING-1 ; round up loop count
+ shr ecx,LOOP_UNROLLING_BITS ; convert from word count to unrolled
+ ; loop count
+ and ebx,LOOP_UNROLLING-1 ; # of partial dwords to do in first
+ ; loop
+ jz short checksum_dword_loop ; special-case when no partial loop,
+ ; because fixup below doesn't work
+ ; in that case (carry flag is
+ ; cleared at this point, as required
+ ; at loop entry)
+ lea esi,[esi+ebx*4-(LOOP_UNROLLING*4)]
+ ; adjust buffer pointer back to
+ ; compensate for hardwired displacement
+ ; at loop entry point
+ ; ***doesn't change carry flag***
+ jmp loop_entry[ebx*4] ; enter the loop to do the first,
+ ; partial iteration, after which we can
+ ; just do 64-word blocks
+ ; ***doesn't change carry flag***
+
+checksum_dword_loop:
+
+DEFLAB macro pre,suf
+pre&suf:
+ endm
+
+TEMP=0
+ REPT LOOP_UNROLLING
+ deflab loop_entry_,%TEMP
+ adc eax,edx
+ mov edx,[esi + TEMP]
+TEMP=TEMP+4
+ ENDM
+
+checksum_dword_loop_end:
+
+ lea esi,[esi + LOOP_UNROLLING * 4] ; update source address
+ ; ***doesn't change carry flag***
+ dec ecx ; count off unrolled loop iteration
+ ; ***doesn't change carry flag***
+ jnz checksum_dword_loop ; do more blocks
+
+checksum_dword_loop_done label proc
+ adc eax,edx ; finish dword checksum
+ mov edx,0 ; prepare to load trailing word
+ adc eax,edx
+
+;
+; Compute checksum on the trailing word, if there is one.
+; High word of EDX = 0 at this point
+; Carry flag set iff there's a trailing word to do at this point
+;
+
+checksum_last_word label proc ; "proc" so not scoped to function
+ pop ecx ; get back word count
+ test ecx,1 ; is there a trailing word?
+ jz short checksum_done ; no trailing word
+ add ax,[esi] ; add in the trailing word
+ adc eax,0 ;
+
+checksum_done label proc ; "proc" so not scoped to function
+ mov ecx,eax ; fold the checksum to 16 bits
+ ror ecx,16
+ add eax,ecx
+ mov ebx,[esp + buf]
+ shr eax,16
+ test ebx,1 ; check if buffer word aligned
+ jz short checksum_combine ; if zf set, buffer word aligned
+ ror ax,8 ; byte aligned--swap bytes back
+checksum_combine label proc ; "proc" so not scoped to function
+ add ax,word ptr [esp + cksum] ; combine checksums
+ pop esi ; restore nonvolatile register
+ adc eax,0 ;
+ pop ebx ; restore nonvolatile register
+ stdRET tcpxsum
+
+
+REFLAB macro pre,suf
+ dd pre&suf
+ endm
+
+ align 4
+loop_entry label dword
+ dd 0
+TEMP=LOOP_UNROLLING*4
+ REPT LOOP_UNROLLING-1
+TEMP=TEMP-4
+ reflab loop_entry_,%TEMP
+ ENDM
+
+stdENDP tcpxsum
+
+ end
+ \ No newline at end of file
diff --git a/private/ntos/tdi/tcpip/tcp/info.c b/private/ntos/tdi/tcpip/tcp/info.c
new file mode 100644
index 000000000..99fff25bf
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/info.c
@@ -0,0 +1,917 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** INFO.C - TDI Query/SetInformation routines.
+//
+// This file contains the code for dealing with TDI Query/Set information
+// calls.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#ifdef VXD
+#include "tdivxd.h"
+#include "tdistat.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "tcp.h"
+#include "tcb.h"
+#include "tcpconn.h"
+#include "tlcommon.h"
+#include "info.h"
+#include "tdiinfo.h"
+#include "tcpcfg.h"
+#include "udp.h"
+#include "tcpsend.h"
+
+#ifndef UDP_ONLY
+#define MY_SERVICE_FLAGS (TDI_SERVICE_CONNECTION_MODE | \
+ TDI_SERVICE_ORDERLY_RELEASE | \
+ TDI_SERVICE_CONNECTIONLESS_MODE | \
+ TDI_SERVICE_ERROR_FREE_DELIVERY | \
+ TDI_SERVICE_BROADCAST_SUPPORTED | \
+ TDI_SERVICE_DELAYED_ACCEPTANCE | \
+ TDI_SERVICE_EXPEDITED_DATA | \
+ TDI_SERVICE_NO_ZERO_LENGTH)
+#else
+#define MY_SERVICE_FLAGS (TDI_SERVICE_CONNECTIONLESS_MODE | \
+ TDI_SERVICE_BROADCAST_SUPPORTED)
+#endif
+
+extern uint StartTime;
+EXTERNAL_LOCK(AddrObjTableLock)
+
+#ifndef UDP_ONLY
+TCPStats TStats;
+#endif
+
+UDPStats UStats;
+
+struct ReadTableStruct {
+ uint (*rts_validate)(void *Context, uint *Valid);
+ uint (*rts_readnext)(void *Context, void *OutBuf);
+};
+
+struct ReadTableStruct ReadAOTable =
+ {ValidateAOContext, ReadNextAO};
+
+#ifndef UDP_ONLY
+
+struct ReadTableStruct ReadTCBTable =
+ {ValidateTCBContext, ReadNextTCB};
+
+EXTERNAL_LOCK(TCBTableLock)
+#endif
+
+EXTERNAL_LOCK(AddrObjTableLock)
+
+extern IPInfo LocalNetInfo;
+
+struct TDIEntityID *EntityList;
+uint EntityCount;
+
+//* TdiQueryInformation - Query Information handler.
+//
+// The TDI QueryInformation routine. Called when the client wants to
+// query information on a connection, the provider as a whole, or to
+// get statistics.
+//
+// Input: Request - The request structure for this command.
+// QueryType - The type of query to be performed.
+// Buffer - Buffer to place data into.
+// BufferSize - Pointer to size in bytes of buffer. On return,
+// filled in with bytes copied.
+// IsConn - Valid only for TDI_QUERY_ADDRESS_INFO. TRUE
+// if we are querying the address info on
+// a connection.
+//
+// Returns: Status of attempt to query information.
+//
+TDI_STATUS
+TdiQueryInformation(PTDI_REQUEST Request, uint QueryType, PNDIS_BUFFER Buffer,
+ uint *BufferSize, uint IsConn)
+{
+ union {
+ TDI_CONNECTION_INFO ConnInfo;
+ TDI_ADDRESS_INFO AddrInfo;
+ TDI_PROVIDER_INFO ProviderInfo;
+ TDI_PROVIDER_STATISTICS ProviderStats;
+ } InfoBuf;
+
+ uint InfoSize;
+ CTELockHandle ConnTableHandle, TCBHandle, AddrHandle, AOHandle;
+#ifndef UDP_ONLY
+ TCPConn *Conn;
+ TCB *InfoTCB;
+#endif
+ AddrObj *InfoAO;
+ void *InfoPtr = NULL;
+ uint Offset;
+ uint Size;
+ uint BytesCopied;
+
+ switch (QueryType) {
+
+ case TDI_QUERY_BROADCAST_ADDRESS:
+ return TDI_INVALID_QUERY;
+ break;
+
+ case TDI_QUERY_PROVIDER_INFO:
+ InfoBuf.ProviderInfo.Version = 0x100;
+#ifndef UDP_ONLY
+ InfoBuf.ProviderInfo.MaxSendSize = 0xffffffff;
+#else
+ InfoBuf.ProviderInfo.MaxSendSize = 0;
+#endif
+ InfoBuf.ProviderInfo.MaxConnectionUserData = 0;
+ InfoBuf.ProviderInfo.MaxDatagramSize = 0xffff - sizeof(UDPHeader);
+ InfoBuf.ProviderInfo.ServiceFlags = MY_SERVICE_FLAGS;
+ InfoBuf.ProviderInfo.MinimumLookaheadData = 1;
+ InfoBuf.ProviderInfo.MaximumLookaheadData = 0xffff;
+ InfoBuf.ProviderInfo.NumberOfResources = 0;
+ InfoBuf.ProviderInfo.StartTime.LowPart = StartTime;
+ InfoBuf.ProviderInfo.StartTime.HighPart = 0;
+ InfoSize = sizeof(TDI_PROVIDER_INFO);
+ InfoPtr = &InfoBuf.ProviderInfo;
+ break;
+
+ case TDI_QUERY_ADDRESS_INFO:
+ InfoSize = sizeof(TDI_ADDRESS_INFO) - sizeof(TRANSPORT_ADDRESS) +
+ TCP_TA_SIZE;
+ CTEMemSet(&InfoBuf.AddrInfo, 0, TCP_TA_SIZE);
+ InfoBuf.AddrInfo.ActivityCount = 1; // Since noone knows what
+ // this means, we'll set
+ // it to one.
+
+ if (IsConn) {
+#ifdef UDP_ONLY
+ return TDI_INVALID_QUERY;
+#else
+
+ CTEGetLock(&AddrObjTableLock, &AddrHandle);
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+ Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ InfoTCB = Conn->tc_tcb;
+ // If we have a TCB we'll
+ // return information about that TCB. Otherwise we'll return
+ // info about the address object.
+ if (InfoTCB != NULL) {
+ CTEStructAssert(InfoTCB, tcb);
+ CTEGetLock(&InfoTCB->tcb_lock, &TCBHandle);
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+ CTEFreeLock(&AddrObjTableLock, ConnTableHandle);
+ BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address,
+ InfoTCB->tcb_saddr, InfoTCB->tcb_sport);
+ CTEFreeLock(&InfoTCB->tcb_lock, AddrHandle);
+ InfoPtr = &InfoBuf.AddrInfo;
+ break;
+ } else {
+ // No TCB, return info on the AddrObj.
+ InfoAO = Conn->tc_ao;
+ if (InfoAO != NULL) {
+ // We have an AddrObj.
+ CTEStructAssert(InfoAO, ao);
+ CTEGetLock(&InfoAO->ao_lock, &AOHandle);
+ BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address,
+ InfoAO->ao_addr, InfoAO->ao_port);
+ CTEFreeLock(&InfoAO->ao_lock, AOHandle);
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ CTEFreeLock(&AddrObjTableLock, AddrHandle);
+ InfoPtr = &InfoBuf.AddrInfo;
+ break;
+ }
+ }
+
+ }
+
+ // Fall through to here when we can't find the connection, or
+ // the connection isn't associated.
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ CTEFreeLock(&AddrObjTableLock, AddrHandle);
+ return TDI_INVALID_CONNECTION;
+ break;
+
+#endif
+ } else {
+ // Asking for information on an addr. object.
+#ifdef VXD
+ InfoAO = GetIndexedAO((uint)Request->Handle.AddressHandle);
+
+ if (InfoAO == NULL)
+ return TDI_ADDR_INVALID;
+#else
+ InfoAO = Request->Handle.AddressHandle;
+#endif
+
+ CTEStructAssert(InfoAO, ao);
+
+ CTEGetLock(&InfoAO->ao_lock, &AOHandle);
+
+ if (!AO_VALID(InfoAO)) {
+ CTEFreeLock(&InfoAO->ao_lock, AOHandle);
+ return TDI_ADDR_INVALID;
+ }
+
+ BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address,
+ InfoAO->ao_addr, InfoAO->ao_port);
+ CTEFreeLock(&InfoAO->ao_lock, AOHandle);
+ InfoPtr = &InfoBuf.AddrInfo;
+ break;
+ }
+
+ break;
+
+ case TDI_QUERY_CONNECTION_INFO:
+#ifndef UDP_ONLY
+ InfoSize = sizeof(TDI_CONNECTION_INFO);
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+ Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ InfoTCB = Conn->tc_tcb;
+ // If we have a TCB we'll return the information. Otherwise
+ // we'll error out.
+ if (InfoTCB != NULL) {
+
+ ulong TotalTime;
+ ulong BPS, PathBPS;
+ IP_STATUS IPStatus;
+ CTEULargeInt TempULargeInt;
+
+ CTEStructAssert(InfoTCB, tcb);
+ CTEGetLock(&InfoTCB->tcb_lock, &TCBHandle);
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+ CTEMemSet(&InfoBuf.ConnInfo, 0, sizeof(TDI_CONNECTION_INFO));
+ InfoBuf.ConnInfo.State = (ulong)InfoTCB->tcb_state;
+ IPStatus = (*LocalNetInfo.ipi_getpinfo)(InfoTCB->tcb_daddr,
+ InfoTCB->tcb_saddr, NULL, &PathBPS);
+
+ if (IPStatus != IP_SUCCESS) {
+ InfoBuf.ConnInfo.Throughput.LowPart = 0xFFFFFFFF;
+ InfoBuf.ConnInfo.Throughput.HighPart = 0xFFFFFFFF;
+ } else {
+ InfoBuf.ConnInfo.Throughput.HighPart = 0;
+ TotalTime = InfoTCB->tcb_totaltime /
+ (1000 / MS_PER_TICK);
+ if (TotalTime != 0) {
+ TempULargeInt.LowPart = InfoTCB->tcb_bcountlow;
+ TempULargeInt.HighPart = InfoTCB->tcb_bcounthi;
+
+ BPS = CTEEnlargedUnsignedDivide(TempULargeInt,
+ TotalTime, NULL);
+ InfoBuf.ConnInfo.Throughput.LowPart =
+ MIN(BPS, PathBPS);
+ } else
+ InfoBuf.ConnInfo.Throughput.LowPart = PathBPS;
+ }
+
+
+
+ // To figure the delay we use the rexmit timeout. Our
+ // rexmit timeout is roughly the round trip time plus
+ // some slop, so we use half of that as the one way delay.
+#ifdef VXD
+ InfoBuf.ConnInfo.Delay.LowPart =
+ (REXMIT_TO(InfoTCB) * MS_PER_TICK) / 2;
+ InfoBuf.ConnInfo.Throughput.HighPart = 0;
+#else // VXD
+ InfoBuf.ConnInfo.Delay.LowPart =
+ (REXMIT_TO(InfoTCB) * MS_PER_TICK) / 2;
+ InfoBuf.ConnInfo.Delay.HighPart = 0;
+ //
+ // Convert milliseconds to 100ns and negate for relative
+ // time.
+ //
+ InfoBuf.ConnInfo.Delay =
+ RtlExtendedIntegerMultiply(
+ InfoBuf.ConnInfo.Delay,
+ 10000
+ );
+
+ CTEAssert(InfoBuf.ConnInfo.Delay.HighPart == 0);
+
+ InfoBuf.ConnInfo.Delay.QuadPart =
+ -InfoBuf.ConnInfo.Delay.QuadPart;
+
+#endif // VXD
+ CTEFreeLock(&InfoTCB->tcb_lock, ConnTableHandle);
+ InfoPtr = &InfoBuf.ConnInfo;
+ break;
+ }
+
+ }
+
+ // Come through here if we can't find the connection or it has
+ // no TCB.
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ return TDI_INVALID_CONNECTION;
+ break;
+
+#else // UDP_ONLY
+ return TDI_INVALID_QUERY;
+ break;
+#endif // UDP_ONLY
+ case TDI_QUERY_PROVIDER_STATISTICS:
+ CTEMemSet(&InfoBuf.ProviderStats, 0, sizeof(TDI_PROVIDER_STATISTICS));
+ InfoBuf.ProviderStats.Version = 0x100;
+ InfoSize = sizeof(TDI_PROVIDER_STATISTICS);
+ InfoPtr = &InfoBuf.ProviderStats;
+ break;
+ default:
+ return TDI_INVALID_QUERY;
+ break;
+ }
+
+ // When we get here, we've got the pointers set up and the information
+ // filled in.
+
+ CTEAssert(InfoPtr != NULL);
+ Offset = 0;
+ Size = *BufferSize;
+ (void)CopyFlatToNdis(Buffer, InfoPtr, MIN(InfoSize, Size), &Offset,
+ &BytesCopied);
+ if (Size < InfoSize)
+ return TDI_BUFFER_OVERFLOW;
+ else {
+ *BufferSize = InfoSize;
+ return TDI_SUCCESS;
+ }
+}
+
+//* TdiSetInformation - Set Information handler.
+//
+// The TDI SetInformation routine. Currently we don't allow anything to be
+// set.
+//
+// Input: Request - The request structure for this command.
+// SetType - The type of set to be performed.
+// Buffer - Buffer to set from.
+// BufferSize - Size in bytes of buffer.
+// IsConn - Valid only for TDI_QUERY_ADDRESS_INFO. TRUE
+// if we are setting the address info on
+// a connection.
+//
+// Returns: Status of attempt to set information.
+//
+TDI_STATUS
+TdiSetInformation(PTDI_REQUEST Request, uint SetType, PNDIS_BUFFER Buffer,
+ uint BufferSize, uint IsConn)
+{
+ return TDI_INVALID_REQUEST;
+}
+
+//* TdiAction - Action handler.
+//
+// The TDI Action routine. Currently we don't support any actions.
+//
+// Input: Request - The request structure for this command.
+// ActionType - The type of action to be performed.
+// Buffer - Buffer of action info.
+// BufferSize - Size in bytes of buffer.
+//
+// Returns: Status of attempt to perform action.
+//
+TDI_STATUS
+TdiAction(PTDI_REQUEST Request, uint ActionType, PNDIS_BUFFER Buffer,
+ uint BufferSize)
+{
+ return TDI_INVALID_REQUEST;
+}
+
+//* TdiQueryInfoEx - Extended TDI query information.
+//
+// This is the new TDI query information handler. We take in a TDIObjectID
+// structure, a buffer and length, and some context information, and return
+// the requested information if possible.
+//
+// Input: Request - The request structure for this command.
+// ID - The object ID
+// Buffer - Pointer to buffer to be filled in.
+// Size - Pointer to size in bytes of Buffer. On exit,
+// filled in with bytes written.
+// Context - Pointer to context buffer.
+//
+// Returns: Status of attempt to get information.
+//
+TDI_STATUS
+TdiQueryInformationEx(PTDI_REQUEST Request, TDIObjectID *ID,
+ PNDIS_BUFFER Buffer, uint *Size, void *Context)
+{
+ uint BufferSize = *Size;
+ uint InfoSize;
+ void *InfoPtr;
+ uint Fixed;
+ CTELockHandle Handle;
+#ifndef VXD
+ CTELock *LockPtr;
+#else
+#ifdef DEBUG
+ CTELock *LockPtr;
+#endif
+#endif
+ uint Offset = 0;
+ uchar InfoBuffer[sizeof(TCPConnTableEntry)];
+ uint BytesRead;
+ uint Valid;
+ uint Entity;
+ uint BytesCopied;
+
+ // First check to see if he's querying for list of entities.
+ Entity = ID->toi_entity.tei_entity;
+ if (Entity == GENERIC_ENTITY) {
+ *Size = 0;
+
+ if (ID->toi_class != INFO_CLASS_GENERIC ||
+ ID->toi_type != INFO_TYPE_PROVIDER ||
+ ID->toi_id != ENTITY_LIST_ID) {
+ return TDI_INVALID_PARAMETER;
+ }
+
+ // Make sure we have room for it the list in the buffer.
+ InfoSize = EntityCount * sizeof(TDIEntityID);
+
+ if (BufferSize < InfoSize) {
+ // Not enough room.
+ return TDI_BUFFER_TOO_SMALL;
+ }
+
+ *Size = InfoSize;
+
+ // Copy it in, free our temp. buffer, and return success.
+ (void)CopyFlatToNdis(Buffer, (uchar *)EntityList, InfoSize, &Offset,
+ &BytesCopied);
+ return TDI_SUCCESS;
+ }
+
+
+ //* Check the level. If it can't be for us, pass it down.
+#ifndef UDP_ONLY
+ if (Entity != CO_TL_ENTITY && Entity != CL_TL_ENTITY) {
+#else
+ if (Entity != CL_TL_ENTITY) {
+#endif
+
+ // When we support multiple lower entities at this layer we'll have
+ // to figure out which one to dispatch to. For now, just pass it
+ // straight down.
+ return (*LocalNetInfo.ipi_qinfo)(ID, Buffer, Size, Context);
+ }
+
+ if (ID->toi_entity.tei_instance != TL_INSTANCE) {
+ // We only support a single instance.
+ return TDI_INVALID_REQUEST;
+ }
+
+ // Zero returned parameters in case of an error below.
+ *Size = 0;
+
+ if (ID->toi_class == INFO_CLASS_GENERIC) {
+ // This is a generic request.
+ if (ID->toi_type == INFO_TYPE_PROVIDER && ID->toi_id == ENTITY_TYPE_ID) {
+ if (BufferSize >= sizeof(uint)) {
+ *(uint *)&InfoBuffer[0] = (Entity == CO_TL_ENTITY) ? CO_TL_TCP
+ : CL_TL_UDP;
+ (void)CopyFlatToNdis(Buffer, InfoBuffer, sizeof(uint), &Offset,
+ &BytesCopied);
+ return TDI_SUCCESS;
+ } else
+ return TDI_BUFFER_TOO_SMALL;
+ }
+ return TDI_INVALID_PARAMETER;
+ }
+
+ if (ID->toi_class == INFO_CLASS_PROTOCOL) {
+ // Handle protocol specific class of information. For us, this is
+ // the MIB-2 stuff or the minimal stuff we do for oob_inline support.
+
+#ifndef UDP_ONLY
+ if (ID->toi_type == INFO_TYPE_CONNECTION) {
+ TCPConn *Conn;
+ TCB *QueryTCB;
+ TCPSocketAMInfo *AMInfo;
+ CTELockHandle TCBHandle;
+
+ if (BufferSize < sizeof(TCPSocketAMInfo) ||
+ ID->toi_id != TCP_SOCKET_ATMARK)
+ return TDI_INVALID_PARAMETER;
+
+ AMInfo = (TCPSocketAMInfo *)InfoBuffer;
+ CTEGetLock(&ConnTableLock, &Handle);
+
+ Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ QueryTCB = Conn->tc_tcb;
+ if (QueryTCB != NULL) {
+ CTEStructAssert(QueryTCB, tcb);
+ CTEGetLock(&QueryTCB->tcb_lock, &TCBHandle);
+ if ((QueryTCB->tcb_flags & (URG_INLINE | URG_VALID)) ==
+ (URG_INLINE | URG_VALID)) {
+ // We're in inline mode, and the urgent data fields are
+ // valid.
+ AMInfo->tsa_size = QueryTCB->tcb_urgend -
+ QueryTCB->tcb_urgstart + 1;
+ // Rcvnext - pendingcnt is the sequence number of the
+ // next byte of data that will be delivered to the
+ // client. Urgend - that value is the offset in the
+ // data stream of the end of urgent data.
+ AMInfo->tsa_offset = QueryTCB->tcb_urgend -
+ (QueryTCB->tcb_rcvnext - QueryTCB->tcb_pendingcnt);
+ } else {
+ AMInfo->tsa_size = 0;
+ AMInfo->tsa_offset = 0;
+ }
+ CTEFreeLock(&QueryTCB->tcb_lock, TCBHandle);
+ *Size = sizeof(TCPSocketAMInfo);
+ CopyFlatToNdis(Buffer, InfoBuffer, sizeof(TCPSocketAMInfo),
+ &Offset, &BytesCopied);
+ return TDI_SUCCESS;
+ }
+ }
+ return TDI_INVALID_PARAMETER;
+
+ }
+
+#endif
+ if (ID->toi_type != INFO_TYPE_PROVIDER)
+ return TDI_INVALID_PARAMETER;
+
+ switch (ID->toi_id) {
+
+ case UDP_MIB_STAT_ID:
+#if UDP_MIB_STAT_ID != TCP_MIB_STAT_ID
+ case TCP_MIB_STAT_ID:
+#endif
+ Fixed = TRUE;
+ if (Entity == CL_TL_ENTITY) {
+ InfoSize = sizeof(UDPStats);
+ InfoPtr = &UStats;
+ } else {
+#ifndef UDP_ONLY
+ InfoSize = sizeof(TCPStats);
+ InfoPtr = &TStats;
+#else
+ return TDI_INVALID_PARAMETER;
+#endif
+ }
+ break;
+ case UDP_MIB_TABLE_ID:
+#if UDP_MIB_TABLE_ID != TCP_MIB_TABLE_ID
+ case TCP_MIB_TABLE_ID:
+#endif
+ Fixed = FALSE;
+ if (Entity == CL_TL_ENTITY) {
+ InfoSize = sizeof(UDPEntry);
+ InfoPtr = &ReadAOTable;
+ CTEGetLock(&AddrObjTableLock, &Handle);
+#ifndef VXD
+ LockPtr = &AddrObjTableLock;
+#else
+#ifdef DEBUG
+ LockPtr = &AddrObjTableLock;
+#endif
+#endif
+ } else {
+#ifndef UDP_ONLY
+ InfoSize = sizeof(TCPConnTableEntry);
+ InfoPtr = &ReadTCBTable;
+ CTEGetLock(&TCBTableLock, &Handle);
+#ifndef VXD
+ LockPtr = &TCBTableLock;
+#else
+#ifdef DEBUG
+ LockPtr = &TCBTableLock;
+#endif
+#endif
+
+#else
+ return TDI_INVALID_PARAMETER;
+#endif
+ }
+ break;
+ default:
+ return TDI_INVALID_PARAMETER;
+ break;
+ }
+
+ if (Fixed) {
+ if (BufferSize < InfoSize)
+ return TDI_BUFFER_TOO_SMALL;
+
+ *Size = InfoSize;
+
+ (void)CopyFlatToNdis(Buffer, InfoPtr, InfoSize, &Offset,
+ &BytesCopied);
+ return TDI_SUCCESS;
+ } else {
+ struct ReadTableStruct *RTSPtr;
+ uint ReadStatus;
+
+ // Have a variable length (or mult-instance) structure to copy.
+ // InfoPtr points to the structure describing the routines to
+ // call to read the table.
+ // Loop through up to CountWanted times, calling the routine
+ // each time.
+ BytesRead = 0;
+
+ RTSPtr = InfoPtr;
+
+ ReadStatus = (*(RTSPtr->rts_validate))(Context, &Valid);
+
+ // If we successfully read something we'll continue. Otherwise
+ // we'll bail out.
+ if (!Valid) {
+ CTEFreeLock(LockPtr, Handle);
+ return TDI_INVALID_PARAMETER;
+ }
+
+ while (ReadStatus) {
+ // The invariant here is that there is data in the table to
+ // read. We may or may not have room for it. So ReadStatus
+ // is TRUE, and BufferSize - BytesRead is the room left
+ // in the buffer.
+ if ((int)(BufferSize - BytesRead) >= (int)InfoSize) {
+ ReadStatus = (*(RTSPtr->rts_readnext))(Context,
+ InfoBuffer);
+ BytesRead += InfoSize;
+ Buffer = CopyFlatToNdis(Buffer, InfoBuffer, InfoSize,
+ &Offset, &BytesCopied);
+ } else
+ break;
+
+ }
+
+ *Size = BytesRead;
+ CTEFreeLock(LockPtr, Handle);
+ return (!ReadStatus ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW);
+ }
+
+ }
+
+ if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) {
+ // We want to return implementation specific info. For now, error out.
+ return TDI_INVALID_PARAMETER;
+ }
+
+ return TDI_INVALID_PARAMETER;
+
+}
+
+//* TdiSetInfoEx - Extended TDI set information.
+//
+// This is the new TDI set information handler. We take in a TDIObjectID
+// structure, a buffer and length. We set the object specifed by the ID
+// (and possibly by the Request) to the value specified in the buffer.
+//
+// Input: Request - The request structure for this command.
+// ID - The object ID
+// Buffer - Pointer to buffer containing value to set.
+// Size - Size in bytes of Buffer.
+//
+// Returns: Status of attempt to get information.
+//
+TDI_STATUS
+TdiSetInformationEx(PTDI_REQUEST Request, TDIObjectID *ID, void *Buffer,
+ uint Size)
+{
+ TCPConnTableEntry *TCPEntry;
+ CTELockHandle TableHandle, TCBHandle;
+ TCB *SetTCB;
+ uint Entity;
+ TCPConn *Conn;
+ TDI_STATUS Status;
+
+
+ //* Check the level. If it can't be for us, pass it down.
+ Entity = ID->toi_entity.tei_entity;
+
+ if (Entity != CO_TL_ENTITY && Entity != CL_TL_ENTITY) {
+ // Someday we'll have to figure out how to dispatch. For now, just pass
+ // it down.
+ return (*LocalNetInfo.ipi_setinfo)(ID, Buffer, Size);
+ }
+
+ if (ID->toi_entity.tei_instance != TL_INSTANCE)
+ return TDI_INVALID_REQUEST;
+
+ if (ID->toi_class == INFO_CLASS_GENERIC) {
+ // Fill this in when we have generic class defines.
+ return TDI_INVALID_PARAMETER;
+ }
+
+ //* Now look at the rest of it.
+ if (ID->toi_class == INFO_CLASS_PROTOCOL) {
+ // Handle protocol specific class of information. For us, this is
+ // the MIB-2 stuff, as well as common sockets options,
+ // and in particular the setting of the state of a TCP connection.
+
+ if (ID->toi_type == INFO_TYPE_CONNECTION) {
+ TCPSocketOption *Option;
+ uint Flag;
+ uint Value;
+
+#ifndef UDP_ONLY
+ // A connection type. Get the connection, and then figure out
+ // what to do with it.
+ Status = TDI_INVALID_PARAMETER;
+
+ if (Size < sizeof(TCPSocketOption))
+ return Status;
+
+ CTEGetLock(&ConnTableLock, &TableHandle);
+
+ Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ Status = TDI_SUCCESS;
+
+ if (ID->toi_id == TCP_SOCKET_WINDOW) {
+ // This is a funny option, because it doesn't involve
+ // flags. Handle this specially.
+ Option = (TCPSocketOption *)Buffer;
+
+ // We don't allow anyone to shrink the window, as this
+ // gets too weird from a protocol point of view. Also,
+ // make sure they don't try and set anything too big.
+ if (Option->tso_value > 0xffff)
+ Status = TDI_INVALID_PARAMETER;
+ else if (Option->tso_value > Conn->tc_window ||
+ Conn->tc_tcb == NULL) {
+ Conn->tc_flags |= CONN_WINSET;
+ Conn->tc_window = Option->tso_value;
+ SetTCB = Conn->tc_tcb;
+
+ if (SetTCB != NULL) {
+ CTEStructAssert(SetTCB, tcb);
+ CTEGetLock(&SetTCB->tcb_lock, &TCBHandle);
+ CTEAssert(Option->tso_value > SetTCB->tcb_defaultwin);
+ if (DATA_RCV_STATE(SetTCB->tcb_state) &&
+ !CLOSING(SetTCB)) {
+ SetTCB->tcb_flags |= WINDOW_SET;
+ SetTCB->tcb_defaultwin = Option->tso_value;
+ SetTCB->tcb_refcnt++;
+ CTEFreeLock(&SetTCB->tcb_lock, TCBHandle);
+ SendACK(SetTCB);
+ CTEGetLock(&SetTCB->tcb_lock, &TCBHandle);
+ DerefTCB(SetTCB, TCBHandle);
+ } else {
+ CTEFreeLock(&SetTCB->tcb_lock, TCBHandle);
+ }
+ }
+ }
+ CTEFreeLock(&ConnTableLock, TableHandle);
+ return Status;
+ }
+
+ Flag = 0;
+ Option = (TCPSocketOption *)Buffer;
+ Value = Option->tso_value;
+ // We have the connection, so figure out which flag to set.
+ switch (ID->toi_id) {
+
+ case TCP_SOCKET_NODELAY:
+ Value = !Value;
+ Flag = NAGLING;
+ break;
+ case TCP_SOCKET_KEEPALIVE:
+ Flag = KEEPALIVE;
+ break;
+ case TCP_SOCKET_BSDURGENT:
+ Flag = BSD_URGENT;
+ break;
+ case TCP_SOCKET_OOBINLINE:
+ Flag = URG_INLINE;
+ break;
+ default:
+ Status = TDI_INVALID_PARAMETER;
+ break;
+ }
+
+ if (Status == TDI_SUCCESS) {
+ if (Value)
+ Conn->tc_tcbflags |= Flag;
+ else
+ Conn->tc_tcbflags &= ~Flag;
+
+ SetTCB = Conn->tc_tcb;
+ if (SetTCB != NULL) {
+ CTEStructAssert(SetTCB, tcb);
+ CTEGetLock(&SetTCB->tcb_lock, &TCBHandle);
+ if (Value)
+ SetTCB->tcb_flags |= Flag;
+ else
+ SetTCB->tcb_flags &= ~Flag;
+
+ if (ID->toi_id == TCP_SOCKET_KEEPALIVE) {
+ SetTCB->tcb_alive = TCPTime;
+ SetTCB->tcb_kacount = 0;
+ }
+
+ CTEFreeLock(&SetTCB->tcb_lock, TCBHandle);
+ }
+ }
+ }
+
+ CTEFreeLock(&ConnTableLock, TableHandle);
+ return Status;
+#else
+ return TDI_INVALID_PARAMETER;
+#endif
+ }
+
+ if (ID->toi_type == INFO_TYPE_ADDRESS_OBJECT) {
+ // We're setting information on an address object. This is
+ // pretty simple.
+
+ return SetAddrOptions(Request, ID->toi_id, Size, Buffer);
+
+ }
+
+
+ if (ID->toi_type != INFO_TYPE_PROVIDER)
+ return TDI_INVALID_PARAMETER;
+
+#ifndef UDP_ONLY
+ if (ID->toi_id == TCP_MIB_TABLE_ID) {
+ if (Size != sizeof(TCPConnTableEntry))
+ return TDI_INVALID_PARAMETER;
+
+ TCPEntry = (TCPConnTableEntry *)Buffer;
+
+ if (TCPEntry->tct_state != TCP_DELETE_TCB)
+ return TDI_INVALID_PARAMETER;
+
+ // We have an apparently valid request. Look up the TCB.
+ CTEGetLock(&TCBTableLock, &TableHandle);
+ SetTCB = FindTCB(TCPEntry->tct_localaddr,
+ TCPEntry->tct_remoteaddr, (ushort)TCPEntry->tct_remoteport,
+ (ushort)TCPEntry->tct_localport);
+
+ // We found him. If he's not closing or closed, close him.
+ if (SetTCB != NULL) {
+ CTEGetLock(&SetTCB->tcb_lock, &TCBHandle);
+ CTEFreeLock(&TCBTableLock, TCBHandle);
+
+ // We've got him. Bump his ref. count, and call TryToCloseTCB
+ // to mark him as closing. Then notify the upper layer client
+ // of the disconnect.
+ SetTCB->tcb_refcnt++;
+ if (SetTCB->tcb_state != TCB_CLOSED && !CLOSING(SetTCB)) {
+ SetTCB->tcb_flags |= NEED_RST;
+ TryToCloseTCB(SetTCB, TCB_CLOSE_ABORTED, TableHandle);
+ CTEGetLock(&SetTCB->tcb_lock, &TableHandle);
+
+ if (SetTCB->tcb_state != TCB_TIME_WAIT) {
+ // Remove him from the TCB, and notify the client.
+ CTEFreeLock(&SetTCB->tcb_lock, TableHandle);
+ RemoveTCBFromConn(SetTCB);
+ NotifyOfDisc(SetTCB, NULL, TDI_CONNECTION_RESET);
+ CTEGetLock(&SetTCB->tcb_lock, &TableHandle);
+ }
+
+ }
+
+ DerefTCB(SetTCB, TableHandle);
+ return TDI_SUCCESS;
+ } else {
+ CTEFreeLock(&TCBTableLock, TableHandle);
+ return TDI_INVALID_PARAMETER;
+ }
+ } else
+ return TDI_INVALID_PARAMETER;
+#else
+ return TDI_INVALID_PARAMETER;
+#endif
+
+ }
+
+ if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) {
+ // We want to return implementation specific info. For now, error out.
+ return TDI_INVALID_REQUEST;
+ }
+
+ return TDI_INVALID_REQUEST;
+
+
+}
diff --git a/private/ntos/tdi/tcpip/tcp/info.h b/private/ntos/tdi/tcpip/tcp/info.h
new file mode 100644
index 000000000..f5274160f
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/info.h
@@ -0,0 +1,51 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** INFO.H - TDI Query/SetInfo and Action definitons.
+//
+// This file contains definitions for the file info.c.
+//
+
+#include "tcpinfo.h"
+
+#define TL_INSTANCE 0
+
+#ifndef UDP_ONLY
+extern TCPStats TStats;
+
+typedef struct TCPConnContext {
+ uint tcc_index;
+ struct TCB *tcc_tcb;
+} TCPConnContext;
+
+#define TCB_STATE_DELTA 1
+
+#endif
+
+typedef struct UDPContext {
+ uint uc_index;
+ struct AddrObj *uc_ao;
+} UDPContext;
+
+extern UDPStats UStats;
+extern struct TDIEntityID *EntityList;
+extern uint EntityCount;
+
+extern TDI_STATUS TdiQueryInformation(PTDI_REQUEST Request, uint QueryType,
+ PNDIS_BUFFER Buffer, uint *BufferSize, uint IsConn);
+
+extern TDI_STATUS TdiSetInformation(PTDI_REQUEST Request, uint SetType,
+ PNDIS_BUFFER Buffer, uint BufferSize, uint IsConn);
+
+extern TDI_STATUS TdiAction(PTDI_REQUEST Request, uint ActionType,
+ PNDIS_BUFFER Buffer, uint BufferSize);
+
+extern TDI_STATUS TdiQueryInformationEx(PTDI_REQUEST Request,
+ struct TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, void *Context);
+
+extern TDI_STATUS TdiSetInformationEx(PTDI_REQUEST Request,
+ struct TDIObjectID *ID, void *Buffer, uint Size);
+
diff --git a/private/ntos/tdi/tcpip/tcp/init.c b/private/ntos/tdi/tcpip/tcp/init.c
new file mode 100644
index 000000000..2a889d88a
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/init.c
@@ -0,0 +1,597 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** INIT.C - TCP/UDP init code.
+//
+// This file contain init code for the TCP/UDP driver. Some things
+// here are ifdef'ed for building a UDP only version.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#ifdef NT
+#include <tdikrnl.h>
+#endif
+#ifdef VXD
+#include "tdivxd.h"
+#include "tdistat.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "udp.h"
+#include "raw.h"
+#include "info.h"
+#ifndef UDP_ONLY
+#include "tcp.h"
+#include "tcpsend.h"
+#include "tcb.h"
+#include "tcpconn.h"
+#include "tcpdeliv.h"
+#include "tlcommon.h"
+
+extern int InitTCPRcv(void);
+extern void UnInitTCPRcv(void);
+#endif // UDP_ONLY
+
+#include "tdiinfo.h"
+#include "tcpcfg.h"
+
+
+//* Definitions of global variables.
+IPInfo LocalNetInfo;
+
+uint DeadGWDetect;
+uint PMTUDiscovery;
+uint PMTUBHDetect;
+uint KeepAliveTime;
+uint KAInterval;
+uint DefaultRcvWin;
+uint MaxConnections;
+uint MaxConnectRexmitCount;
+uint MaxConnectResponseRexmitCount;
+#ifdef SYN_ATTACK
+uint MaxConnectResponseRexmitCountTmp;
+#endif
+uint MaxDataRexmitCount;
+uint BSDUrgent;
+uint FinWait2TO;
+uint NTWMaxConnectCount;
+uint NTWMaxConnectTime;
+uint MaxUserPort;
+
+#ifdef SECFLTR
+uint SecurityFilteringEnabled;
+#endif // SECFLTR
+
+#ifdef VXD
+uint PreloadCount;
+#endif
+
+#ifdef _PNP_POWER
+HANDLE AddressChangeHandle;
+#endif
+
+#ifdef VXD
+TDIDispatchTable TLDispatch;
+
+#ifndef CHICAGO
+char TransportName[] = "MSTCP";
+#else
+char TransportName[] = TCP_NAME;
+#endif
+
+#ifdef CHICAGO
+extern int RegisterAddrChangeHndlr(void *Handler, uint Add);
+#endif
+
+#endif
+
+uint StartTime;
+
+extern void *UDPProtInfo;
+extern void *RawProtInfo;
+
+extern int InitTCPConn(void);
+extern void UnInitTCPConn(void);
+extern IP_STATUS TLGetIPInfo(IPInfo *Buffer, int Size);
+
+
+
+//
+// All of the init code can be discarded.
+//
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+
+int tlinit();
+
+#pragma alloc_text(INIT, tlinit)
+
+#endif // ALLOC_PRAGMA
+#endif
+
+//* Dummy routines for UDP only version. All of these routines return
+// 'Invalid Request'.
+
+#ifdef UDP_ONLY
+TDI_STATUS
+TdiOpenConnection(PTDI_REQUEST Request, PVOID Context)
+{
+ return TDI_INVALID_REQUEST;
+}
+TDI_STATUS
+TdiCloseConnection(PTDI_REQUEST Request)
+{
+ return TDI_INVALID_REQUEST;
+}
+
+TDI_STATUS
+TdiAssociateAddress(PTDI_REQUEST Request, HANDLE AddrHandle)
+{
+ return TDI_INVALID_REQUEST;
+}
+TDI_STATUS TdiDisAssociateAddress(PTDI_REQUEST Request)
+{
+ return TDI_INVALID_REQUEST;
+}
+TDI_STATUS TdiConnect(PTDI_REQUEST Request, void *Timeout,
+ PTDI_CONNECTION_INFORMATION RequestAddr,
+ PTDI_CONNECTION_INFORMATION ReturnAddr)
+{
+ return TDI_INVALID_REQUEST;
+}
+TDI_STATUS TdiListen(PTDI_REQUEST Request, ushort Flags,
+ PTDI_CONNECTION_INFORMATION AcceptableAddr,
+ PTDI_CONNECTION_INFORMATION ConnectedAddr)
+{
+ return TDI_INVALID_REQUEST;
+}
+TDI_STATUS TdiAccept(PTDI_REQUEST Request,
+ PTDI_CONNECTION_INFORMATION AcceptInfo,
+ PTDI_CONNECTION_INFORMATION ConnectedInfo)
+{
+ return TDI_INVALID_REQUEST;
+}
+TDI_STATUS TdiReceive(PTDI_REQUEST Request, ushort *Flags,
+ uint *RcvLength, PNDIS_BUFFER Buffer)
+{
+ return TDI_INVALID_REQUEST;
+}
+
+TDI_STATUS TdiSend(PTDI_REQUEST Request, ushort Flags, uint SendLength,
+ PNDIS_BUFFER Buffer)
+{
+ return TDI_INVALID_REQUEST;
+}
+
+TDI_STATUS TdiDisconnect(PTDI_REQUEST Request, PVOID Timeout, ushort Flags,
+ PTDI_CONNECTION_INFORMATION DisconnectInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo)
+{
+ return TDI_INVALID_REQUEST;
+}
+
+#endif
+
+#ifdef VXD
+
+extern void *ConvertPtr(void *Ptr);
+
+//** Routines to handle incoming QueryInformation requests, and dispatch
+//** them.
+
+struct ClientReq {
+ ushort cr_opcode;
+ void *cr_ID;
+ void *cr_buffer;
+ void *cr_length;
+ void *cr_context;
+};
+
+//* VxDQueryInfo - VxD thunk to query information.
+//
+// The VxD thunk to TdiQueryInformationEx. All we do it convert the pointers
+// and call in.
+//
+// Input: Req - A pointer to a ClientReq structure.
+//
+// Returns: Status of command.
+//
+TDI_STATUS
+VxDQueryInfo(struct ClientReq *Req)
+{
+ NDIS_BUFFER Buffer;
+ TDI_REQUEST Request;
+ uint *Size;
+ TDIObjectID *ID;
+ void *Context;
+
+ CTEAssert(Req->cr_opcode == 0);
+
+ CTEMemSet(&Request, 0, sizeof(TDI_REQUEST));
+
+ ID = (TDIObjectID *)ConvertPtr(Req->cr_ID);
+ Size = (uint *)ConvertPtr(Req->cr_length);
+
+#ifdef DEBUG
+ Buffer.Signature = BUFFER_SIGN;
+#endif
+
+ Buffer.VirtualAddress = ConvertPtr(Req->cr_buffer);
+ Buffer.Length = *Size;
+ Buffer.Next = NULL;
+
+ return TdiQueryInformationEx(&Request, ID, &Buffer, Size,
+ ConvertPtr(Req->cr_context));
+
+}
+
+//* VxDSetInfo - VxD thunk to set information.
+//
+// The VxD thunk to TdiSetInformationEx. All we do it convert the pointers
+// and call in.
+//
+// Input: Req - A pointer to a ClientReq structure.
+//
+// Returns: Status of command.
+//
+TDI_STATUS
+VxDSetInfo(struct ClientReq *Req)
+{
+ TDIObjectID *ID;
+ uint *Size;
+ void *Buffer;
+ TDI_REQUEST Request;
+
+ CTEAssert(Req->cr_opcode == 1);
+
+ CTEMemSet(&Request, 0, sizeof(TDI_REQUEST));
+
+ ID = (TDIObjectID *)ConvertPtr(Req->cr_ID);
+ Size = (uint *)ConvertPtr(Req->cr_length);
+ Buffer = ConvertPtr(Req->cr_buffer);
+
+ return TdiSetInformationEx(&Request, ID, Buffer, *Size);
+
+}
+#endif
+
+#ifdef CHICAGO
+//* AddrChange - Receive notification of an IP address change.
+//
+// Called by IP when an address comes or goes. We get the address
+// and mask, and depending on what's actually happened we may close address
+// and connections.
+//
+// Input: Addr - IP address that's coming or going.
+// Mask - Mask for Addr.
+// Context - PNP context (unused)
+// IPContext - IP context (unused)
+// Added - True if the address is coming, False if it's going.
+//
+// Returns: Nothing.
+//
+void
+AddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext,
+ uint Added)
+{
+ if (Added) {
+ // He's adding an address. Re-query the entity list now.
+ EntityList[0].tei_entity = CO_TL_ENTITY;
+ EntityList[0].tei_instance = 0;
+ EntityList[1].tei_entity = CL_TL_ENTITY;
+ EntityList[1].tei_instance = 0;
+ EntityCount = 2;
+
+ // When we have multiple networks under us, we'll want to loop through
+ // here calling them all. For now just call the one we have.
+ (*LocalNetInfo.ipi_getelist)(EntityList, &EntityCount);
+ } else {
+ // He's deleting an address.
+ if (!IP_ADDR_EQUAL(Addr, NULL_IP_ADDR)) {
+#ifndef UDP_ONLY
+ TCBWalk(DeleteTCBWithSrc, &Addr, NULL, NULL);
+#endif
+ InvalidateAddrs(Addr);
+ }
+
+ }
+}
+#endif
+
+#ifdef NT
+#ifdef _PNP_POWER
+
+//* AddressArrival - Handle an IP address arriving
+//
+// Called by TDI when an address arrives. All we do is query the
+// EntityList.
+//
+// Input: Addr - IP address that's coming.
+//
+// Returns: Nothing.
+//
+void
+AddressArrival(PTA_ADDRESS Addr)
+{
+ if (Addr->AddressType == TDI_ADDRESS_TYPE_IP) {
+ // He's adding an address. Re-query the entity list now.
+ EntityList[0].tei_entity = CO_TL_ENTITY;
+ EntityList[0].tei_instance = 0;
+ EntityList[1].tei_entity = CL_TL_ENTITY;
+ EntityList[1].tei_instance = 0;
+ EntityCount = 2;
+
+ // When we have multiple networks under us, we'll want to loop through
+ // here calling them all. For now just call the one we have.
+ (*LocalNetInfo.ipi_getelist)(EntityList, &EntityCount);
+ }
+}
+
+//* AddressDeletion - Handle an IP address going away.
+//
+// Called by TDI when an address is deleted. If it's an address we
+// care about we'll clean up appropriately.
+//
+// Input: Addr - IP address that's going.
+//
+// Returns: Nothing.
+//
+void
+AddressDeletion(PTA_ADDRESS Addr)
+{
+ PTDI_ADDRESS_IP MyAddress;
+ IPAddr LocalAddress;
+
+ if (Addr->AddressType == TDI_ADDRESS_TYPE_IP) {
+ // He's deleting an address.
+
+ MyAddress = (PTDI_ADDRESS_IP)Addr->Address;
+ LocalAddress = MyAddress->in_addr;
+
+ if (!IP_ADDR_EQUAL(LocalAddress, NULL_IP_ADDR)) {
+#ifndef UDP_ONLY
+ TCBWalk(DeleteTCBWithSrc, &LocalAddress, NULL, NULL);
+#endif
+ InvalidateAddrs(LocalAddress);
+ }
+ }
+}
+
+#endif // _PNP_POWER
+#endif // NT
+
+#pragma BEGIN_INIT
+
+extern uchar TCPGetConfigInfo(void);
+
+extern uchar IPPresent(void);
+
+//** tlinit - Initialize the transport layer.
+//
+// The main transport layer initialize routine. We get whatever config
+// info we need, initialize some data structures, get information
+// from IP, do some more initialization, and finally register our
+// protocol values with IP.
+//
+// Input: Nothing
+//
+// Returns: True is we succeeded, False if we fail to initialize.
+//
+int
+tlinit()
+{
+#ifdef VXD
+ void *PreloadPtrs[MAX_PRELOAD_COUNT];
+ uint i;
+#endif
+
+ uint TCBInitialized = 0;
+
+ if (!CTEInitialize())
+ return FALSE;
+
+#ifdef VXD
+ if (!IPPresent())
+ return FALSE;
+#endif
+
+ if (!TCPGetConfigInfo())
+ return FALSE;
+
+ StartTime = CTESystemUpTime();
+
+#ifndef UDP_ONLY
+ KeepAliveTime = MS_TO_TICKS(KeepAliveTime);
+ KAInterval = MS_TO_TICKS(KAInterval);
+
+#endif
+
+ CTERefillMem();
+
+ // Get net information from IP.
+ if (TLGetIPInfo(&LocalNetInfo, sizeof(IPInfo)) != IP_SUCCESS)
+ goto failure;
+
+ if (LocalNetInfo.ipi_version != IP_DRIVER_VERSION)
+ goto failure; // Wrong version of IP.
+
+#ifdef CHICAGO
+ if (!RegisterAddrChangeHndlr(AddrChange, TRUE))
+ goto failure;
+#endif
+
+#ifdef NT
+#ifdef _PNP_POWER
+
+ (void)TdiRegisterAddressChangeHandler(
+ AddressArrival,
+ AddressDeletion,
+ &AddressChangeHandle
+ );
+
+#endif // _PNP_POWER
+#endif // NT
+
+ //* Initialize addr obj management code.
+ if (!InitAddr())
+ goto failure;
+
+ CTERefillMem();
+ if (!InitDG(sizeof(UDPHeader)))
+ goto failure;
+
+#ifndef UDP_ONLY
+ MaxConnections = MIN(MaxConnections, INVALID_CONN_INDEX - 1);
+ CTERefillMem();
+ if (!InitTCPConn())
+ goto failure;
+
+ CTERefillMem();
+ if (!InitTCB())
+ goto failure;
+
+ TCBInitialized = 1;
+
+ CTERefillMem();
+ if (!InitTCPRcv())
+ goto failure;
+
+ CTERefillMem();
+ if (!InitTCPSend())
+ goto failure;
+
+ CTEMemSet(&TStats, 0, sizeof(TCPStats));
+
+ TStats.ts_rtoalgorithm = TCP_RTO_VANJ;
+ TStats.ts_rtomin = MIN_RETRAN_TICKS * MS_PER_TICK;
+ TStats.ts_rtomax = MAX_REXMIT_TO * MS_PER_TICK;
+ TStats.ts_maxconn = (ulong) TCP_MAXCONN_DYNAMIC;
+
+#endif
+
+ CTEMemSet(&UStats,0, sizeof(UDPStats));
+
+
+ // Register our UDP protocol handler.
+ UDPProtInfo = TLRegisterProtocol(PROTOCOL_UDP, UDPRcv, DGSendComplete,
+ UDPStatus, NULL);
+
+ if (UDPProtInfo == NULL)
+ goto failure; // Failed to register!
+
+ // Register the Raw IP (wildcard) protocol handler.
+ RawProtInfo = TLRegisterProtocol(PROTOCOL_ANY, RawRcv, DGSendComplete,
+ RawStatus, NULL);
+
+ if (RawProtInfo == NULL) {
+ CTEPrint(("failed to register raw prot with IP\n"));
+ goto failure; // Failed to register!
+ }
+
+#ifdef VXD
+ TLDispatch.TdiOpenAddressEntry = TdiOpenAddress;
+ TLDispatch.TdiCloseAddressEntry = TdiCloseAddress;
+ TLDispatch.TdiSendDatagramEntry = TdiSendDatagram;
+ TLDispatch.TdiReceiveDatagramEntry = TdiReceiveDatagram;
+ TLDispatch.TdiSetEventEntry = TdiSetEvent;
+
+ TLDispatch.TdiOpenConnectionEntry = TdiOpenConnection;
+ TLDispatch.TdiCloseConnectionEntry = TdiCloseConnection;
+ TLDispatch.TdiAssociateAddressEntry = TdiAssociateAddress;
+ TLDispatch.TdiDisAssociateAddressEntry = TdiDisAssociateAddress;
+ TLDispatch.TdiConnectEntry = TdiConnect;
+ TLDispatch.TdiDisconnectEntry = TdiDisconnect;
+ TLDispatch.TdiListenEntry = TdiListen;
+ TLDispatch.TdiAcceptEntry = TdiAccept;
+ TLDispatch.TdiReceiveEntry = TdiReceive;
+ TLDispatch.TdiSendEntry = TdiSend;
+ TLDispatch.TdiQueryInformationEntry = TdiQueryInformation;
+ TLDispatch.TdiSetInformationEntry = TdiSetInformation;
+ TLDispatch.TdiActionEntry = TdiAction;
+ TLDispatch.TdiQueryInformationExEntry = TdiQueryInformationEx;
+ TLDispatch.TdiSetInformationExEntry = TdiSetInformationEx;
+
+ if (!TLRegisterDispatch(TransportName, &TLDispatch))
+ goto failure;
+
+#endif
+
+ CTERefillMem();
+
+ // Now query the lower layer entities, and save the information.
+ EntityList = CTEAllocMem(sizeof(TDIEntityID) * MAX_TDI_ENTITIES);
+ if (EntityList == NULL)
+ goto failure;
+
+ EntityList[0].tei_entity = CO_TL_ENTITY;
+ EntityList[0].tei_instance = 0;
+ EntityList[1].tei_entity = CL_TL_ENTITY;
+ EntityList[1].tei_instance = 0;
+ EntityCount = 2;
+
+ // When we have multiple networks under us, we'll want to loop through
+ // here calling them all. For now just call the one we have.
+ (*LocalNetInfo.ipi_getelist)(EntityList, &EntityCount);
+
+ CTERefillMem();
+
+#ifdef VXD
+ // Allocate memory as needed to satisfy the heap preload requirements. We'll
+ // allocate a bunch of memory from the IFSMgr and then free it, so
+ // hopefully it'll be there later when we need it.
+ PreloadCount = MIN(PreloadCount, MAX_PRELOAD_COUNT);
+ for (i = 0; i < PreloadCount; i++) {
+ void *Temp;
+
+ Temp = CTEAllocMem(PRELOAD_BLOCK_SIZE);
+ if (Temp != NULL)
+ PreloadPtrs[i] = Temp;
+ else
+ break;
+ CTERefillMem();
+ }
+
+ PreloadCount = i;
+ for (i = 0; i < PreloadCount; i++) {
+ CTEFreeMem(PreloadPtrs[i]);
+ }
+
+#endif
+
+ return TRUE;
+
+ // Come here to handle all failure cases.
+failure:
+
+ // If we've registered Raw IP, unregister it now.
+ if (RawProtInfo != NULL)
+ TLRegisterProtocol(PROTOCOL_ANY, NULL, NULL, NULL, NULL);
+
+ // If we've registered UDP, unregister it now.
+ if (UDPProtInfo != NULL)
+ TLRegisterProtocol(PROTOCOL_UDP, NULL, NULL, NULL, NULL);
+#ifndef UDP_ONLY
+ UnInitTCPSend();
+ UnInitTCPRcv();
+ if (TCBInitialized) {
+ UnInitTCB();
+ }
+ UnInitTCPConn();
+#endif
+
+ CTERefillMem();
+ return FALSE;
+}
+
+#pragma END_INIT
diff --git a/private/ntos/tdi/tcpip/tcp/mips/sources b/private/ntos/tdi/tcpip/tcp/mips/sources
new file mode 100644
index 000000000..e4f0fd9fa
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/mips/sources
@@ -0,0 +1,4 @@
+MIPS_SOURCES= \
+ ..\mips\xsum.s
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/mips/xsum.s b/private/ntos/tdi/tcpip/tcp/mips/xsum.s
new file mode 100644
index 000000000..1463a897b
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/mips/xsum.s
@@ -0,0 +1,243 @@
+// TITLE("Compute Checksum")
+//++
+//
+// Copyright (c) 1992-1994 Microsoft Corporation
+//
+// Module Name:
+//
+// tcpxsum.s
+//
+// Abstract:
+//
+// This module implement a function to compute the checksum of a buffer.
+//
+// Author:
+//
+// David N. Cutler (davec) 27-Jan-1992
+//
+// Environment:
+//
+// User mode.
+//
+// Revision History:
+//
+//--
+
+#include "ksmips.h"
+
+ SBTTL("Compute Checksum")
+//++
+//
+// ULONG
+// tcpxsum (
+// IN ULONG Checksum,
+// IN PUCHAR Source,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function computes the checksum of the specified buffer.
+//
+// Arguments:
+//
+// Checksum (a0) - Supplies the initial checksum value.
+//
+// Source (a1) - Supplies a pointer to the checksum buffer.
+//
+// Length (a2) - Supplies the length of the buffer in bytes.
+//
+// Return Value:
+//
+// The computed checksum is returned as the function value.
+//
+//--
+
+ LEAF_ENTRY(tcpxsum)
+
+//
+// Clear the computed checksum and check if the buffer is word aligned.
+//
+
+ move a3,zero // clear computed checksum
+ and v1,a1,1 // check if buffer word aligned
+ beq zero,a2,90f // if eq, no bytes to checksum
+ and t1,a2,1 // check if length is even
+ beq zero,v1,10f // if eq, buffer word aligned
+
+//
+// Initialize the checksum to the first byte shifted up by a byte.
+//
+
+ lbu t2,0(a1) // get first byte of buffer
+ addu a1,a1,1 // advance buffer address
+ subu a2,a2,1 // reduce count of bytes to checksum
+ dsll a3,t2,8 // shift byte up in computed checksum
+ beq zero,a2,90f // if eq, no more bytes in buffer
+ and t1,a2,1 // check if length is even
+
+//
+// Check if the length of the buffer if an even number of bytes.
+//
+// If the buffer is not an even number of bytes, then add the last byte
+// to the computed checksum.
+//
+
+10: and t3,a1,2 // check if buffer long aligned
+ beq zero,t1,20f // if eq, even number of bytes
+ addu t0,a1,a2 // compute address of ending byte + 1
+ lbu t2,-1(t0) // get last byte of buffer
+ subu a2,a2,1 // reduce count of bytes to checksum
+ daddu a3,a3,t2 // add last byte to computed checksum
+ beq zero,a2,90f // if eq, no more bytes in buffer
+
+//
+// Check if the buffer is long aligned.
+//
+// If the buffer is not long aligned, then long align the buffer.
+//
+
+20: and t0,a2,8 - 1 // compute residual bytes
+ beq zero,t3,30f // if eq, buffer long aligned
+ lhu t2,0(a1) // get next word of buffer
+ addu a1,a1,2 // advance buffer address
+ subu a2,a2,2 // reduce count of bytes to checksum
+ daddu a3,a3,t2 // add next word to computed checksum
+ beq zero,a2,90f // if eq, no more bytes in buffer
+ and t0,a2,8 - 1 // compute residual bytes
+
+//
+// Compute checksum.
+//
+
+ .set noreorder
+ .set at
+30: subu t9,a2,t0 // subtract out residual bytes
+ beq zero,t9,70f // if eq, no large blocks
+ addu t8,a1,t9 // compute ending block address
+ move a2,t0 // set residual number of bytes
+ and v0,t9,1 << 3 // check for initial 8-byte block
+ beq zero,v0,40f // if eq, no 8-byte block
+ and v0,t9,1 << 4 // check for initial 16-byte block
+ lwu t0,0(a1) // load 8-byte block
+ lwu t1,4(a1) //
+ addu a1,a1,8 // advance source address
+ daddu a3,a3,t0 // compute 8-byte checksum
+ beq t8,a1,70f // if eq, end of block
+ daddu a3,a3,t1 //
+40: beq zero,v0,50f // if eq, no 16-byte block
+ and v0,t9,1 << 5 // check for initial 32-byte block
+ lwu t0,0(a1) // load 16-byte data block
+ lwu t1,4(a1) //
+ lwu t2,8(a1) //
+ lwu t3,12(a1) //
+ addu a1,a1,16 // advance source address
+ daddu a3,a3,t0 // compute 16-byte block checksum
+ daddu a3,a3,t1 //
+ daddu a3,a3,t2 //
+ beq t8,a1,70f // if eq, end of block
+ daddu a3,a3,t3 //
+50: beq zero,v0,60f // if eq, no 32-byte block
+ lwu t0,0(a1) // load 32-byte data block
+ lwu t1,4(a1) //
+ lwu t2,8(a1) //
+ lwu t3,12(a1) //
+ lwu t4,16(a1) //
+ lwu t5,20(a1) //
+ lwu t6,24(a1) //
+ lwu t7,28(a1) //
+ addu a1,a1,32 // advance source address
+ daddu a3,a3,t0 // compute 32-byte block checksum
+ daddu a3,a3,t1 //
+ daddu a3,a3,t2 //
+ daddu a3,a3,t3 //
+ daddu a3,a3,t4 //
+ daddu a3,a3,t5 //
+ daddu a3,a3,t6 //
+ beq t8,a1,70f // if eq, end of block
+ daddu a3,a3,t7 //
+55: lwu t0,0(a1) // load 32-byte data block
+60: lwu t1,4(a1) //
+ lwu t2,8(a1) //
+ lwu t3,12(a1) //
+ lwu t4,16(a1) //
+ lwu t5,20(a1) //
+ lwu t6,24(a1) //
+ lwu t7,28(a1) //
+ daddu a3,a3,t0 // compute 32-byte block checksum
+ daddu a3,a3,t1 //
+ daddu a3,a3,t2 //
+ daddu a3,a3,t3 //
+ daddu a3,a3,t4 //
+ daddu a3,a3,t5 //
+ daddu a3,a3,t6 //
+ daddu a3,a3,t7 //
+ lwu t0,32(a1) // load 32-byte data block
+ lwu t1,36(a1) //
+ lwu t2,40(a1) //
+ lwu t3,44(a1) //
+ lwu t4,48(a1) //
+ lwu t5,52(a1) //
+ lwu t6,56(a1) //
+ lwu t7,60(a1) //
+ addu a1,a1,64 // advance source address
+ daddu a3,a3,t0 // compute 32-byte block checksum
+ daddu a3,a3,t1 //
+ daddu a3,a3,t2 //
+ daddu a3,a3,t3 //
+ daddu a3,a3,t4 //
+ daddu a3,a3,t5 //
+ daddu a3,a3,t6 //
+ bne t8,a1,55b // if ne, not end of block
+ daddu a3,a3,t7 //
+ .set at
+ .set reorder
+
+//
+// Compute the checksum of in 2-byte blocks.
+//
+
+70: addu t8,a1,a2 // compute ending block address
+ beq zero,a2,90f // if eq, no bytes to checksum
+
+ .set noreorder
+ .set noat
+80: lhu t0,0(a1) // compute checksum of 2-byte block
+ addu a1,a1,2 // advance source address
+ bne t8,a1,80b // if ne, more 2-byte blocks
+ daddu a3,a3,t0 //
+ .set at
+ .set reorder
+
+//
+// Combine input checksum and paritial checksum.
+//
+// If the input buffer was byte aligned, then word swap bytes in computed
+// checksum before combination with the input checksum.
+//
+
+90: beq zero,v1,100f // if eq, buffer word aligned
+ li t6,0xff00ff // get byte swap mask
+ dsll t7,t6,32 //
+ or t6,t6,t7 //
+ and t3,a3,t6 // isolate bytes 0, 2, 4, and 6
+ dsll t3,t3,8 // shift bytes 0, 2, 4, and 6 into position
+ dsrl t4,a3,8 // shift bytes 1, 3, 5, and 7 into position
+ and t4,t4,t6 // isolate bytes 1, 3, 5, and 7
+ or a3,t4,t3 // merge checksum bytes
+100: dsll a0,a0,32 // make sure upper 32 bits are clear
+ dsrl a0,a0,32 //
+ daddu v0,a0,a3 // combine checksums
+ dsrl t0,v0,32 // swap checksum longs
+ dsll t1,v0,32 //
+ or t0,t0,t1 //
+ daddu v0,v0,t0 // compute 32-bit checksum with carry
+ dsrl v0,v0,32 //
+ srl t0,v0,16 // swap checksum words
+ sll t1,v0,16 //
+ or t0,t0,t1 //
+ addu v0,v0,t0 // add words with carry into high word
+ srl v0,v0,16 // extract final checksum
+ j ra // return
+
+ .end tcpxsum
diff --git a/private/ntos/tdi/tcpip/tcp/mp/makefile b/private/ntos/tdi/tcpip/tcp/mp/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/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/tcpip/tcp/mp/sources b/private/ntos/tdi/tcpip/tcp/mp/sources
new file mode 100644
index 000000000..dfff2b249
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/mp/sources
@@ -0,0 +1,30 @@
+!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
+
+LINKLIBS=..\..\ip\mp\obj\*\ip.lib
+TARGETPATH=\nt\public\sdk\lib
+
+!include ..\sources.inc
diff --git a/private/ntos/tdi/tcpip/tcp/mp/tcpip.prf b/private/ntos/tdi/tcpip/tcp/mp/tcpip.prf
new file mode 100644
index 000000000..4f03cef97
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/mp/tcpip.prf
@@ -0,0 +1,297 @@
+TdiQueryInformation@20
+TCPDispatchInternalDeviceControl@8
+DerefTCB@8
+FreeARPBuffer@8
+ProcessTCBDelayQ@0
+ARPTransmit@16
+TCPRcv@48
+IPRcv@28
+IPRcvComplete@0
+GetARPBuffer@12
+FreeIPPacket@4
+TCBTimeout@8
+ARPTimeout@8
+ICMPTimer@4
+IPTimeout@8
+IGMPTimer@4
+IndicateData@16
+TCPSendComplete@8
+GetSendReq@0
+FreeSendReq@4
+FillTCPHeader@8
+RcvWin@4
+IPTransmit@36
+GetRcvReq@0
+GetTCPHeader@0
+FindTCB@16
+TCPSendData@8
+TdiSend@16
+TCPSend@8
+ACKData@8
+FreeRBChain@4
+FreeRcvReq@4
+TCPDataRequestComplete@12
+FindUserRcv@4
+TCPRcvComplete@0
+GetLocalNTE@8
+GetConnFromConnID@4
+ARPSendData@16
+XsumRcvBuf@8
+FreeTCPHeader@4
+ARPRcv@28
+XsumSendChain@8
+CTEStartTimer@16
+DeliverToUser@32
+GetIPPacket@0
+ARPRcvComplete@4
+CTESystemUpTime@0
+ARPSendComplete@12
+IPSendComplete@12
+DelayAction@8
+TCPPrepareIrpForCancel@12
+BufferData@16
+SendACK@4
+CompleteRcvs@4
+FreePartialRB@8
+GetAddrObj@16
+AddrOnIF@8
+FindRTE@20
+TdiCopyBufferToMdl@24
+TCPGetMdlChainByteCount@4
+IPHash@4
+LookupRTE@12
+OpenRCE@24
+TCPQueryInformation@8
+CopyFlatToNdis@20
+FindListenConn@16
+DummyDone@8
+GetAddrType@4
+InitTCBFromConn@16
+tcpxsum@12
+FindMSS@4
+DelayDerefAO@4
+FindRCE@12
+TryToCloseTCB@12
+InitRCE@4
+NotifyOfDisc@12
+UpdateConnInfo@16
+GoToEstab@4
+TCPDisconnect@8
+FreeConnReq@4
+TdiDisconnect@20
+IPGetLocalMTU@8
+CloseRCE@4
+IPInitOptions@4
+AdjustRcvWin@4
+BuildTDIAddress@12
+SendSYN@8
+AllocTCB@0
+ProcessUserOptions@8
+FreeTCB@4
+RemoveTCBFromConn@4
+InitSendState@4
+CompleteConnReq@12
+IPFreeOptions@4
+TCPRequestComplete@12
+InvalidSourceAddress@4
+AcceptConn@8
+RemoveTCB@4
+GetConnReq@0
+CloseTCB@8
+RemoveConnFromTCB@4
+FinishRemoveTCBFromConn@4
+IsBCastOnNTE@8
+InsertTCB@4
+ResetSendNext@8
+ARPLookup@12
+BCastRcv@40
+IPGetAddrType@4
+UDPRcv@48
+GetFirstAddrObj@16
+IPForward@32
+GetNextAddrObj@4
+UDPDeliver@32
+LockedDerefIF@4
+BestNTEForIF@8
+ARPInvalidate@8
+IsBCastOnIF@8
+ARPSendBCast@16
+UDPSendDatagram@8
+DGSendComplete@8
+TdiSendDatagram@20
+DerefAO@4
+FreeDGSendReq@4
+GetDGSendReq@0
+FreeDGHeader@4
+UDPSend@8
+GetAddress@12
+ARPRemoveRCE@8
+HandleARPPacket@24
+IsLocalAddr@8
+ARPLocalAddr@8
+IPRouteTimeout@8
+GetConnID@4
+TdiAssociateAddress@8
+FindEA@12
+TCPCreate@12
+TCPAssociateAddress@8
+TdiOpenConnection@8
+TCPDispatch@8
+TdiMapUserRequest@12
+IPGetPInfo@16
+SendARPPacket@40
+RemoveARPTableEntry@8
+CreateARPTableEntry@12
+SendARPRequest@20
+GrowARPHeaders@4
+GrowTCPHeaderList@0
+TdiCloseConnection@4
+RemoveConnFromAO@8
+TCPCloseObjectComplete@12
+TCPClose@8
+CloseDone@8
+TCPCleanup@12
+FreeConnID@4
+DummyCmplt@12
+LoopAddAddr@16
+FindIGMPAddr@12
+CTEInitEvent@8
+TdiSetEvent@16
+ARPSetMCastList@4
+IPGetInfo@8
+IGMPAddrChange@12
+ICMPInit@4
+IGMPInit@0
+ARPRequestComplete@12
+FreeAORequest@4
+TCPQueryInformationEx@8
+ARPFindMCast@12
+InitAdapter@4
+CTESignal@8
+CTEBlock@4
+CTEInitTimer@4
+InsertAddrObj@4
+TdiQueryInformationEx@20
+ARPAddAddr@16
+TdiOpenAddress@16
+InitNTERouting@12
+ARPAddMCast@8
+InitIGMPForNTE@4
+FindAnyAddrObj@8
+GrowDGHeaderList@0
+FindSpecificRTE@20
+InitNTE@4
+CopyToNdis@16
+IPRegisterProtocol@20
+GetAddrOptions@12
+InitInterface@8
+TdiSetInformationEx@16
+TCPConnect@8
+TCPQueryInformationExComplete@12
+TCPSetInformationEx@8
+LockedAddRoute@36
+TCPAcdBind@0
+AddRoute@36
+TdiConnect@16
+LoopXmit@16
+AddValueSecurityFilter@12
+InitGateway@4
+ARPOAComplete@12
+CTEScheduleEvent@8
+GetHashMask@8
+InitLoopback@4
+RawStatus@28
+FindInterfaceEntry@4
+FindProtocolEntry@8
+AddProtocolSecurityFilter@12
+TdiRegisterNetAddress@8
+UDPStatus@28
+ARPDynRegister@36
+FindInsertPoint@4
+IPAddInterface@20
+NotifyAddrChange@28
+TCPStatus@28
+TdiInitialize@0
+TdiRegisterDeviceObject@8
+TdiRegisterAddressChangeHandler@12
+CTEInitialize@0
+ARPBindAdapter@20
+ICMPStatus@28
+IPQueryInfo@16
+DeleteProtocolValueEntries@4
+ModifyProtocolEntry@12
+ModifyInterfaceEntry@16
+ModifySecurityFilter@16
+DoNDISRequest@24
+LoopGetEList@12
+TCPDisassociateAddress@8
+TdiDisAssociateAddress@4
+EnumRegMultiSz@12
+AddressArrival@4
+ARPGetEList@12
+GrowIPPacketList@0
+IPGetEList@8
+RTValidateContext@8
+RTReadNext@8
+GetCurrentRouteTable@4
+AddNTERoutes@4
+GetSecurityFilterList@12
+LoopXmitRtn@8
+InitDG@4
+IPGetConfig@0
+InitTimestamp@0
+IPProcessAdapterSection@8
+TCPInitializeParameter@12
+TLGetIPInfo@8
+InitializeSecurityFilters@0
+IPFreeConfig@4
+InitTCPRcv@0
+InitAddr@0
+IPInit@0
+GetTime@0
+ARPInit@0
+InitTCPConn@0
+IPProcessConfiguration@0
+IPDriverEntry@8
+tlinit@0
+InitTCPSend@0
+InitTCB@0
+GetIFAddrList@8
+ARPOpen@4
+TCPDispatchDeviceControl@8
+TCPSetEventHandler@8
+ARPRegister@12
+GetRegStringValue@16
+IsDHCPZeroAddress@4
+EnumSecurityFilterValue@12
+CloseIFConfig@4
+UseEtherSNAP@4
+OpenIFConfig@8
+IPConvertStringToAddress@8
+SetRegDWORDValue@12
+GetArpCacheLife@0
+InitRegDWORDParameter@16
+GetRegMultiSZValue@12
+OpenRegKey@8
+GetGeneralIFConfig@8
+GetAlwaysSourceRoute@0
+GetRegSZValue@16
+GetRegDWORDValue@12
+IsLLInterfaceValueNull@4
+DerefIF@4
+ARPQueryInfo@20
+DriverEntry@8
+TCPGetConfigInfo@0
+SendIPPacket@28
+GetIPHdrBuffer@0
+GrowHdrBufList@0
+FreeIPHdrBuffer@4
+InitRouting@4
+TLRegisterProtocol@20
+LookupNextHopWithBuffer@28
+SetPersistentRoutesForNTE@12
+SendARPReply@28
+GetGMTDelta@0
+SendRSTFromHeader@20
+InvalidateRCEChain@4
+LoopQInfo@20
diff --git a/private/ntos/tdi/tcpip/tcp/ntautodl.c b/private/ntos/tdi/tcpip/tcp/ntautodl.c
new file mode 100644
index 000000000..0c244aff7
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/ntautodl.c
@@ -0,0 +1,258 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ ntautodl.c
+
+Abstract:
+
+ NT specific routines for interfacing with the
+ RAS AutoDial driver (acd.sys).
+
+Author:
+
+ Anthony Discolo (adiscolo) Aug 30, 1995
+
+Revision History:
+
+ Who When What
+ -------- -------- ----------------------------------------------
+ adiscolo 08-30-95 created
+
+Notes:
+
+--*/
+
+#include <oscfg.h>
+#include <ntrtl.h>
+#include <ntddip.h>
+#include <ndis.h>
+#include <cxport.h>
+#include <tdikrnl.h>
+#include <tdint.h>
+#include <tdistat.h>
+#include <tdiinfo.h>
+#include <ip.h>
+#include <acd.h>
+#include <acdapi.h>
+
+#include "queue.h"
+#include "addr.h"
+#include "tcp.h"
+#include "tcb.h"
+#include "tcpconn.h"
+#include "udp.h"
+#include "tlcommon.h"
+#include <ntddtcp.h>
+
+//
+// Macro for calculating
+// an IP address component.
+//
+#define UC(pIpAddr, i) ((ULONG)(((PCHAR)(pIpAddr))[i]) & 0xff)
+
+//
+// Global variables
+//
+BOOLEAN fAcdLoadedG;
+ACD_DRIVER AcdDriverG;
+ULONG ulDriverIdG = 'Tcp ';
+
+
+
+VOID
+TCPNoteNewConnection(
+ IN TCB *pTCB,
+ IN CTELockHandle Handle
+ )
+{
+ ACD_ADDR addr;
+ ACD_ADAPTER adapter;
+
+ //
+ // If there is a NULL source
+ // or destination IP address, then return.
+ //
+ if (!pTCB->tcb_saddr || !pTCB->tcb_daddr) {
+ CTEFreeLock(&pTCB->tcb_lock, Handle);
+ return;
+ }
+ //
+ // We also know we aren't interested in
+ // any connections on the 127 network.
+ //
+ if (UC(&pTCB->tcb_daddr, 0) == 127) {
+ CTEFreeLock(&pTCB->tcb_lock, Handle);
+ return;
+ }
+ //
+ // Get the address of the connection.
+ //
+ addr.fType = ACD_ADDR_IP;
+ addr.ulIpaddr = pTCB->tcb_daddr;
+ adapter.fType = ACD_ADAPTER_IP;
+ adapter.ulIpaddr = pTCB->tcb_saddr;
+ //
+ // Release the TCB lock handle before
+ // calling out of this driver.
+ //
+ CTEFreeLock(&pTCB->tcb_lock, Handle);
+ //
+ // Inform the automatic connection driver
+ // of the new connection.
+ //
+ (*AcdDriverG.lpfnNewConnection)(&addr, &adapter);
+} // TCPNoteNewConnection
+
+
+
+VOID
+TCPAcdBind()
+{
+ 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);
+} // TCPAcdBind
+
+
+
+VOID
+TCPAcdUnbind()
+{
+ 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);
+} // TCPAcdUnbind
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/ntdisp.c b/private/ntos/tdi/tcpip/tcp/ntdisp.c
new file mode 100644
index 000000000..15c6f10cc
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/ntdisp.c
@@ -0,0 +1,4063 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ntdisp.c
+
+Abstract:
+
+ NT specific routines for dispatching and handling IRPs.
+
+Author:
+
+ Mike Massa (mikemas) Aug 13, 1993
+
+Revision History:
+
+ Who When What
+ -------- -------- ----------------------------------------------
+ mikemas 08-13-93 created
+
+Notes:
+
+--*/
+
+#include <oscfg.h>
+#include <ntrtl.h>
+#include <ntddip.h>
+#include <ndis.h>
+#include <cxport.h>
+#include <tdikrnl.h>
+#include <tdint.h>
+#include <tdistat.h>
+#include <tdiinfo.h>
+#include <ip.h>
+#include "queue.h"
+#include "addr.h"
+#include "tcp.h"
+#include "udp.h"
+#include "raw.h"
+#include <tcpinfo.h>
+#include <ntddtcp.h>
+#include "tcpcfg.h"
+#include "secfltr.h"
+#include "tcpconn.h"
+
+//
+// Macros
+//
+//++
+//
+// LARGE_INTEGER
+// CTEConvert100nsToMilliseconds(
+// IN LARGE_INTEGER HnsTime
+// );
+//
+// Routine Description:
+//
+// Converts time expressed in hundreds of nanoseconds to milliseconds.
+//
+// Arguments:
+//
+// HnsTime - Time in hundreds of nanoseconds.
+//
+// Return Value:
+//
+// Time in milliseconds.
+//
+//--
+
+#define SHIFT10000 13
+static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758};
+
+#define CTEConvert100nsToMilliseconds(HnsTime) \
+ RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000)
+
+
+//
+// Global variables
+//
+extern PDEVICE_OBJECT TCPDeviceObject, UDPDeviceObject;
+extern PDEVICE_OBJECT IPDeviceObject;
+extern PDEVICE_OBJECT RawIPDeviceObject;
+
+
+//
+// Local types
+//
+typedef struct {
+ PIRP Irp;
+ PMDL InputMdl;
+ PMDL OutputMdl;
+ TCP_REQUEST_QUERY_INFORMATION_EX QueryInformation;
+} TCP_QUERY_CONTEXT, *PTCP_QUERY_CONTEXT;
+
+
+
+//
+// General external function prototypes
+//
+extern
+NTSTATUS
+IPDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+//
+// External TDI function prototypes
+//
+extern TDI_STATUS
+TdiOpenAddress(
+ PTDI_REQUEST Request,
+ TRANSPORT_ADDRESS UNALIGNED *AddrList,
+ uint protocol,
+ void *Reuse
+ );
+
+extern TDI_STATUS
+TdiCloseAddress(
+ PTDI_REQUEST Request
+ );
+
+extern TDI_STATUS
+TdiOpenConnection(
+ PTDI_REQUEST Request,
+ PVOID Context
+ );
+
+extern TDI_STATUS
+TdiCloseConnection(
+ PTDI_REQUEST Request
+ );
+
+extern TDI_STATUS
+TdiAssociateAddress(
+ PTDI_REQUEST Request,
+ HANDLE AddrHandle
+ );
+
+extern TDI_STATUS
+TdiCancelDisAssociateAddress(
+ PTDI_REQUEST Request
+ );
+
+extern TDI_STATUS
+TdiDisAssociateAddress(
+ PTDI_REQUEST Request
+ );
+
+extern TDI_STATUS
+TdiConnect(
+ PTDI_REQUEST Request,
+ void *Timeout,
+ PTDI_CONNECTION_INFORMATION RequestAddr,
+ PTDI_CONNECTION_INFORMATION ReturnAddr
+ );
+
+extern TDI_STATUS
+TdiListen(
+ PTDI_REQUEST Request,
+ ushort Flags,
+ PTDI_CONNECTION_INFORMATION AcceptableAddr,
+ PTDI_CONNECTION_INFORMATION ConnectedAddr
+ );
+
+extern TDI_STATUS
+TdiAccept(
+ PTDI_REQUEST Request,
+ PTDI_CONNECTION_INFORMATION AcceptInfo,
+ PTDI_CONNECTION_INFORMATION ConnectedInfo
+ );
+
+extern TDI_STATUS
+TdiDisconnect(
+ PTDI_REQUEST Request,
+ void *TO,
+ ushort Flags,
+ PTDI_CONNECTION_INFORMATION DiscConnInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo
+ );
+
+extern TDI_STATUS
+TdiSend(
+ PTDI_REQUEST Request,
+ ushort Flags,
+ uint SendLength,
+ PNDIS_BUFFER SendBuffer
+ );
+
+extern TDI_STATUS
+TdiReceive(
+ PTDI_REQUEST Request,
+ ushort *Flags,
+ uint *RcvLength,
+ PNDIS_BUFFER Buffer
+ );
+
+extern TDI_STATUS
+TdiSendDatagram(
+ PTDI_REQUEST Request,
+ PTDI_CONNECTION_INFORMATION ConnInfo,
+ uint DataSize,
+ uint *BytesSent,
+ PNDIS_BUFFER Buffer
+ );
+
+VOID
+TdiCancelSendDatagram(
+ AddrObj *SrcAO,
+ PVOID Context
+ );
+
+extern TDI_STATUS
+TdiReceiveDatagram(
+ PTDI_REQUEST Request,
+ PTDI_CONNECTION_INFORMATION ConnInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo,
+ uint RcvSize,
+ uint *BytesRcvd,
+ PNDIS_BUFFER Buffer
+ );
+
+VOID
+TdiCancelReceiveDatagram(
+ AddrObj *SrcAO,
+ PVOID Context
+ );
+
+extern TDI_STATUS
+TdiSetEvent(
+ PVOID Handle,
+ int Type,
+ PVOID Handler,
+ PVOID Context
+ );
+
+extern TDI_STATUS
+TdiQueryInformation(
+ PTDI_REQUEST Request,
+ uint QueryType,
+ PNDIS_BUFFER Buffer,
+ uint *BytesReturned,
+ uint IsConn
+ );
+
+extern TDI_STATUS
+TdiSetInformation(
+ PTDI_REQUEST Request,
+ uint SetType,
+ PNDIS_BUFFER Buffer,
+ uint BufferSize,
+ uint IsConn
+ );
+
+extern TDI_STATUS
+TdiQueryInformationEx(
+ PTDI_REQUEST Request,
+ struct TDIObjectID *ID,
+ PNDIS_BUFFER Buffer,
+ uint *Size,
+ void *Context
+ );
+
+extern TDI_STATUS
+TdiSetInformationEx(
+ PTDI_REQUEST Request,
+ struct TDIObjectID *ID,
+ void *Buffer,
+ uint Size
+ );
+
+extern TDI_STATUS
+TdiAction(
+ PTDI_REQUEST Request,
+ uint ActionType,
+ PNDIS_BUFFER Buffer,
+ uint BufferSize
+ );
+
+//
+// Other external functions
+//
+void
+TCPAbortAndIndicateDisconnect(
+ uint ConnnectionContext
+ );
+
+
+//
+// Local pageable function prototypes
+//
+NTSTATUS
+TCPDispatchDeviceControl(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+TCPCreate(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+TCPAssociateAddress(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+TCPSetEventHandler(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+TCPQueryInformation(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+FILE_FULL_EA_INFORMATION UNALIGNED *
+FindEA(
+ PFILE_FULL_EA_INFORMATION StartEA,
+ CHAR *TargetName,
+ USHORT TargetNameLength
+ );
+
+BOOLEAN
+IsDHCPZeroAddress(
+ TRANSPORT_ADDRESS UNALIGNED *AddrList
+ );
+
+ULONG
+RawExtractProtocolNumber(
+ IN PUNICODE_STRING FileName
+ );
+
+#ifdef SECFLTR
+
+NTSTATUS
+TCPControlSecurityFilter(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+TCPProcessSecurityFilterRequest(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+NTSTATUS
+TCPEnumerateSecurityFilter(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+#endif // SECFLTR
+
+NTSTATUS
+TCPEnumerateConnectionList(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ );
+
+//
+// Local helper routine prototypes.
+//
+ULONG
+TCPGetMdlChainByteCount(
+ PMDL Mdl
+ );
+
+
+//
+// All of this code is pageable.
+//
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, TCPDispatchDeviceControl)
+#pragma alloc_text(PAGE, TCPCreate)
+#pragma alloc_text(PAGE, TCPAssociateAddress)
+#pragma alloc_text(PAGE, TCPSetEventHandler)
+#pragma alloc_text(PAGE, FindEA)
+#pragma alloc_text(PAGE, IsDHCPZeroAddress)
+#pragma alloc_text(PAGE, RawExtractProtocolNumber)
+
+#ifdef SECFLTR
+
+#pragma alloc_text(PAGE, TCPControlSecurityFilter)
+#pragma alloc_text(PAGE, TCPProcessSecurityFilterRequest)
+#pragma alloc_text(PAGE, TCPEnumerateSecurityFilter)
+
+#endif // SECFLTR
+
+#pragma alloc_text(PAGE, TCPEnumerateSecurityFilter)
+
+#endif // ALLOC_PRAGMA
+
+
+
+//
+// Generic Irp completion and cancellation routines.
+//
+
+void
+TCPDataRequestComplete(
+ void *Context,
+ unsigned int Status,
+ unsigned int ByteCount
+ )
+
+/*++
+
+Routine Description:
+
+ Completes a UDP/TCP send/receive request.
+
+Arguments:
+
+ Context - A pointer to the IRP for this request.
+ Status - The final TDI status of the request.
+ ByteCount - Bytes sent/received information.
+
+Return Value:
+
+ None.
+
+Notes:
+
+--*/
+
+{
+ KIRQL oldIrql;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PTCP_CONTEXT tcpContext;
+ PIRP item = NULL;
+
+
+ irp = (PIRP) Context;
+ irpSp = IoGetCurrentIrpStackLocation(irp);
+ tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext;
+
+ IoAcquireCancelSpinLock(&oldIrql);
+
+#if DBG
+
+ IF_TCPDBG(TCP_DEBUG_CANCEL) {
+
+ PLIST_ENTRY entry, listHead;
+ PIRP item = NULL;
+
+ if (irp->Cancel) {
+ CTEAssert(irp->CancelRoutine == NULL);
+ listHead = &(tcpContext->CancelledIrpList);
+ }
+ else {
+ listHead = &(tcpContext->PendingIrpList);
+ }
+
+ //
+ // Verify that the Irp is on the appropriate list
+ //
+ for ( entry = listHead->Flink;
+ entry != listHead;
+ entry = entry->Flink
+ ) {
+
+ item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
+
+ if (item == irp) {
+ RemoveEntryList(&(irp->Tail.Overlay.ListEntry));
+ break;
+ }
+ }
+
+ CTEAssert(item == irp);
+ }
+
+#endif
+
+ IoSetCancelRoutine(irp, NULL);
+
+ CTEAssert(tcpContext->ReferenceCount > 0);
+
+ if (--(tcpContext->ReferenceCount) == 0) {
+
+ IF_TCPDBG(TCP_DEBUG_CANCEL) {
+ CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList)));
+ CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList)));
+ }
+
+ //
+ // Set the cleanup event.
+ //
+ KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE);
+ }
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE((
+ "TCPDataRequestComplete: Irp %lx fileobj %lx refcnt dec to %u\n",
+ irp,
+ irpSp->FileObject,
+ tcpContext->ReferenceCount
+ ));
+ }
+
+ if (irp->Cancel || tcpContext->CancelIrps) {
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE(("TCPDataRequestComplete: Irp %lx was cancelled\n", irp));
+ }
+
+ Status = (unsigned int) STATUS_CANCELLED;
+ ByteCount = 0;
+ }
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE((
+ "TCPDataRequestComplete: completing irp %lx, status %lx, byte count %lx\n",
+ irp,
+ Status,
+ ByteCount
+ ));
+ }
+
+ irp->IoStatus.Status = (NTSTATUS) Status;
+ irp->IoStatus.Information = ByteCount;
+
+ IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
+
+ return;
+
+} // TCPDataRequestComplete
+
+
+void
+TCPRequestComplete(
+ void *Context,
+ unsigned int Status,
+ unsigned int UnUsed
+ )
+
+/*++
+
+Routine Description:
+
+ Completes a cancellable TDI request which returns no data by
+ calling TCPDataRequestComplete with a ByteCount of zero.
+
+Arguments:
+
+ Context - A pointer to the IRP for this request.
+ Status - The final TDI status of the request.
+ UnUsed - An unused parameter
+
+Return Value:
+
+ None.
+
+Notes:
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(UnUsed);
+
+ TCPDataRequestComplete(Context, Status, 0);
+
+} // TCPRequestComplete
+
+
+
+void
+TCPNonCancellableRequestComplete(
+ void *Context,
+ unsigned int Status,
+ unsigned int UnUsed
+ )
+
+/*++
+
+Routine Description:
+
+ Completes a TDI request which cannot be cancelled.
+
+Arguments:
+
+ Context - A pointer to the IRP for this request.
+ Status - The final TDI status of the request.
+ UnUsed - An unused parameter
+
+Return Value:
+
+ None.
+
+Notes:
+
+--*/
+
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+
+
+ UNREFERENCED_PARAMETER(UnUsed);
+
+ irp = (PIRP) Context;
+ irpSp = IoGetCurrentIrpStackLocation(irp);
+
+ IF_TCPDBG(TCP_DEBUG_CLOSE) {
+ TCPTRACE((
+ "TCPNonCancellableRequestComplete: irp %lx status %lx\n",
+ irp,
+ Status
+ ));
+ }
+
+ //
+ // Complete the IRP
+ //
+ irp->IoStatus.Status = (NTSTATUS) Status;
+ irp->IoStatus.Information = 0;
+ IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
+
+ return;
+
+} // TCPNonCancellableRequestComplete
+
+
+
+void
+TCPCancelComplete(
+ void *Context,
+ unsigned int Unused1,
+ unsigned int Unused2
+ )
+{
+ PFILE_OBJECT fileObject = (PFILE_OBJECT) Context;
+ PTCP_CONTEXT tcpContext = (PTCP_CONTEXT) fileObject->FsContext;
+ KIRQL oldIrql;
+
+
+ UNREFERENCED_PARAMETER(Unused1);
+ UNREFERENCED_PARAMETER(Unused2);
+
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ //
+ // Remove the reference placed on the endpoint by the cancel routine.
+ // The cancelled Irp will be completed by the completion routine for the
+ // request.
+ //
+ if (--(tcpContext->ReferenceCount) == 0) {
+
+ IF_TCPDBG(TCP_DEBUG_CANCEL) {
+ CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList)));
+ CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList)));
+ }
+
+ //
+ // Set the cleanup event.
+ //
+ KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE);
+ }
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE((
+ "TCPCancelComplete: fileobj %lx refcnt dec to %u\n",
+ fileObject,
+ tcpContext->ReferenceCount
+ ));
+ }
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ return;
+
+} // TCPCancelComplete
+
+
+VOID
+TCPCancelRequest(
+ PDEVICE_OBJECT Device,
+ PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels an outstanding Irp.
+
+Arguments:
+
+ Device - Pointer to the device object for this request.
+ Irp - Pointer to I/O request packet
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpSp;
+ PTCP_CONTEXT tcpContext;
+ NTSTATUS status = STATUS_SUCCESS;
+ PFILE_OBJECT fileObject;
+ UCHAR minorFunction;
+ TDI_REQUEST request;
+
+
+ irpSp = IoGetCurrentIrpStackLocation(Irp);
+ fileObject = irpSp->FileObject;
+ tcpContext = (PTCP_CONTEXT) fileObject->FsContext;
+ minorFunction = irpSp->MinorFunction;
+
+ CTEAssert(Irp->Cancel);
+ IoSetCancelRoutine(Irp, NULL);
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE((
+ "TCPCancelRequest: cancelling irp %lx, file object %lx\n",
+ Irp,
+ fileObject
+ ));
+ }
+
+#if DBG
+
+ IF_TCPDBG(TCP_DEBUG_CANCEL) {
+ //
+ // Verify that the Irp is on the pending list
+ //
+ PLIST_ENTRY entry;
+ PIRP item = NULL;
+
+
+ for ( entry = tcpContext->PendingIrpList.Flink;
+ entry != &(tcpContext->PendingIrpList);
+ entry = entry->Flink
+ ) {
+
+ item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
+
+ if (item == Irp) {
+ RemoveEntryList( &(Irp->Tail.Overlay.ListEntry));
+ break;
+ }
+ }
+
+ CTEAssert(item == Irp);
+
+ InsertTailList(
+ &(tcpContext->CancelledIrpList),
+ &(Irp->Tail.Overlay.ListEntry)
+ );
+ }
+
+#endif // DBG
+
+ //
+ // Add a reference so the object can't be closed while the cancel routine
+ // is executing.
+ //
+ CTEAssert(tcpContext->ReferenceCount > 0);
+ tcpContext->ReferenceCount++;
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE((
+ "TCPCancelRequest: Irp %lx fileobj %lx refcnt inc to %u\n",
+ Irp,
+ fileObject,
+ tcpContext->ReferenceCount
+ ));
+ }
+
+ IoReleaseCancelSpinLock(Irp->CancelIrql);
+
+ //
+ // Try to cancel the request.
+ //
+ switch(minorFunction) {
+
+ case TDI_SEND:
+ case TDI_RECEIVE:
+
+ CTEAssert(((int)fileObject->FsContext2) == TDI_CONNECTION_FILE);
+
+ TCPAbortAndIndicateDisconnect(
+ (uint) tcpContext->Handle.ConnectionContext
+ );
+ break;
+
+ case TDI_SEND_DATAGRAM:
+
+ CTEAssert(((int)fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE);
+
+ TdiCancelSendDatagram(tcpContext->Handle.AddressHandle, Irp);
+ break;
+
+ case TDI_RECEIVE_DATAGRAM:
+
+ CTEAssert(((int)fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE);
+
+ TdiCancelReceiveDatagram(tcpContext->Handle.AddressHandle, Irp);
+ break;
+
+ case TDI_DISASSOCIATE_ADDRESS:
+
+ CTEAssert(((int)fileObject->FsContext2) == TDI_CONNECTION_FILE);
+ //
+ // This pends but is not cancellable. We put it thru the cancel code
+ // anyway so a reference is made for it and so it can be tracked in
+ // a debug build.
+ //
+ break;
+
+ default:
+
+ //
+ // Initiate a disconnect to cancel the request.
+ //
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ request.RequestNotifyObject = TCPCancelComplete;
+ request.RequestContext = fileObject;
+
+ status = TdiDisconnect(
+ &request,
+ NULL,
+ TDI_DISCONNECT_ABORT,
+ NULL,
+ NULL
+ );
+ break;
+ }
+
+ if (status != TDI_PENDING) {
+ TCPCancelComplete(fileObject, 0, 0);
+ }
+
+ return;
+
+} // TCPCancelRequest
+
+
+
+NTSTATUS
+TCPPrepareIrpForCancel(
+ PTCP_CONTEXT TcpContext,
+ PIRP Irp,
+ PDRIVER_CANCEL CancelRoutine
+ )
+{
+ KIRQL oldIrql;
+
+ //
+ // Set up for cancellation
+ //
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ CTEAssert(Irp->CancelRoutine == NULL);
+
+ if (!Irp->Cancel) {
+
+ IoMarkIrpPending(Irp);
+ IoSetCancelRoutine(Irp, CancelRoutine);
+ TcpContext->ReferenceCount++;
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE((
+ "TCPPrepareIrpForCancel: irp %lx fileobj %lx refcnt inc to %u\n",
+ Irp,
+ (IoGetCurrentIrpStackLocation(Irp))->FileObject,
+ TcpContext->ReferenceCount
+ ));
+ }
+
+#if DBG
+ IF_TCPDBG(TCP_DEBUG_CANCEL) {
+ PLIST_ENTRY entry;
+ PIRP item = NULL;
+
+ //
+ // Verify that the Irp has not already been submitted.
+ //
+ for ( entry = TcpContext->PendingIrpList.Flink;
+ entry != &(TcpContext->PendingIrpList);
+ entry = entry->Flink
+ ) {
+
+ item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
+
+ CTEAssert(item != Irp);
+ }
+
+ for ( entry = TcpContext->CancelledIrpList.Flink;
+ entry != &(TcpContext->CancelledIrpList);
+ entry = entry->Flink
+ ) {
+
+ item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
+
+ CTEAssert(item != Irp);
+ }
+
+ InsertTailList(
+ &(TcpContext->PendingIrpList),
+ &(Irp->Tail.Overlay.ListEntry)
+ );
+ }
+#endif // DBG
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ return(STATUS_SUCCESS);
+ }
+
+ //
+ // The IRP has already been cancelled. Complete it now.
+ //
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE(("TCP: irp %lx already cancelled, completing.\n", Irp));
+ }
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ Irp->IoStatus.Status = STATUS_CANCELLED;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(STATUS_CANCELLED);
+
+} // TCPPrepareIrpForCancel
+
+
+
+//
+// TDI functions
+//
+NTSTATUS
+TCPAssociateAddress(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI Associate Address IRP into a call to TdiAssociateAddress.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+Notes:
+
+ This routine does not pend.
+
+--*/
+
+{
+ NTSTATUS status;
+ TDI_REQUEST request;
+ PTCP_CONTEXT tcpContext;
+ PTDI_REQUEST_KERNEL_ASSOCIATE associateInformation;
+ PFILE_OBJECT fileObject;
+
+
+ PAGED_CODE();
+
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ associateInformation = (PTDI_REQUEST_KERNEL_ASSOCIATE) &(IrpSp->Parameters);
+
+ //
+ // Get the file object for the address. Then extract the Address Handle
+ // from the TCP_CONTEXT associated with it.
+ //
+ status = ObReferenceObjectByHandle(
+ associateInformation->AddressHandle,
+ 0,
+ NULL,
+ KernelMode,
+ &fileObject,
+ NULL
+ );
+
+ if (NT_SUCCESS(status)) {
+
+ if ( (fileObject->DeviceObject == TCPDeviceObject) &&
+ (((int)fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE)
+ ) {
+
+ tcpContext = (PTCP_CONTEXT) fileObject->FsContext;
+
+ status = TdiAssociateAddress(
+ &request,
+ tcpContext->Handle.AddressHandle
+ );
+
+ CTEAssert(status != STATUS_PENDING);
+
+ ObDereferenceObject(fileObject);
+
+ IF_TCPDBG(TCP_DEBUG_ASSOCIATE) {
+ TCPTRACE((
+ "TCPAssociateAddress complete on file object %lx\n",
+ IrpSp->FileObject
+ ));
+ }
+ }
+ else {
+ ObDereferenceObject(fileObject);
+ status = STATUS_INVALID_HANDLE;
+
+ IF_TCPDBG(TCP_DEBUG_ASSOCIATE) {
+ TCPTRACE((
+ "TCPAssociateAddress: ObReference failed on object %lx, status %lx\n",
+ associateInformation->AddressHandle,
+ status
+ ));
+ }
+ }
+ }
+ else {
+ IF_TCPDBG(TCP_DEBUG_ASSOCIATE) {
+ TCPTRACE((
+ "TCPAssociateAddress: ObReference failed on object %lx, status %lx\n",
+ associateInformation->AddressHandle,
+ status
+ ));
+ }
+ }
+
+ return(status);
+}
+
+
+NTSTATUS
+TCPDisassociateAddress(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI Associate Address IRP into a call to TdiAssociateAddress.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+--*/
+
+{
+ NTSTATUS status;
+ TDI_REQUEST request;
+ PTCP_CONTEXT tcpContext;
+
+ IF_TCPDBG(TCP_DEBUG_ASSOCIATE) {
+ TCPTRACE(("TCP disassociating address\n"));
+ }
+
+ CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE );
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ request.RequestNotifyObject = TCPRequestComplete;
+ request.RequestContext = Irp;
+
+ status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
+
+ if (NT_SUCCESS(status)) {
+
+ status = TdiDisAssociateAddress(&request);
+
+ if (status != TDI_PENDING) {
+ TCPRequestComplete(Irp, status, 0);
+ }
+ //
+ // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
+ //
+ return(TDI_PENDING);
+ }
+
+ return(status);
+
+} // TCPDisassociateAddress
+
+
+
+NTSTATUS
+TCPConnect(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI Connect IRP into a call to TdiConnect.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ NTSTATUS status;
+ PTCP_CONTEXT tcpContext;
+ TDI_REQUEST request;
+ PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
+ PTDI_REQUEST_KERNEL_CONNECT connectRequest;
+ LARGE_INTEGER millisecondTimeout;
+ PLARGE_INTEGER requestTimeout;
+
+
+ IF_TCPDBG(TCP_DEBUG_CONNECT) {
+ TCPTRACE((
+ "TCPConnect irp %lx, file object %lx\n",
+ Irp,
+ IrpSp->FileObject
+ ));
+ }
+
+ CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
+
+ connectRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters);
+ requestInformation = connectRequest->RequestConnectionInformation;
+ returnInformation = connectRequest->ReturnConnectionInformation;
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ request.RequestNotifyObject = TCPRequestComplete;
+ request.RequestContext = Irp;
+
+ requestTimeout = (PLARGE_INTEGER) connectRequest->RequestSpecific;
+
+ if (requestTimeout != NULL) {
+ //
+ // NT relative timeouts are negative. Negate first to get a positive
+ // value to pass to the transport.
+ //
+ millisecondTimeout.QuadPart = -((*requestTimeout).QuadPart);
+ millisecondTimeout = CTEConvert100nsToMilliseconds(
+ millisecondTimeout
+ );
+ }
+ else {
+ millisecondTimeout.LowPart = 0;
+ millisecondTimeout.HighPart = 0;
+ }
+
+
+ CTEAssert(millisecondTimeout.HighPart == 0);
+
+ status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
+
+ if (NT_SUCCESS(status)) {
+
+ status = TdiConnect(
+ &request,
+ ((millisecondTimeout.LowPart != 0) ?
+ &(millisecondTimeout.LowPart) : NULL),
+ requestInformation,
+ returnInformation
+ );
+
+ if (status != STATUS_PENDING) {
+ TCPRequestComplete(Irp, status, 0);
+ }
+ //
+ // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
+ //
+ return(STATUS_PENDING);
+ }
+
+ return(status);
+
+} // TCPConnect
+
+
+NTSTATUS
+TCPDisconnect(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI Disconnect IRP into a call to TdiDisconnect.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+Notes:
+
+ Abortive disconnects may pend, but cannot be cancelled.
+
+--*/
+
+{
+ NTSTATUS status;
+ PTCP_CONTEXT tcpContext;
+ TDI_REQUEST request;
+ PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
+ PTDI_REQUEST_KERNEL_DISCONNECT disconnectRequest;
+ LARGE_INTEGER millisecondTimeout;
+ PLARGE_INTEGER requestTimeout;
+ BOOLEAN abortive = FALSE;
+
+
+ CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
+
+ disconnectRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters);
+ requestInformation = disconnectRequest->RequestConnectionInformation;
+ returnInformation = disconnectRequest->ReturnConnectionInformation;
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ request.RequestContext = Irp;
+
+ //
+ // Set up the timeout value.
+ //
+ if (disconnectRequest->RequestSpecific != NULL) {
+ requestTimeout = (PLARGE_INTEGER) disconnectRequest->RequestSpecific;
+
+ if ((requestTimeout->LowPart == -1) && (requestTimeout->HighPart == -1)) {
+ millisecondTimeout.LowPart = requestTimeout->LowPart;
+ millisecondTimeout.HighPart = 0;
+ }
+ else {
+ //
+ // NT relative timeouts are negative. Negate first to get a
+ // positive value to pass to the transport.
+ //
+ millisecondTimeout.QuadPart = -((*requestTimeout).QuadPart);
+ millisecondTimeout = CTEConvert100nsToMilliseconds(
+ millisecondTimeout
+ );
+ }
+ }
+ else {
+ millisecondTimeout.LowPart = 0;
+ millisecondTimeout.HighPart = 0;
+ }
+
+ CTEAssert(millisecondTimeout.HighPart == 0);
+
+ if (disconnectRequest->RequestFlags & TDI_DISCONNECT_ABORT) {
+ //
+ // Abortive disconnects cannot be cancelled and must use
+ // a specific completion routine.
+ //
+ abortive = TRUE;
+ IoMarkIrpPending(Irp);
+ request.RequestNotifyObject = TCPNonCancellableRequestComplete;
+ status = STATUS_SUCCESS;
+ }
+ else {
+ //
+ // Non-abortive disconnects can use the generic cancellation and
+ // completion routines.
+ //
+ status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
+ request.RequestNotifyObject = TCPRequestComplete;
+ }
+
+ IF_TCPDBG(TCP_DEBUG_CLOSE) {
+ TCPTRACE((
+ "TCPDisconnect irp %lx, flags %lx, fileobj %lx, abortive = %d\n",
+ Irp,
+ disconnectRequest->RequestFlags,
+ IrpSp->FileObject,
+ abortive
+ ));
+ }
+
+ if (NT_SUCCESS(status)) {
+ status = TdiDisconnect(
+ &request,
+ ((millisecondTimeout.LowPart != 0) ?
+ &(millisecondTimeout.LowPart) : NULL),
+ (ushort) disconnectRequest->RequestFlags,
+ requestInformation,
+ returnInformation
+ );
+
+ if (status != STATUS_PENDING) {
+ if (abortive) {
+ TCPNonCancellableRequestComplete(Irp, status, 0);
+ }
+ else {
+ TCPRequestComplete(Irp, status, 0);
+ }
+ }
+ else {
+ IF_TCPDBG(TCP_DEBUG_CLOSE) {
+ TCPTRACE(("TCPDisconnect pending irp %lx\n", Irp));
+ }
+ }
+ //
+ // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
+ //
+ return(STATUS_PENDING);
+ }
+
+ return(status);
+
+} // TCPDisconnect
+
+
+NTSTATUS
+TCPListen(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI Listen IRP into a call to TdiListen.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+--*/
+
+{
+ NTSTATUS status;
+ PTCP_CONTEXT tcpContext;
+ TDI_REQUEST request;
+ PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
+ PTDI_REQUEST_KERNEL_LISTEN listenRequest;
+
+
+ IF_TCPDBG(TCP_DEBUG_CONNECT) {
+ TCPTRACE((
+ "TCPListen irp %lx on file object %lx\n",
+ Irp,
+ IrpSp->FileObject
+ ));
+ }
+
+ CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
+
+ listenRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters);
+ requestInformation = listenRequest->RequestConnectionInformation;
+ returnInformation = listenRequest->ReturnConnectionInformation;
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ request.RequestNotifyObject = TCPRequestComplete;
+ request.RequestContext = Irp;
+
+ status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
+
+ if (NT_SUCCESS(status)) {
+
+ status = TdiListen(
+ &request,
+ (ushort) listenRequest->RequestFlags,
+ requestInformation,
+ returnInformation
+ );
+
+ if (status != TDI_PENDING) {
+ TCPRequestComplete(Irp, status, 0);
+ }
+ //
+ // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
+ //
+ return(TDI_PENDING);
+ }
+
+ return(status);
+
+} // TCPListen
+
+
+NTSTATUS
+TCPAccept(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI Accept IRP into a call to TdiAccept.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ NTSTATUS status;
+ PTCP_CONTEXT tcpContext;
+ TDI_REQUEST request;
+ PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
+ PTDI_REQUEST_KERNEL_ACCEPT acceptRequest;
+
+
+ IF_TCPDBG(TCP_DEBUG_CONNECT) {
+ TCPTRACE((
+ "TCPAccept irp %lx on file object %lx\n", Irp,
+ IrpSp->FileObject
+ ));
+ }
+
+ CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
+
+ acceptRequest = (PTDI_REQUEST_KERNEL_ACCEPT) &(IrpSp->Parameters);
+ requestInformation = acceptRequest->RequestConnectionInformation;
+ returnInformation = acceptRequest->ReturnConnectionInformation;
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ request.RequestNotifyObject = TCPRequestComplete;
+ request.RequestContext = Irp;
+
+ status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
+
+ if (NT_SUCCESS(status)) {
+
+ status = TdiAccept(
+ &request,
+ requestInformation,
+ returnInformation
+ );
+
+ if (status != TDI_PENDING) {
+ TCPRequestComplete(Irp, status, 0);
+ }
+ //
+ // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
+ //
+ return(TDI_PENDING);
+ }
+
+ return(status);
+
+} // TCPAccept
+
+
+
+NTSTATUS
+TCPSendData(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI Send IRP into a call to TdiSend.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+--*/
+
+{
+ TDI_STATUS status;
+ TDI_REQUEST request;
+ PTCP_CONTEXT tcpContext;
+ PTDI_REQUEST_KERNEL_SEND requestInformation;
+ KIRQL oldIrql;
+
+
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE );
+ requestInformation = (PTDI_REQUEST_KERNEL_SEND) &(IrpSp->Parameters);
+
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ request.RequestNotifyObject = TCPDataRequestComplete;
+ request.RequestContext = Irp;
+
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ CTEAssert(Irp->CancelRoutine == NULL);
+
+ if (!Irp->Cancel) {
+ //
+ // Set up for cancellation
+ //
+
+ IoMarkIrpPending(Irp);
+ IoSetCancelRoutine(Irp, TCPCancelRequest);
+
+ tcpContext->ReferenceCount++;
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE((
+ "TCPSendData: irp %lx fileobj %lx refcnt inc to %u\n",
+ Irp,
+ IrpSp,
+ tcpContext->ReferenceCount
+ ));
+ }
+
+#if DBG
+ IF_TCPDBG(TCP_DEBUG_CANCEL) {
+ PLIST_ENTRY entry;
+ PIRP item = NULL;
+
+ //
+ // Verify that the Irp has not already been submitted.
+ //
+ for ( entry = tcpContext->PendingIrpList.Flink;
+ entry != &(tcpContext->PendingIrpList);
+ entry = entry->Flink
+ ) {
+
+ item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
+
+ CTEAssert(item != Irp);
+ }
+
+ for ( entry = tcpContext->CancelledIrpList.Flink;
+ entry != &(tcpContext->CancelledIrpList);
+ entry = entry->Flink
+ ) {
+
+ item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
+
+ CTEAssert(item != Irp);
+ }
+
+ InsertTailList(
+ &(tcpContext->PendingIrpList),
+ &(Irp->Tail.Overlay.ListEntry)
+ );
+ }
+#endif // DBG
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ IF_TCPDBG(TCP_DEBUG_SEND) {
+ TCPTRACE((
+ "TCPSendData irp %lx sending %d bytes, flags %lx, fileobj %lx\n",
+ Irp,
+ requestInformation->SendLength,
+ requestInformation->SendFlags,
+ IrpSp->FileObject
+ ));
+ }
+
+ status = TdiSend(
+ &request,
+ (ushort) requestInformation->SendFlags,
+ requestInformation->SendLength,
+ (PNDIS_BUFFER) Irp->MdlAddress
+ );
+
+ if (status == TDI_PENDING) {
+ IF_TCPDBG(TCP_DEBUG_SEND) {
+ TCPTRACE(("TCPSendData pending irp %lx\n", Irp));
+ }
+
+ return(status);
+ }
+
+ //
+ // The status is not pending. We reset the pending bit
+ //
+ IrpSp->Control &= ~SL_PENDING_RETURNED;
+
+ if (status == TDI_SUCCESS) {
+ CTEAssert(requestInformation->SendLength == 0);
+
+ TCPDataRequestComplete(Irp, status, requestInformation->SendLength);
+ }
+ else {
+
+ IF_TCPDBG(TCP_DEBUG_SEND) {
+ TCPTRACE((
+ "TCPSendData - irp %lx send failed, status %lx\n",
+ Irp,
+ status
+ ));
+ }
+
+ TCPDataRequestComplete(Irp, status, 0);
+ }
+
+
+ }
+ else {
+ //
+ // Irp was cancelled previously.
+ //
+ IoReleaseCancelSpinLock(oldIrql);
+
+ IF_TCPDBG(TCP_DEBUG_SEND) {
+ TCPTRACE((
+ "TCPSendData: Irp %lx on fileobj %lx was cancelled\n",
+ Irp,
+ IrpSp->FileObject
+ ));
+ }
+
+ Irp->IoStatus.Status = STATUS_CANCELLED;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ status = STATUS_CANCELLED;
+ }
+
+ return(status);
+
+} // TCPSendData
+
+
+
+NTSTATUS
+TCPReceiveData(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI Receive IRP into a call to TdiReceive.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+--*/
+
+{
+ TDI_STATUS status;
+ TDI_REQUEST request;
+ PTCP_CONTEXT tcpContext;
+ PTDI_REQUEST_KERNEL_RECEIVE requestInformation;
+ KIRQL oldIrql;
+
+
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE );
+ requestInformation = (PTDI_REQUEST_KERNEL_RECEIVE) &(IrpSp->Parameters);
+
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ request.RequestNotifyObject = TCPDataRequestComplete;
+ request.RequestContext = Irp;
+
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ CTEAssert(Irp->CancelRoutine == NULL);
+
+ if (!Irp->Cancel) {
+ //
+ // Set up for cancellation
+ //
+
+ IoMarkIrpPending(Irp);
+ IoSetCancelRoutine(Irp, TCPCancelRequest);
+
+ tcpContext->ReferenceCount++;
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE((
+ "TCPReceiveData: irp %lx fileobj %lx refcnt inc to %u\n",
+ Irp,
+ IrpSp->FileObject,
+ tcpContext->ReferenceCount
+ ));
+ }
+
+#if DBG
+ IF_TCPDBG(TCP_DEBUG_CANCEL) {
+ PLIST_ENTRY entry;
+ PIRP item = NULL;
+
+ //
+ // Verify that the Irp has not already been submitted.
+ //
+ for ( entry = tcpContext->PendingIrpList.Flink;
+ entry != &(tcpContext->PendingIrpList);
+ entry = entry->Flink
+ ) {
+
+ item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
+
+ CTEAssert(item != Irp);
+ }
+
+ for ( entry = tcpContext->CancelledIrpList.Flink;
+ entry != &(tcpContext->CancelledIrpList);
+ entry = entry->Flink
+ ) {
+
+ item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
+
+ CTEAssert(item != Irp);
+ }
+
+ InsertTailList(
+ &(tcpContext->PendingIrpList),
+ &(Irp->Tail.Overlay.ListEntry)
+ );
+ }
+#endif // DBG
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ IF_TCPDBG(TCP_DEBUG_RECEIVE) {
+ TCPTRACE((
+ "TCPReceiveData irp %lx receiving %d bytes flags %lx filobj %lx\n",
+ Irp,
+ requestInformation->ReceiveLength,
+ requestInformation->ReceiveFlags,
+ IrpSp->FileObject
+ ));
+ }
+
+ status = TdiReceive(
+ &request,
+ (ushort *) &(requestInformation->ReceiveFlags),
+ &(requestInformation->ReceiveLength),
+ (PNDIS_BUFFER) Irp->MdlAddress
+ );
+
+ if (status == TDI_PENDING) {
+ IF_TCPDBG(TCP_DEBUG_RECEIVE) {
+ TCPTRACE(("TCPReceiveData: pending irp %lx\n", Irp));
+ }
+
+ return(status);
+ }
+
+ //
+ // The status is not pending. We reset the pending bit
+ //
+ IrpSp->Control &= ~SL_PENDING_RETURNED;
+
+ CTEAssert(status != TDI_SUCCESS);
+
+ IF_TCPDBG(TCP_DEBUG_RECEIVE) {
+ TCPTRACE((
+ "TCPReceiveData - irp %lx failed, status %lx\n",
+ Irp,
+ status
+ ));
+ }
+
+ TCPDataRequestComplete(Irp, status, 0);
+ }
+ else {
+ //
+ // Irp was cancelled previously.
+ //
+ IoReleaseCancelSpinLock(oldIrql);
+
+ IF_TCPDBG(TCP_DEBUG_SEND) {
+ TCPTRACE((
+ "TCPReceiveData: Irp %lx on fileobj %lx was cancelled\n",
+ Irp,
+ IrpSp->FileObject
+ ));
+ }
+
+ Irp->IoStatus.Status = STATUS_CANCELLED;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ status = STATUS_CANCELLED;
+ }
+
+ return status;
+
+} // TCPReceiveData
+
+
+
+NTSTATUS
+UDPSendDatagram(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ TDI_STATUS status;
+ TDI_REQUEST request;
+ PTCP_CONTEXT tcpContext;
+ PTDI_REQUEST_KERNEL_SENDDG datagramInformation;
+ ULONG bytesSent = 0;
+
+
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ datagramInformation = (PTDI_REQUEST_KERNEL_SENDDG) &(IrpSp->Parameters);
+ CTEAssert(((int)IrpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE);
+
+ request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
+ request.RequestNotifyObject = TCPDataRequestComplete;
+ request.RequestContext = Irp;
+
+ IF_TCPDBG(TCP_DEBUG_SEND_DGRAM) {
+ TCPTRACE((
+ "UDPSendDatagram irp %lx sending %d bytes\n",
+ Irp,
+ datagramInformation->SendLength
+ ));
+ }
+
+ status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
+
+ if (NT_SUCCESS(status)) {
+
+ status = TdiSendDatagram(
+ &request,
+ datagramInformation->SendDatagramInformation,
+ datagramInformation->SendLength,
+ &bytesSent,
+ (PNDIS_BUFFER) Irp->MdlAddress
+ );
+
+ if (status == TDI_PENDING) {
+ return(status);
+ }
+
+ CTEAssert(status != TDI_SUCCESS);
+ CTEAssert(bytesSent == 0);
+
+ TCPTRACE((
+ "UDPSendDatagram - irp %lx send failed, status %lx\n",
+ Irp,
+ status
+ ));
+
+ TCPDataRequestComplete(Irp, status, bytesSent);
+ //
+ // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
+ //
+ return(TDI_PENDING);
+ }
+
+ return status;
+
+} // UDPSendDatagram
+
+
+
+NTSTATUS
+UDPReceiveDatagram(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI ReceiveDatagram IRP into a call to TdiReceiveDatagram.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+--*/
+
+{
+ TDI_STATUS status;
+ TDI_REQUEST request;
+ PTCP_CONTEXT tcpContext;
+ PTDI_REQUEST_KERNEL_RECEIVEDG datagramInformation;
+ uint bytesReceived = 0;
+
+
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ datagramInformation = (PTDI_REQUEST_KERNEL_RECEIVEDG) &(IrpSp->Parameters);
+ CTEAssert(((int)IrpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE);
+
+ request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
+ request.RequestNotifyObject = TCPDataRequestComplete;
+ request.RequestContext = Irp;
+
+ IF_TCPDBG(TCP_DEBUG_RECEIVE_DGRAM) {
+ TCPTRACE((
+ "UDPReceiveDatagram: irp %lx receiveing %d bytes\n",
+ Irp,
+ datagramInformation->ReceiveLength
+ ));
+ }
+
+ status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
+
+ if (NT_SUCCESS(status)) {
+
+ status = TdiReceiveDatagram(
+ &request,
+ datagramInformation->ReceiveDatagramInformation,
+ datagramInformation->ReturnDatagramInformation,
+ datagramInformation->ReceiveLength,
+ &bytesReceived,
+ Irp->MdlAddress
+ );
+
+ if (status == TDI_PENDING) {
+ return(status);
+ }
+
+ CTEAssert(status != TDI_SUCCESS);
+ CTEAssert(bytesReceived == 0);
+
+ TCPTRACE((
+ "UDPReceiveDatagram: irp %lx send failed, status %lx\n",
+ Irp,
+ status
+ ));
+
+ TCPDataRequestComplete(Irp, status, bytesReceived);
+ //
+ // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
+ //
+ return(TDI_PENDING);
+ }
+
+ return status;
+
+} // UDPReceiveDatagram
+
+
+
+NTSTATUS
+TCPSetEventHandler(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI SetEventHandler IRP into a call to TdiSetEventHandler.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+Notes:
+
+ This routine does not pend.
+
+--*/
+
+{
+ NTSTATUS status;
+ PTDI_REQUEST_KERNEL_SET_EVENT event;
+ PTCP_CONTEXT tcpContext;
+
+ PAGED_CODE();
+
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ event = (PTDI_REQUEST_KERNEL_SET_EVENT) &(IrpSp->Parameters);
+
+ IF_TCPDBG(TCP_DEBUG_EVENT_HANDLER) {
+ TCPTRACE((
+ "TCPSetEventHandler: irp %lx event %lx handler %lx context %lx\n",
+ Irp,
+ event->EventType,
+ event->EventHandler,
+ event->EventContext
+ ));
+ }
+
+ status = TdiSetEvent(
+ tcpContext->Handle.AddressHandle,
+ event->EventType,
+ event->EventHandler,
+ event->EventContext
+ );
+
+ CTEAssert(status != TDI_PENDING);
+
+ return(status);
+
+} // TCPSetEventHandler
+
+
+
+NTSTATUS
+TCPQueryInformation(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI QueryInformation IRP into a call to TdiQueryInformation.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+Notes:
+
+--*/
+
+{
+ TDI_REQUEST request;
+ TDI_STATUS status = STATUS_SUCCESS;
+ PTCP_CONTEXT tcpContext;
+ PTDI_REQUEST_KERNEL_QUERY_INFORMATION queryInformation;
+ uint isConn = FALSE;
+ uint dataSize = 0;
+
+
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ queryInformation = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)
+ &(IrpSp->Parameters);
+
+ request.RequestNotifyObject = TCPDataRequestComplete;
+ request.RequestContext = Irp;
+
+ switch(queryInformation->QueryType) {
+
+ case TDI_QUERY_BROADCAST_ADDRESS:
+ CTEAssert( ((int) IrpSp->FileObject->FsContext2) ==
+ TDI_CONTROL_CHANNEL_FILE
+ );
+ request.Handle.ControlChannel = tcpContext->Handle.ControlChannel;
+ break;
+
+ case TDI_QUERY_PROVIDER_INFO:
+//
+// NetBT does this. Reinstate the CTEAssert when it is fixed.
+//
+// CTEAssert( ((int) IrpSp->FileObject->FsContext2) ==
+// TDI_CONTROL_CHANNEL_FILE
+// );
+ request.Handle.ControlChannel = tcpContext->Handle.ControlChannel;
+ break;
+
+ case TDI_QUERY_ADDRESS_INFO:
+ if (((int) IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) {
+ //
+ // This is a TCP connection object.
+ //
+ isConn = TRUE;
+ request.Handle.ConnectionContext =
+ tcpContext->Handle.ConnectionContext;
+ }
+ else {
+ //
+ // This is an address object
+ //
+ request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
+ }
+ break;
+
+ case TDI_QUERY_CONNECTION_INFO:
+ CTEAssert(((int) IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
+ isConn = TRUE;
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ break;
+
+ case TDI_QUERY_PROVIDER_STATISTICS:
+ CTEAssert( ((int) IrpSp->FileObject->FsContext2) ==
+ TDI_CONTROL_CHANNEL_FILE
+ );
+ request.Handle.ControlChannel = tcpContext->Handle.ControlChannel;
+ break;
+
+ default:
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ if (NT_SUCCESS(status)) {
+ //
+ // This request isn't cancellable, but we put it through
+ // the cancel path because it handles some checks for us
+ // and tracks the irp.
+ //
+ status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL);
+
+ if (NT_SUCCESS(status)) {
+ dataSize = TCPGetMdlChainByteCount(Irp->MdlAddress);
+
+ status = TdiQueryInformation(
+ &request,
+ queryInformation->QueryType,
+ Irp->MdlAddress,
+ &dataSize,
+ isConn
+ );
+
+ if (status != TDI_PENDING) {
+ TCPDataRequestComplete(Irp, status, dataSize);
+ return(status);
+
+ }
+
+ return(STATUS_PENDING);
+ }
+
+ return(status);
+ }
+
+ Irp->IoStatus.Status = (NTSTATUS) status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(status);
+
+} // TCPQueryInformation
+
+
+
+void
+TCPQueryInformationExComplete(
+ void *Context,
+ unsigned int Status,
+ unsigned int ByteCount
+ )
+
+/*++
+
+Routine Description:
+
+ Completes a TdiQueryInformationEx request.
+
+Arguments:
+
+ Context - A pointer to the IRP for this request.
+ Status - The final TDI status of the request.
+ ByteCount - Bytes returned in output buffer.
+
+Return Value:
+
+ None.
+
+Notes:
+
+--*/
+{
+ PTCP_QUERY_CONTEXT queryContext = (PTCP_QUERY_CONTEXT) Context;
+ ULONG bytesCopied;
+
+
+ if (NT_SUCCESS(Status)) {
+ //
+ // Copy the returned context to the input buffer.
+ //
+ TdiCopyBufferToMdl(
+ &(queryContext->QueryInformation.Context),
+ 0,
+ CONTEXT_SIZE,
+ queryContext->InputMdl,
+ FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX, Context),
+ &bytesCopied
+ );
+
+ CTEAssert(bytesCopied == CONTEXT_SIZE);
+ }
+
+ //
+ // Unlock the user's buffers and free the MDLs describing them.
+ //
+ MmUnlockPages(queryContext->InputMdl);
+ IoFreeMdl(queryContext->InputMdl);
+ MmUnlockPages(queryContext->OutputMdl);
+ IoFreeMdl(queryContext->OutputMdl);
+
+ //
+ // Complete the request
+ //
+ TCPDataRequestComplete(queryContext->Irp, Status, ByteCount);
+
+ CTEFreeMem(queryContext);
+
+ return;
+}
+
+
+
+NTSTATUS
+TCPQueryInformationEx(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI QueryInformationEx IRP into a call to TdiQueryInformationEx.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+Notes:
+
+--*/
+
+{
+ TDI_REQUEST request;
+ TDI_STATUS status = STATUS_SUCCESS;
+ PTCP_CONTEXT tcpContext;
+ uint size;
+ PTCP_REQUEST_QUERY_INFORMATION_EX InputBuffer;
+ PVOID OutputBuffer;
+ PMDL inputMdl = NULL;
+ PMDL outputMdl = NULL;
+ ULONG InputBufferLength,
+ OutputBufferLength;
+ PTCP_QUERY_CONTEXT queryContext;
+ BOOLEAN inputLocked = FALSE;
+ BOOLEAN outputLocked = FALSE;
+
+
+ PAGED_CODE();
+
+ IF_TCPDBG(TCP_DEBUG_INFO) {
+ TCPTRACE((
+ "QueryInformationEx starting - irp %lx fileobj %lx\n",
+ Irp,
+ IrpSp->FileObject
+ ));
+ }
+
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+
+ switch ((int) IrpSp->FileObject->FsContext2) {
+
+ case TDI_TRANSPORT_ADDRESS_FILE:
+ request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
+ break;
+
+ case TDI_CONNECTION_FILE:
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ break;
+
+ case TDI_CONTROL_CHANNEL_FILE:
+ request.Handle.ControlChannel = tcpContext->Handle.ControlChannel;
+ break;
+
+ default:
+ CTEAssert(0);
+
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ //
+ // Validate the input parameters
+ //
+ if ( (InputBufferLength == sizeof(TCP_REQUEST_QUERY_INFORMATION_EX)) &&
+ (OutputBufferLength != 0)
+ )
+ {
+ OutputBuffer = Irp->UserBuffer;
+ InputBuffer = (PTCP_REQUEST_QUERY_INFORMATION_EX)
+ IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
+
+ queryContext = CTEAllocMem(sizeof(TCP_QUERY_CONTEXT));
+
+ if (queryContext != NULL) {
+ status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL);
+
+ if (!NT_SUCCESS(status)) {
+ CTEFreeMem(queryContext);
+ return(status);
+ }
+
+ //
+ // Allocate Mdls to describe the input and output buffers.
+ // Probe and lock the buffers.
+ //
+ try {
+ inputMdl = IoAllocateMdl(
+ InputBuffer,
+ sizeof(TCP_REQUEST_QUERY_INFORMATION_EX),
+ FALSE,
+ TRUE,
+ NULL
+ );
+
+ outputMdl = IoAllocateMdl(
+ OutputBuffer,
+ OutputBufferLength,
+ FALSE,
+ TRUE,
+ NULL
+ );
+
+ if ((inputMdl != NULL) && (outputMdl != NULL)) {
+
+ MmProbeAndLockPages(
+ inputMdl,
+ Irp->RequestorMode,
+ IoModifyAccess
+ );
+
+ inputLocked = TRUE;
+
+ MmProbeAndLockPages(
+ outputMdl,
+ Irp->RequestorMode,
+ IoWriteAccess
+ );
+
+ outputLocked = TRUE;
+
+ //
+ // Copy the input parameter to our pool block so
+ // TdiQueryInformationEx can manipulate it directly.
+ //
+ RtlCopyMemory(
+ &(queryContext->QueryInformation),
+ InputBuffer,
+ sizeof(TCP_REQUEST_QUERY_INFORMATION_EX)
+ );
+ }
+ else {
+
+ IF_TCPDBG(TCP_DEBUG_INFO) {
+ TCPTRACE(("QueryInfoEx: Couldn't allocate MDL\n"));
+ }
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+
+ IF_TCPDBG(TCP_DEBUG_INFO) {
+ TCPTRACE((
+ "QueryInfoEx: exception copying input params %lx\n",
+ GetExceptionCode()
+ ));
+ }
+
+ status = GetExceptionCode();
+ }
+
+ if (NT_SUCCESS(status)) {
+ //
+ // It's finally time to do this thing.
+ //
+ size = TCPGetMdlChainByteCount(outputMdl);
+
+ queryContext->Irp = Irp;
+ queryContext->InputMdl = inputMdl;
+ queryContext->OutputMdl = outputMdl;
+
+ request.RequestNotifyObject = TCPQueryInformationExComplete;
+ request.RequestContext = queryContext;
+
+ status = TdiQueryInformationEx(
+ &request,
+ &(queryContext->QueryInformation.ID),
+ outputMdl,
+ &size,
+ &(queryContext->QueryInformation.Context)
+ );
+
+ if (status != TDI_PENDING) {
+ TCPQueryInformationExComplete(
+ queryContext,
+ status,
+ size
+ );
+
+ return(status);
+ }
+
+ IF_TCPDBG(TCP_DEBUG_INFO) {
+ TCPTRACE((
+ "QueryInformationEx - pending irp %lx fileobj %lx\n",
+ Irp,
+ IrpSp->FileObject
+ ));
+ }
+
+ return(STATUS_PENDING);
+ }
+
+ //
+ // If we get here, something failed. Clean up.
+ //
+ if (inputMdl != NULL) {
+ if (inputLocked) {
+ MmUnlockPages(inputMdl);
+ }
+
+ IoFreeMdl(inputMdl);
+ }
+
+ if (outputMdl != NULL) {
+ if (outputLocked) {
+ MmUnlockPages(outputMdl);
+ }
+
+ IoFreeMdl(outputMdl);
+ }
+
+ CTEFreeMem(queryContext);
+
+ TCPDataRequestComplete(Irp, status, 0);
+
+ return(status);
+
+ }
+ else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+
+ IF_TCPDBG(TCP_DEBUG_INFO) {
+ TCPTRACE(("QueryInfoEx: Unable to allocate query context\n"));
+ }
+ }
+ }
+ else {
+ status = STATUS_INVALID_PARAMETER;
+
+ IF_TCPDBG(TCP_DEBUG_INFO) {
+ TCPTRACE((
+ "QueryInfoEx: Bad buffer len, OBufLen %d, InBufLen %d\n",
+ OutputBufferLength, InputBufferLength
+ ));
+ }
+ }
+
+
+ IF_TCPDBG(TCP_DEBUG_INFO) {
+ TCPTRACE((
+ "QueryInformationEx complete - irp %lx, status %lx\n",
+ Irp,
+ status
+ ));
+ }
+
+ Irp->IoStatus.Status = (NTSTATUS) status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+
+ return(status);
+}
+
+
+
+NTSTATUS
+TCPSetInformationEx(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a TDI SetInformationEx IRP into a call to TdiSetInformationEx.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+Notes:
+
+ This routine does not pend.
+
+--*/
+
+{
+ TDI_REQUEST request;
+ TDI_STATUS status;
+ PTCP_CONTEXT tcpContext;
+ PTCP_REQUEST_SET_INFORMATION_EX setInformation;
+
+
+ PAGED_CODE();
+
+ IF_TCPDBG(TCP_DEBUG_INFO) {
+ TCPTRACE((
+ "SetInformationEx - irp %lx fileobj %lx\n",
+ Irp,
+ IrpSp->FileObject
+ ));
+ }
+
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+ setInformation = (PTCP_REQUEST_SET_INFORMATION_EX)
+ Irp->AssociatedIrp.SystemBuffer;
+
+ switch ((int) IrpSp->FileObject->FsContext2) {
+
+ case TDI_TRANSPORT_ADDRESS_FILE:
+ request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
+ break;
+
+ case TDI_CONNECTION_FILE:
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ break;
+
+ case TDI_CONTROL_CHANNEL_FILE:
+ request.Handle.ControlChannel = tcpContext->Handle.ControlChannel;
+ break;
+
+ default:
+ CTEAssert(0);
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL);
+
+ if (NT_SUCCESS(status)) {
+ request.RequestNotifyObject = TCPDataRequestComplete;
+ request.RequestContext = Irp;
+
+ status = TdiSetInformationEx(
+ &request,
+ &(setInformation->ID),
+ &(setInformation->Buffer[0]),
+ setInformation->BufferSize
+ );
+
+ if (status != TDI_PENDING) {
+ TCPDataRequestComplete(
+ Irp,
+ status,
+ 0
+ );
+
+ return(status);
+ }
+
+ IF_TCPDBG(TCP_DEBUG_INFO) {
+ TCPTRACE((
+ "SetInformationEx - pending irp %lx fileobj %lx\n",
+ Irp,
+ IrpSp->FileObject
+ ));
+ }
+
+ return(STATUS_PENDING);
+ }
+
+ IF_TCPDBG(TCP_DEBUG_INFO) {
+ TCPTRACE((
+ "SetInformationEx complete - irp %lx\n",
+ Irp
+ ));
+ }
+
+ //
+ // The irp has already been completed.
+ //
+ return(status);
+}
+
+
+#ifdef SECFLTR
+
+
+
+NTSTATUS
+TCPControlSecurityFilter(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Processes a request to query or set the status of security filtering.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+Notes:
+
+ This routine does not pend.
+
+--*/
+
+{
+
+ PTCP_SECURITY_FILTER_STATUS request;
+ ULONG requestLength;
+ ULONG requestCode;
+ TDI_STATUS status = STATUS_SUCCESS;
+
+
+ PAGED_CODE();
+
+ Irp->IoStatus.Information = 0;
+
+ request = (PTCP_SECURITY_FILTER_STATUS) Irp->AssociatedIrp.SystemBuffer;
+ requestCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
+
+ if (requestCode == IOCTL_TCP_QUERY_SECURITY_FILTER_STATUS) {
+ requestLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (requestLength < sizeof(TCP_SECURITY_FILTER_STATUS)) {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else {
+ request->FilteringEnabled = IsSecurityFilteringEnabled();
+ Irp->IoStatus.Information = sizeof(TCP_SECURITY_FILTER_STATUS);
+ }
+ }
+ else {
+ CTEAssert(requestCode == IOCTL_TCP_SET_SECURITY_FILTER_STATUS);
+
+ requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+
+ if (requestLength < sizeof(TCP_SECURITY_FILTER_STATUS)) {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else {
+ ControlSecurityFiltering(request->FilteringEnabled);
+ }
+ }
+
+ Irp->IoStatus.Status = status;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(status);
+}
+
+
+
+NTSTATUS
+TCPProcessSecurityFilterRequest(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Processes a request to add or delete a transport security filter.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+Notes:
+
+ This routine does not pend.
+
+--*/
+
+{
+ TCPSecurityFilterEntry *request;
+ ULONG requestLength;
+ ULONG i;
+ ULONG requestCode;
+ NTSTATUS status = STATUS_SUCCESS;
+
+
+ PAGED_CODE();
+
+ Irp->IoStatus.Information = 0;
+
+ request = (TCPSecurityFilterEntry *) Irp->AssociatedIrp.SystemBuffer;
+ requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+ requestCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
+
+ if (requestLength < sizeof(TCPSecurityFilterEntry)) {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else {
+ if (requestCode == IOCTL_TCP_ADD_SECURITY_FILTER) {
+ status = AddValueSecurityFilter(
+ net_long(request->tsf_address),
+ request->tsf_protocol,
+ request->tsf_value
+ );
+ }
+ else {
+ CTEAssert(requestCode == IOCTL_TCP_DELETE_SECURITY_FILTER);
+ status = DeleteValueSecurityFilter(
+ net_long(request->tsf_address),
+ request->tsf_protocol,
+ request->tsf_value
+ );
+ }
+ }
+
+ Irp->IoStatus.Status = status;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(status);
+}
+
+
+
+NTSTATUS
+TCPEnumerateSecurityFilter(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Processes a request to enumerate a transport security filter list.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+Notes:
+
+ This routine does not pend.
+
+--*/
+
+{
+
+ TCPSecurityFilterEntry *request;
+ TCPSecurityFilterEnum *response;
+ ULONG requestLength, responseLength;
+ NTSTATUS status;
+
+
+ PAGED_CODE();
+
+ request = (TCPSecurityFilterEntry *) Irp->AssociatedIrp.SystemBuffer;
+ response = (TCPSecurityFilterEnum *) request;
+ requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+ responseLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (requestLength < sizeof(TCPSecurityFilterEntry)) {
+ status = STATUS_INVALID_PARAMETER;
+ Irp->IoStatus.Information = 0;
+ }
+ else if (responseLength < sizeof(TCPSecurityFilterEnum)) {
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = 0;
+ }
+ else {
+ EnumerateSecurityFilters(
+ net_long(request->tsf_address),
+ request->tsf_protocol,
+ request->tsf_value,
+ (uchar *) (response + 1),
+ responseLength - sizeof(TCPSecurityFilterEnum),
+ &(response->tfe_entries_returned),
+ &(response->tfe_entries_available)
+ );
+
+ status = TDI_SUCCESS;
+ Irp->IoStatus.Information =
+ sizeof(TCPSecurityFilterEnum) +
+ (response->tfe_entries_returned * sizeof(TCPSecurityFilterEntry));
+
+
+ }
+
+ Irp->IoStatus.Status = status;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(status);
+}
+
+#endif // SECFLTR
+
+
+NTSTATUS
+TCPEnumerateConnectionList(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Processes a request to enumerate the workstation connection list.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successful.
+
+Notes:
+
+ This routine does not pend.
+
+--*/
+
+{
+
+ TCPConnectionListEntry *request;
+ TCPConnectionListEnum *response;
+ ULONG requestLength, responseLength;
+ NTSTATUS status;
+
+
+ PAGED_CODE();
+
+ request = (TCPConnectionListEntry *) Irp->AssociatedIrp.SystemBuffer;
+ response = (TCPConnectionListEnum *) request;
+ requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+ responseLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (responseLength < sizeof(TCPConnectionListEnum)) {
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = 0;
+ }
+ else {
+ EnumerateConnectionList(
+ (uchar *) (response + 1),
+ responseLength - sizeof(TCPConnectionListEnum),
+ &(response->tce_entries_returned),
+ &(response->tce_entries_available)
+ );
+
+ status = TDI_SUCCESS;
+ Irp->IoStatus.Information =
+ sizeof(TCPConnectionListEnum) +
+ (response->tce_entries_returned * sizeof(TCPConnectionListEntry));
+
+
+ }
+
+ Irp->IoStatus.Status = status;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(status);
+}
+
+
+
+NTSTATUS
+TCPCreate(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+ DeviceObject - Pointer to the device object for this request.
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ TDI_REQUEST Request;
+ NTSTATUS status;
+ FILE_FULL_EA_INFORMATION *ea;
+ FILE_FULL_EA_INFORMATION UNALIGNED *targetEA;
+ PTCP_CONTEXT tcpContext;
+ uint protocol;
+
+
+ PAGED_CODE();
+
+ tcpContext = ExAllocatePool(NonPagedPool, sizeof(TCP_CONTEXT));
+
+ if (tcpContext == NULL) {
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+#if DBG
+ InitializeListHead(&(tcpContext->PendingIrpList));
+ InitializeListHead(&(tcpContext->CancelledIrpList));
+#endif
+
+ tcpContext->ReferenceCount = 1; // put initial reference on open object
+ tcpContext->CancelIrps = FALSE;
+ KeInitializeEvent(&(tcpContext->CleanupEvent), SynchronizationEvent, FALSE);
+
+ ea = (PFILE_FULL_EA_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // See if this is a Control Channel open.
+ //
+ if (!ea) {
+ IF_TCPDBG(TCP_DEBUG_OPEN) {
+ TCPTRACE((
+ "TCPCreate: Opening control channel for file object %lx\n",
+ IrpSp->FileObject
+ ));
+ }
+
+ tcpContext->Handle.ControlChannel = NULL;
+ IrpSp->FileObject->FsContext = tcpContext;
+ IrpSp->FileObject->FsContext2 = (PVOID) TDI_CONTROL_CHANNEL_FILE;
+
+ return(STATUS_SUCCESS);
+ }
+
+ //
+ // See if this is an Address Object open.
+ //
+ targetEA = FindEA(
+ ea,
+ TdiTransportAddress,
+ TDI_TRANSPORT_ADDRESS_LENGTH
+ );
+
+ if (targetEA != NULL) {
+ UCHAR optionsBuffer[3];
+ PUCHAR optionsPointer = optionsBuffer;
+
+
+ if (DeviceObject == TCPDeviceObject) {
+ protocol = PROTOCOL_TCP;
+ }
+ else if (DeviceObject == UDPDeviceObject) {
+ protocol = PROTOCOL_UDP;
+
+ CTEAssert(optionsPointer - optionsBuffer <= 3);
+
+ if (IsDHCPZeroAddress(
+ (TRANSPORT_ADDRESS UNALIGNED *)
+ &(targetEA->EaName[targetEA->EaNameLength + 1])
+ )) {
+ *optionsPointer = TDI_ADDRESS_OPTION_DHCP;
+ optionsPointer++;
+ }
+
+ CTEAssert(optionsPointer - optionsBuffer <= 3);
+ }
+ else {
+ //
+ // This is a raw ip open
+ //
+ protocol = RawExtractProtocolNumber(
+ &(IrpSp->FileObject->FileName)
+ );
+
+ if (protocol == 0xFFFFFFFF) {
+ ExFreePool(tcpContext);
+ return(STATUS_INVALID_PARAMETER);
+ }
+ }
+
+ if ( (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) ||
+ (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)
+ ) {
+ *optionsPointer = TDI_ADDRESS_OPTION_REUSE;
+ optionsPointer++;
+ }
+
+ *optionsPointer = TDI_OPTION_EOL;
+
+ IF_TCPDBG(TCP_DEBUG_OPEN) {
+ TCPTRACE((
+ "TCPCreate: Opening address for file object %lx\n",
+ IrpSp->FileObject
+ ));
+ }
+
+ status = TdiOpenAddress(
+ &Request,
+ (TRANSPORT_ADDRESS UNALIGNED *)
+ &(targetEA->EaName[targetEA->EaNameLength + 1]),
+ protocol,
+ optionsBuffer
+ );
+
+ if (NT_SUCCESS(status)) {
+ //
+ // Save off the handle to the AO passed back.
+ //
+ tcpContext->Handle.AddressHandle = Request.Handle.AddressHandle;
+ IrpSp->FileObject->FsContext = tcpContext;
+ IrpSp->FileObject->FsContext2 =
+ (PVOID) TDI_TRANSPORT_ADDRESS_FILE;
+ }
+ else {
+ ExFreePool(tcpContext);
+ TCPTRACE(("TdiOpenAddress failed, status %lx\n", status));
+ if (status == STATUS_ADDRESS_ALREADY_EXISTS) {
+ status = STATUS_SHARING_VIOLATION;
+ }
+ }
+
+ CTEAssert(status != TDI_PENDING);
+
+ return(status);
+ }
+
+ //
+ // See if this is a Connection Object open.
+ //
+ targetEA = FindEA(
+ ea,
+ TdiConnectionContext,
+ TDI_CONNECTION_CONTEXT_LENGTH
+ );
+
+ if (targetEA != NULL) {
+ //
+ // This is an open of a Connection Object.
+ //
+
+ if (DeviceObject == TCPDeviceObject) {
+
+ IF_TCPDBG(TCP_DEBUG_OPEN) {
+ TCPTRACE((
+ "TCPCreate: Opening connection for file object %lx\n",
+ IrpSp->FileObject
+ ));
+ }
+
+ status = TdiOpenConnection(
+ &Request,
+ *((CONNECTION_CONTEXT UNALIGNED *)
+ &(targetEA->EaName[targetEA->EaNameLength + 1]))
+ );
+
+ if (NT_SUCCESS(status)) {
+ //
+ // Save off the Connection Context passed back.
+ //
+ tcpContext->Handle.ConnectionContext =
+ Request.Handle.ConnectionContext;
+ IrpSp->FileObject->FsContext = tcpContext;
+ IrpSp->FileObject->FsContext2 =
+ (PVOID) TDI_CONNECTION_FILE;
+ }
+ else {
+ ExFreePool(tcpContext);
+ TCPTRACE((
+ "TdiOpenConnection failed, status %lx\n",
+ status
+ ));
+ }
+ }
+ else {
+ TCPTRACE((
+ "TCP: TdiOpenConnection issued on UDP device!\n"
+ ));
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ ExFreePool(tcpContext);
+ }
+
+ CTEAssert(status != TDI_PENDING);
+
+ return(status);
+ }
+
+ TCPTRACE(("TCPCreate: didn't find any useful ea's\n"));
+ status = STATUS_INVALID_EA_NAME;
+ ExFreePool(tcpContext);
+
+
+ CTEAssert(status != TDI_PENDING);
+
+ return(status);
+
+} // TCPCreate
+
+
+
+void
+TCPCloseObjectComplete(
+ void *Context,
+ unsigned int Status,
+ unsigned int UnUsed
+ )
+
+/*++
+
+Routine Description:
+
+ Completes a TdiCloseConnectoin or TdiCloseAddress request.
+
+Arguments:
+
+ Context - A pointer to the IRP for this request.
+ Status - The final status of the operation.
+ UnUsed - An unused parameter
+
+Return Value:
+
+ None.
+
+Notes:
+
+--*/
+
+{
+ KIRQL oldIrql;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PTCP_CONTEXT tcpContext;
+
+
+ UNREFERENCED_PARAMETER(UnUsed);
+
+ irp = (PIRP) Context;
+ irpSp = IoGetCurrentIrpStackLocation(irp);
+ tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext;
+ irp->IoStatus.Status = Status;
+
+ IF_TCPDBG(TCP_DEBUG_CLEANUP) {
+ TCPTRACE((
+ "TCPCloseObjectComplete on file object %lx\n",
+ irpSp->FileObject
+ ));
+ }
+
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ CTEAssert(tcpContext->ReferenceCount > 0);
+ CTEAssert(tcpContext->CancelIrps);
+
+ //
+ // Remove the initial reference that was put on by TCPCreate.
+ //
+ CTEAssert(tcpContext->ReferenceCount > 0);
+
+ if (--(tcpContext->ReferenceCount) == 0) {
+
+ IF_TCPDBG(TCP_DEBUG_CANCEL) {
+ CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList)));
+ CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList)));
+ }
+
+ KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE);
+ }
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE((
+ "TCPCloseObjectComplete: irp %lx fileobj %lx refcnt dec to %u\n",
+ irp,
+ irpSp,
+ tcpContext->ReferenceCount
+ ));
+ }
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ return;
+
+} // TCPCleanupComplete
+
+
+
+NTSTATUS
+TCPCleanup(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels all outstanding Irps on a TDI object by calling the close
+ routine for the object. It then waits for them to be completed
+ before returning.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+Notes:
+
+ This routine blocks, but does not pend.
+
+--*/
+
+{
+ KIRQL oldIrql;
+ PIRP cancelIrp = NULL;
+ PTCP_CONTEXT tcpContext;
+ NTSTATUS status;
+ TDI_REQUEST request;
+
+
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ tcpContext->CancelIrps = TRUE;
+ KeResetEvent(&(tcpContext->CleanupEvent));
+
+ IoReleaseCancelSpinLock(oldIrql);
+
+ //
+ // Now call the TDI close routine for this object to force all of its Irps
+ // to complete.
+ //
+ request.RequestNotifyObject = TCPCloseObjectComplete;
+ request.RequestContext = Irp;
+
+ switch ((int) IrpSp->FileObject->FsContext2) {
+
+ case TDI_TRANSPORT_ADDRESS_FILE:
+ IF_TCPDBG(TCP_DEBUG_CLOSE) {
+ TCPTRACE((
+ "TCPCleanup: Closing address object on file object %lx\n",
+ IrpSp->FileObject
+ ));
+ }
+ request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
+ status = TdiCloseAddress(&request);
+ break;
+
+ case TDI_CONNECTION_FILE:
+ IF_TCPDBG(TCP_DEBUG_CLOSE) {
+ TCPTRACE((
+ "TCPCleanup: Closing Connection object on file object %lx\n",
+ IrpSp->FileObject
+ ));
+ }
+ request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
+ status = TdiCloseConnection(&request);
+ break;
+
+ case TDI_CONTROL_CHANNEL_FILE:
+ IF_TCPDBG(TCP_DEBUG_CLOSE) {
+ TCPTRACE((
+ "TCPCleanup: Closing Control Channel object on file object %lx\n",
+ IrpSp->FileObject
+ ));
+ }
+ status = STATUS_SUCCESS;
+ break;
+
+ default:
+ //
+ // This should never happen.
+ //
+ CTEAssert(FALSE);
+
+ IoAcquireCancelSpinLock(&oldIrql);
+ tcpContext->CancelIrps = FALSE;
+ IoReleaseCancelSpinLock(oldIrql);
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ if (status != TDI_PENDING) {
+ TCPCloseObjectComplete(Irp, status, 0);
+ }
+
+ IF_TCPDBG(TCP_DEBUG_CLEANUP) {
+ TCPTRACE((
+ "TCPCleanup: waiting for completion of Irps on file object %lx\n",
+ IrpSp->FileObject
+ ));
+ }
+
+ status = KeWaitForSingleObject(
+ &(tcpContext->CleanupEvent),
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ CTEAssert(NT_SUCCESS(status));
+
+ IF_TCPDBG(TCP_DEBUG_CLEANUP) {
+ TCPTRACE((
+ "TCPCleanup: Wait on file object %lx finished\n",
+ IrpSp->FileObject
+ ));
+ }
+
+ //
+ // The cleanup Irp will be completed by the dispatch routine.
+ //
+
+ return(Irp->IoStatus.Status);
+
+} // TCPCleanup
+
+
+NTSTATUS
+TCPClose(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+ Dispatch routine for MJ_CLOSE IRPs. Performs final cleanup of the
+ open endpoint.
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+Notes:
+
+ This request does not pend.
+
+--*/
+
+{
+ PTCP_CONTEXT tcpContext;
+
+
+ tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
+
+#if DBG
+
+ IF_TCPDBG(TCP_DEBUG_CANCEL) {
+
+ KIRQL oldIrql;
+
+ IoAcquireCancelSpinLock(&oldIrql);
+
+ CTEAssert(tcpContext->ReferenceCount == 0);
+ CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList)));
+ CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList)));
+
+ IoReleaseCancelSpinLock(oldIrql);
+ }
+#endif // DBG
+
+ IF_TCPDBG(TCP_DEBUG_CLOSE) {
+ TCPTRACE(("TCPClose on file object %lx\n", IrpSp->FileObject));
+ }
+
+ ExFreePool(tcpContext);
+
+ return(STATUS_SUCCESS);
+
+} // TCPClose
+
+
+
+NTSTATUS
+TCPDispatchDeviceControl(
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp
+ )
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+ Irp - Pointer to I/O request packet
+ IrpSp - Pointer to the current stack location in the Irp.
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ NTSTATUS status;
+
+
+ PAGED_CODE();
+
+ //
+ // Set this in advance. Any IOCTL dispatch routine that cares about it
+ // will modify it itself.
+ //
+ Irp->IoStatus.Information = 0;
+
+ switch(IrpSp->Parameters.DeviceIoControl.IoControlCode) {
+
+ case IOCTL_TCP_QUERY_INFORMATION_EX:
+ return(TCPQueryInformationEx(Irp, IrpSp));
+ break;
+
+ case IOCTL_TCP_SET_INFORMATION_EX:
+ return(TCPSetInformationEx(Irp, IrpSp));
+ break;
+
+#ifdef SECFLTR
+
+ case IOCTL_TCP_QUERY_SECURITY_FILTER_STATUS:
+ case IOCTL_TCP_SET_SECURITY_FILTER_STATUS:
+ return(TCPControlSecurityFilter(Irp, IrpSp));
+ break;
+
+ case IOCTL_TCP_ADD_SECURITY_FILTER:
+ case IOCTL_TCP_DELETE_SECURITY_FILTER:
+ return(TCPProcessSecurityFilterRequest(Irp, IrpSp));
+ break;
+
+ case IOCTL_TCP_ENUMERATE_SECURITY_FILTER:
+ return(TCPEnumerateSecurityFilter(Irp, IrpSp));
+ break;
+
+#endif // SECFLTR
+
+ default:
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ Irp->IoStatus.Status = status;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return status;
+
+} // TCPDispatchDeviceControl
+
+
+
+NTSTATUS
+TCPDispatchInternalDeviceControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This is the dispatch routine for Internal Device Control IRPs.
+ This is the hot path for kernel-mode clients.
+
+Arguments:
+
+ DeviceObject - Pointer to device object for target device
+ Irp - Pointer to I/O request packet
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+
+
+ if (DeviceObject != IPDeviceObject) {
+
+ irpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ if (((int)irpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) {
+ //
+ // Send and receive are the performance path, so check for them
+ // right away.
+ //
+ if (irpSp->MinorFunction == TDI_SEND) {
+ return(TCPSendData(Irp, irpSp));
+ }
+
+ if (irpSp->MinorFunction == TDI_RECEIVE) {
+ return(TCPReceiveData(Irp, irpSp));
+ }
+
+ switch(irpSp->MinorFunction) {
+
+ case TDI_ASSOCIATE_ADDRESS:
+ status = TCPAssociateAddress(Irp, irpSp);
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(status);
+
+ case TDI_DISASSOCIATE_ADDRESS:
+ return(TCPDisassociateAddress(Irp, irpSp));
+
+ case TDI_CONNECT:
+ return(TCPConnect(Irp, irpSp));
+
+ case TDI_DISCONNECT:
+ return(TCPDisconnect(Irp, irpSp));
+
+ case TDI_LISTEN:
+ return(TCPListen(Irp, irpSp));
+
+ case TDI_ACCEPT:
+ return(TCPAccept(Irp, irpSp));
+
+ default:
+ break;
+ }
+
+ //
+ // Fall through.
+ //
+ }
+ else if ( ((int)irpSp->FileObject->FsContext2) ==
+ TDI_TRANSPORT_ADDRESS_FILE
+ )
+ {
+ if (irpSp->MinorFunction == TDI_SEND_DATAGRAM) {
+ return(UDPSendDatagram(Irp, irpSp));
+ }
+
+ if (irpSp->MinorFunction == TDI_RECEIVE_DATAGRAM) {
+ return(UDPReceiveDatagram(Irp, irpSp));
+ }
+
+ if (irpSp->MinorFunction == TDI_SET_EVENT_HANDLER) {
+ status = TCPSetEventHandler(Irp, irpSp);
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return(status);
+ }
+
+ //
+ // Fall through.
+ //
+ }
+
+ CTEAssert(
+ (((int)irpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE)
+ ||
+ (((int)irpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE)
+ ||
+ (((int)irpSp->FileObject->FsContext2) == TDI_CONTROL_CHANNEL_FILE)
+ );
+
+ //
+ // These functions are common to all endpoint types.
+ //
+ switch(irpSp->MinorFunction) {
+
+ case TDI_QUERY_INFORMATION:
+ return(TCPQueryInformation(Irp, irpSp));
+
+ case TDI_SET_INFORMATION:
+ case TDI_ACTION:
+ TCPTRACE((
+ "TCP: Call to unimplemented TDI function 0x%x\n",
+ irpSp->MinorFunction
+ ));
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ default:
+ TCPTRACE((
+ "TCP: call to invalid TDI function 0x%x\n",
+ irpSp->MinorFunction
+ ));
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ CTEAssert(status != TDI_PENDING);
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return status;
+ }
+
+ return(IPDispatch(DeviceObject, Irp));
+}
+
+
+
+NTSTATUS
+TCPDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This is the generic dispatch routine for TCP/UDP/RawIP.
+
+Arguments:
+
+ DeviceObject - Pointer to device object for target device
+ Irp - Pointer to I/O request packet
+
+Return Value:
+
+ NTSTATUS -- Indicates whether the request was successfully queued.
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+
+
+ if (DeviceObject != IPDeviceObject) {
+
+ irpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ CTEAssert(irpSp->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL);
+
+ switch (irpSp->MajorFunction) {
+
+ case IRP_MJ_CREATE:
+ status = TCPCreate(DeviceObject, Irp, irpSp);
+ break;
+
+ case IRP_MJ_CLEANUP:
+ status = TCPCleanup(DeviceObject, Irp, irpSp);
+ break;
+
+ case IRP_MJ_CLOSE:
+ status = TCPClose(Irp, irpSp);
+ break;
+
+ case IRP_MJ_DEVICE_CONTROL:
+ status = TdiMapUserRequest(DeviceObject, Irp, irpSp);
+
+ if (status == STATUS_SUCCESS) {
+ return(TCPDispatchInternalDeviceControl(DeviceObject, Irp));
+ }
+
+ return(TCPDispatchDeviceControl(
+ Irp,
+ IoGetCurrentIrpStackLocation(Irp)
+ ));
+ break;
+
+ case IRP_MJ_QUERY_SECURITY:
+ //
+ // This is generated on Raw endpoints. We don't do anything
+ // for it.
+ //
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+
+ case IRP_MJ_WRITE:
+ case IRP_MJ_READ:
+
+ default:
+ TCPTRACE((
+ "TCPDispatch: Irp %lx unsupported major function 0x%lx\n",
+ irpSp,
+ irpSp->MajorFunction
+ ));
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ CTEAssert(status != TDI_PENDING);
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
+
+ return status;
+
+ }
+
+ return(IPDispatch(DeviceObject, Irp));
+
+
+} // TCPDispatch
+
+
+
+//
+// Private utility functions
+//
+FILE_FULL_EA_INFORMATION UNALIGNED *
+FindEA(
+ PFILE_FULL_EA_INFORMATION StartEA,
+ CHAR *TargetName,
+ USHORT TargetNameLength
+ )
+
+/*++
+
+Routine Description:
+
+ Parses and extended attribute list for a given target attribute.
+
+Arguments:
+
+ StartEA - the first extended attribute in the list.
+ TargetName - the name of the target attribute.
+ TargetNameLength - the length of the name of the target attribute.
+
+Return Value:
+
+ A pointer to the requested attribute or NULL if the target wasn't found.
+
+--*/
+
+{
+ USHORT i;
+ BOOLEAN found;
+ FILE_FULL_EA_INFORMATION UNALIGNED *CurrentEA;
+
+
+ PAGED_CODE();
+
+ do {
+ found = TRUE;
+
+ CurrentEA = StartEA;
+ StartEA += CurrentEA->NextEntryOffset;
+
+ if (CurrentEA->EaNameLength != TargetNameLength) {
+ continue;
+ }
+
+ for (i=0; i < CurrentEA->EaNameLength; i++) {
+ if (CurrentEA->EaName[i] == TargetName[i]) {
+ continue;
+ }
+ found = FALSE;
+ break;
+ }
+
+ if (found) {
+ return(CurrentEA);
+ }
+
+ } while(CurrentEA->NextEntryOffset != 0);
+
+ return(NULL);
+}
+
+
+
+BOOLEAN
+IsDHCPZeroAddress(
+ TRANSPORT_ADDRESS UNALIGNED *AddrList
+ )
+
+/*++
+
+Routine Description:
+
+ Checks a TDI IP address list for an address from DHCP binding
+ to the IP address zero. Normally, binding to zero means wildcard.
+ For DHCP, it really means bind to an interface with an address of
+ zero. This semantic is flagged by a special value in an unused
+ portion of the address structure (ie. this is a kludge).
+
+Arguments:
+
+ AddrList - The TDI transport address list passed in the create IRP.
+
+Return Value:
+
+ TRUE if the first IP address found had the flag set. FALSE otherwise.
+
+--*/
+
+{
+ int i; // Index variable.
+ TA_ADDRESS UNALIGNED *CurrentAddr; // Address we're examining and may use.
+
+
+ // First, verify that someplace in Address is an address we can use.
+ CurrentAddr = (TA_ADDRESS UNALIGNED *)AddrList->Address;
+
+ for (i = 0; i < AddrList->TAAddressCount; i++) {
+ if (CurrentAddr->AddressType == TDI_ADDRESS_TYPE_IP) {
+ if (CurrentAddr->AddressLength == TDI_ADDRESS_LENGTH_IP) {
+ TDI_ADDRESS_IP UNALIGNED *ValidAddr;
+
+ ValidAddr = (TDI_ADDRESS_IP UNALIGNED *)CurrentAddr->Address;
+
+ if (*((ULONG UNALIGNED *) ValidAddr->sin_zero) == 0x12345678) {
+ return TRUE;
+ }
+
+ } else {
+ return FALSE; // Wrong length for address.
+ }
+ } else {
+ CurrentAddr = (TA_ADDRESS UNALIGNED *)
+ (CurrentAddr->Address + CurrentAddr->AddressLength);
+ }
+ }
+
+ return FALSE; // Didn't find a match.
+}
+
+
+
+ULONG
+TCPGetMdlChainByteCount(
+ PMDL Mdl
+ )
+
+/*++
+
+Routine Description:
+
+ Sums the byte counts of each MDL in a chain.
+
+Arguments:
+
+ Mdl - Pointer to the MDL chain to sum.
+
+Return Value:
+
+ The byte count of the MDL chain.
+
+--*/
+
+{
+ ULONG count = 0;
+
+ while (Mdl != NULL) {
+ count += MmGetMdlByteCount(Mdl);
+ Mdl = Mdl->Next;
+ }
+
+ return(count);
+}
+
+
+
+
+ULONG
+RawExtractProtocolNumber(
+ IN PUNICODE_STRING FileName
+ )
+
+/*++
+
+Routine Description:
+
+ Extracts the protocol number from the file object name.
+
+Arguments:
+
+ FileName - The unicode file name.
+
+Return Value:
+
+ The protocol number or 0xFFFFFFFF on error.
+
+--*/
+
+{
+ PWSTR name;
+ UNICODE_STRING unicodeString;
+ USHORT length;
+ ULONG protocol;
+ NTSTATUS status;
+
+
+ PAGED_CODE();
+
+ name = FileName->Buffer;
+
+ if ( FileName->Length <
+ (sizeof(OBJ_NAME_PATH_SEPARATOR) + sizeof(WCHAR))
+ )
+ {
+ return(0xFFFFFFFF);
+ }
+
+ //
+ // Step over separator
+ //
+ if (*name++ != OBJ_NAME_PATH_SEPARATOR) {
+ return(0xFFFFFFFF);
+ }
+
+ if (*name == UNICODE_NULL) {
+ return(0xFFFFFFFF);
+ }
+
+ //
+ // Convert the remaining name into a number.
+ //
+ RtlInitUnicodeString(&unicodeString, name);
+
+ status = RtlUnicodeStringToInteger(
+ &unicodeString,
+ 10,
+ &protocol
+ );
+
+ if (!NT_SUCCESS(status)) {
+ return(0xFFFFFFFF);
+ }
+
+ if (protocol > 255) {
+ return(0xFFFFFFFF);
+ }
+
+ return(protocol);
+
+}
+
diff --git a/private/ntos/tdi/tcpip/tcp/ntinit.c b/private/ntos/tdi/tcpip/tcp/ntinit.c
new file mode 100644
index 000000000..e1fdbd7f7
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/ntinit.c
@@ -0,0 +1,1038 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ntinit.c
+
+Abstract:
+
+ NT specific routines for loading and configuring the TCP/UDP driver.
+
+Author:
+
+ Mike Massa (mikemas) Aug 13, 1993
+
+Revision History:
+
+ Who When What
+ -------- -------- ----------------------------------------------
+ mikemas 08-13-93 created
+
+Notes:
+
+--*/
+
+#include <oscfg.h>
+#include <ntddip.h>
+#include <ndis.h>
+#include <cxport.h>
+#include <tdi.h>
+#include <tdikrnl.h>
+#include <tdint.h>
+#include <tdistat.h>
+#include <tdiinfo.h>
+#include <ip.h>
+
+#include "queue.h"
+#include "addr.h"
+#include "tcp.h"
+#include "tcb.h"
+#include "udp.h"
+#include "raw.h"
+#include "tcpconn.h"
+#include "tcpcfg.h"
+#include <ntddtcp.h>
+#include "secfltr.h"
+
+//
+// Global variables.
+//
+PDRIVER_OBJECT TCPDriverObject = NULL;
+PDEVICE_OBJECT TCPDeviceObject = NULL;
+PDEVICE_OBJECT UDPDeviceObject = NULL;
+PDEVICE_OBJECT RawIPDeviceObject = NULL;
+extern PDEVICE_OBJECT IPDeviceObject;
+
+//
+//Place holder for Maximum duplicate acks we would like
+//to see before we do fast retransmit
+//
+#if FAST_RETRANSMIT
+uint MaxDupAcks;
+#endif
+
+
+#ifdef _PNP_POWER
+
+HANDLE TCPRegistrationHandle;
+HANDLE UDPRegistrationHandle;
+HANDLE IPRegistrationHandle;
+
+#endif
+
+#ifdef SYN_ATTACK
+BOOLEAN SynAttackProtect; //if TRUE, syn-att protection checks
+ // are made
+uint TCPPortsExhausted; //# of ports exhausted
+uint TCPMaxPortsExhausted; //Max # of ports that must be exhausted
+ // for syn-att protection to kick in
+uint TCPMaxPortsExhaustedLW; //Low-watermark of # of ports exhausted
+ //When reached, we revert to normal
+ // count for syn-ack retries
+uint TCPMaxHalfOpen; //Max # of half-open connections allowed
+ // before we dec. the syn-ack retries
+uint TCPMaxHalfOpenRetried; //Max # of half-open conn. that have
+ // been retried at least 1 time
+uint TCPMaxHalfOpenRetriedLW; //Low-watermark of the above. When
+ // go down to it, we revert to normal
+ // # of retries for syn-acks
+uint TCPHalfOpen; //# of half-open connections
+uint TCPHalfOpenRetried; //# of half-open conn. that have been
+ //retried at least once
+#endif
+//
+// External function prototypes
+//
+
+int
+tlinit(
+ void
+ );
+
+NTSTATUS
+TCPDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+TCPDispatchInternalDeviceControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+IPDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+IPDriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ );
+
+NTSTATUS
+GetRegMultiSZValue(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PUNICODE_STRING ValueData
+ );
+
+PWCHAR
+EnumRegMultiSz(
+ IN PWCHAR MszString,
+ IN ULONG MszStringLength,
+ IN ULONG StringIndex
+ );
+
+//
+// Local funcion prototypes
+//
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ );
+
+void *
+TLRegisterProtocol(
+ uchar Protocol,
+ void *RcvHandler,
+ void *XmitHandler,
+ void *StatusHandler,
+ void *RcvCmpltHandler
+ );
+
+IP_STATUS
+TLGetIPInfo(
+ IPInfo *Buffer,
+ int Size
+ );
+
+uchar
+TCPGetConfigInfo(
+ void
+ );
+
+NTSTATUS
+TCPInitializeParameter(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PULONG Value
+ );
+
+#ifdef SECFLTR
+
+uint
+EnumSecurityFilterValue(
+ PNDIS_STRING FilterList,
+ ulong Index,
+ ulong *FilterValue
+ );
+
+#endif // SECFLTR
+
+#ifdef RASAUTODIAL
+VOID
+TCPAcdBind();
+#endif // RASAUTODIAL
+
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(INIT, TLRegisterProtocol)
+#pragma alloc_text(INIT, TLGetIPInfo)
+#pragma alloc_text(INIT, TCPGetConfigInfo)
+#pragma alloc_text(INIT, TCPInitializeParameter)
+
+#ifdef SECFLTR
+#pragma alloc_text(PAGE, EnumSecurityFilterValue)
+#endif // SECFLTR
+
+#ifdef RASAUTODIAL
+#pragma alloc_text(INIT, TCPAcdBind)
+#endif // RASAUTODIAL
+
+#endif // ALLOC_PRAGMA
+
+
+//
+// Function definitions
+//
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+
+/*++
+
+Routine Description:
+
+ Initialization routine for the TCP/UDP driver.
+
+Arguments:
+
+ DriverObject - Pointer to the TCP driver object created by the system.
+ DeviceDescription - The name of TCP's node in the registry.
+
+Return Value:
+
+ The final status from the initialization operation.
+
+--*/
+
+{
+ NTSTATUS status;
+ UNICODE_STRING deviceName;
+ USHORT i;
+ int initStatus;
+
+
+#ifdef _PNP_POWER
+ TdiInitialize();
+#endif
+
+#ifdef SECFLTR
+ //
+ // IP calls the security filter code, so initialize it first.
+ //
+ InitializeSecurityFilters();
+
+#endif // SECFLTR
+
+ //
+ // Initialize IP
+ //
+ status = IPDriverEntry(DriverObject, RegistryPath);
+
+ if (!NT_SUCCESS(status)) {
+ TCPTRACE(("Tcpip: IP initialization failed, status %lx\n", status));
+ return(status);
+ }
+
+ //
+ // Initialize TCP, UDP, and RawIP
+ //
+ TCPDriverObject = DriverObject;
+
+ //
+ // Create the device objects. IoCreateDevice zeroes the memory
+ // occupied by the object.
+ //
+
+ RtlInitUnicodeString(&deviceName, DD_TCP_DEVICE_NAME);
+
+ status = IoCreateDevice(
+ DriverObject,
+ 0,
+ &deviceName,
+ FILE_DEVICE_NETWORK,
+ 0,
+ FALSE,
+ &TCPDeviceObject
+ );
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ DriverObject,
+ EVENT_TCPIP_CREATE_DEVICE_FAILED,
+ 1,
+ 1,
+ &deviceName.Buffer,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "TCP: Failed to create TCP device object, status %lx\n",
+ status
+ ));
+ goto init_failed;
+ }
+
+ RtlInitUnicodeString(&deviceName, DD_UDP_DEVICE_NAME);
+
+ status = IoCreateDevice(
+ DriverObject,
+ 0,
+ &deviceName,
+ FILE_DEVICE_NETWORK,
+ 0,
+ FALSE,
+ &UDPDeviceObject
+ );
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ DriverObject,
+ EVENT_TCPIP_CREATE_DEVICE_FAILED,
+ 1,
+ 1,
+ &deviceName.Buffer,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "TCP: Failed to create UDP device object, status %lx\n",
+ status
+ ));
+ goto init_failed;
+ }
+
+ RtlInitUnicodeString(&deviceName, DD_RAW_IP_DEVICE_NAME);
+
+ status = IoCreateDevice(
+ DriverObject,
+ 0,
+ &deviceName,
+ FILE_DEVICE_NETWORK,
+ 0,
+ FALSE,
+ &RawIPDeviceObject
+ );
+
+ if (!NT_SUCCESS(status)) {
+ CTELogEvent(
+ DriverObject,
+ EVENT_TCPIP_CREATE_DEVICE_FAILED,
+ 1,
+ 1,
+ &deviceName.Buffer,
+ 0,
+ NULL
+ );
+
+ TCPTRACE((
+ "TCP: Failed to create Raw IP device object, status %lx\n",
+ status
+ ));
+ goto init_failed;
+ }
+
+ //
+ // Initialize the driver object
+ //
+ DriverObject->DriverUnload = NULL;
+ DriverObject->FastIoDispatch = NULL;
+ for (i=0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
+ DriverObject->MajorFunction[i] = TCPDispatch;
+ }
+
+ //
+ // We special case Internal Device Controls because they are the
+ // hot path for kernel-mode clients.
+ //
+ DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
+ TCPDispatchInternalDeviceControl;
+
+ //
+ // Intialize the device objects.
+ //
+ TCPDeviceObject->Flags |= DO_DIRECT_IO;
+ UDPDeviceObject->Flags |= DO_DIRECT_IO;
+ RawIPDeviceObject->Flags |= DO_DIRECT_IO;
+
+ //
+ // Finally, initialize the stack.
+ //
+ initStatus = tlinit();
+
+ if (initStatus == TRUE) {
+#ifdef RASAUTODIAL
+ //
+ // Get the automatic connection driver
+ // entry points.
+ //
+ TCPAcdBind();
+#endif // RASAUTODIAL
+
+
+#ifdef _PNP_POWER
+
+ RtlInitUnicodeString(&deviceName, DD_TCP_DEVICE_NAME);
+ (void)TdiRegisterDeviceObject(&deviceName,&TCPRegistrationHandle);
+
+ RtlInitUnicodeString(&deviceName, DD_UDP_DEVICE_NAME);
+ (void)TdiRegisterDeviceObject(&deviceName,&UDPRegistrationHandle);
+
+ RtlInitUnicodeString(&deviceName, DD_RAW_IP_DEVICE_NAME);
+ (void)TdiRegisterDeviceObject(&deviceName,&IPRegistrationHandle);
+
+#endif
+ return(STATUS_SUCCESS);
+ }
+
+ TCPTRACE((
+ "Tcpip: TCP/UDP initialization failed, but IP will be available.\n"
+ ));
+
+ CTELogEvent(
+ DriverObject,
+ EVENT_TCPIP_TCP_INIT_FAILED,
+ 1,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+ status = STATUS_UNSUCCESSFUL;
+
+
+init_failed:
+
+ //
+ // IP has successfully started, but TCP & UDP failed. Set the
+ // Dispatch routine to point to IP only, since the TCP and UDP
+ // devices don't exist.
+ //
+
+ if (TCPDeviceObject != NULL) {
+ IoDeleteDevice(TCPDeviceObject);
+ }
+
+ if (UDPDeviceObject != NULL) {
+ IoDeleteDevice(UDPDeviceObject);
+ }
+
+ if (RawIPDeviceObject != NULL) {
+ IoDeleteDevice(RawIPDeviceObject);
+ }
+
+ for (i=0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
+ DriverObject->MajorFunction[i] = IPDispatch;
+ }
+
+ return(STATUS_SUCCESS);
+}
+
+
+IP_STATUS
+TLGetIPInfo(
+ IPInfo *Buffer,
+ int Size
+ )
+
+/*++
+
+Routine Description:
+
+ Returns information necessary for TCP to call into IP.
+
+Arguments:
+
+ Buffer - A pointer to the IP information structure.
+
+ Size - The size of Buffer.
+
+Return Value:
+
+ The IP status of the operation.
+
+--*/
+
+{
+ return(IPGetInfo(Buffer, Size));
+}
+
+
+void *
+TLRegisterProtocol(
+ uchar Protocol,
+ void *RcvHandler,
+ void *XmitHandler,
+ void *StatusHandler,
+ void *RcvCmpltHandler
+ )
+
+/*++
+
+Routine Description:
+
+ Calls the IP driver's protocol registration function.
+
+Arguments:
+
+ Protocol - The protocol number to register.
+
+ RcvHandler - Transport's packet receive handler.
+
+ XmitHandler - Transport's packet transmit complete handler.
+
+ StatusHandler - Transport's status update handler.
+
+ RcvCmpltHandler - Transport's receive complete handler
+
+Return Value:
+
+ A context value for the protocol to pass to IP when transmitting.
+
+--*/
+
+{
+ return(IPRegisterProtocol(
+ Protocol,
+ RcvHandler,
+ XmitHandler,
+ StatusHandler,
+ RcvCmpltHandler
+ )
+ );
+}
+
+
+//
+// Interval in milliseconds between keepalive transmissions until a
+// response is received.
+//
+#define DEFAULT_KEEPALIVE_INTERVAL 1000
+
+//
+// time to first keepalive transmission. 2 hours == 7,200,000 milliseconds
+//
+#define DEFAULT_KEEPALIVE_TIME 7200000
+
+#ifdef SYN_ATTACK
+#define MIN_THRESHOLD_MAX_HO 100
+#define MIN_THRESHOLD_MAX_HO_RETRIED 80
+#endif
+
+uchar
+TCPGetConfigInfo(
+ void
+ )
+
+/*++
+
+Routine Description:
+
+ Initializes TCP global configuration parameters.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Zero on failure, nonzero on success.
+
+--*/
+
+{
+ HANDLE keyHandle;
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objectAttributes;
+ UNICODE_STRING UKeyName;
+ ULONG maxConnectRexmits = 0;
+ ULONG maxConnectResponseRexmits = 0;
+ ULONG maxDataRexmits = 0;
+ ULONG pptpmaxDataRexmits = 0;
+ ULONG useRFC1122UrgentPointer = 0;
+ BOOLEAN AsSystem;
+
+
+ //
+ // Initialize to the defaults in case an error occurs somewhere.
+ //
+ KAInterval = DEFAULT_KEEPALIVE_INTERVAL;
+ KeepAliveTime = DEFAULT_KEEPALIVE_TIME;
+ PMTUDiscovery = TRUE;
+ PMTUBHDetect = FALSE;
+ DeadGWDetect = TRUE;
+ DefaultRcvWin = 0; // Automagically pick a reasonable one.
+ MaxConnections = DEFAULT_MAX_CONNECTIONS;
+ maxConnectRexmits = MAX_CONNECT_REXMIT_CNT;
+ maxConnectResponseRexmits = MAX_CONNECT_RESPONSE_REXMIT_CNT;
+ pptpmaxDataRexmits = maxDataRexmits = MAX_REXMIT_CNT;
+ BSDUrgent = TRUE;
+ FinWait2TO = FIN_WAIT2_TO;
+ NTWMaxConnectCount = NTW_MAX_CONNECT_COUNT;
+ NTWMaxConnectTime = NTW_MAX_CONNECT_TIME;
+ MaxUserPort = MAX_USER_PORT;
+
+
+#if FAST_RETRANSMIT
+// Default number of duplicate acks
+ MaxDupAcks = 2;
+#endif
+
+
+#ifdef SYN_ATTACK
+ SynAttackProtect = FALSE; //by default it is always off
+ if (MmIsThisAnNtAsSystem()) {
+ TCPMaxPortsExhausted = 5;
+ TCPMaxHalfOpen = 100;
+ TCPMaxHalfOpenRetried = 80;
+ }
+ else {
+ TCPMaxPortsExhausted = 5;
+ TCPMaxHalfOpen = 500;
+ TCPMaxHalfOpenRetried = 400;
+ }
+#endif
+
+#ifdef SECFLTR
+ SecurityFilteringEnabled = FALSE;
+#endif // SECFLTR
+
+
+ //
+ // Read the TCP optional (hidden) registry parameters.
+ //
+ RtlInitUnicodeString(
+ &UKeyName,
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters"
+ );
+
+ memset(&objectAttributes, 0, sizeof(OBJECT_ATTRIBUTES));
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &UKeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = ZwOpenKey(
+ &keyHandle,
+ KEY_READ,
+ &objectAttributes
+ );
+
+ if (NT_SUCCESS(status)) {
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"KeepAliveInterval",
+ &KAInterval
+ );
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"KeepAliveTime",
+ &KeepAliveTime
+ );
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"EnablePMTUBHDetect",
+ &PMTUBHDetect
+ );
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"TcpWindowSize",
+ &DefaultRcvWin
+ );
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"TcpNumConnections",
+ &MaxConnections
+ );
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"TcpMaxConnectRetransmissions",
+ &maxConnectRexmits
+ );
+
+ if (maxConnectRexmits > 255) {
+ maxConnectRexmits = 255;
+ }
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"TcpMaxConnectResponseRetransmissions",
+ &maxConnectResponseRexmits
+ );
+
+ if (maxConnectResponseRexmits > 255) {
+ maxConnectResponseRexmits = 255;
+ }
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"TcpMaxDataRetransmissions",
+ &maxDataRexmits
+ );
+
+ if (maxDataRexmits > 255) {
+ maxDataRexmits = 255;
+ }
+
+
+#if FAST_RETRANSMIT
+ // Limit the MaxDupAcks to 3
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"TcpMaxDupAcks",
+ &MaxDupAcks
+ );
+
+ if (MaxDupAcks > 3) {
+ MaxDupAcks = 3;
+ }
+ if (MaxDupAcks < 0) {
+ MaxDupAcks = 1;
+ }
+#endif
+
+
+
+#ifdef SYN_ATTACK
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"SynAttackProtect",
+ (unsigned long *)&SynAttackProtect
+ );
+
+
+ if (SynAttackProtect) {
+
+ //
+ // We don't want syn-attack protection to kick in if the user
+ // has set the MaxConnectResponseRetransmissions to lower than
+ // a certain threshold.
+ //
+ if (maxConnectResponseRexmits >= MAX_CONNECT_RESPONSE_REXMIT_CNT){
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"TCPMaxPortsExhausted",
+ &TCPMaxPortsExhausted
+ );
+
+ TCPMaxPortsExhaustedLW = MAX((TCPMaxPortsExhausted >> 1) + (TCPMaxPortsExhausted >> 2), 1);
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"TCPMaxHalfOpen",
+ &TCPMaxHalfOpen
+ );
+
+ if (TCPMaxHalfOpen < MIN_THRESHOLD_MAX_HO) {
+ TCPMaxHalfOpen = MIN_THRESHOLD_MAX_HO;
+ }
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"TCPMaxHalfOpenRetried",
+ &TCPMaxHalfOpenRetried
+ );
+
+ if (
+ (TCPMaxHalfOpenRetried > TCPMaxHalfOpen) ||
+ (TCPMaxHalfOpenRetried < MIN_THRESHOLD_MAX_HO_RETRIED)
+ )
+
+ {
+ TCPMaxHalfOpenRetried = MIN_THRESHOLD_MAX_HO_RETRIED;
+ }
+
+ TCPMaxHalfOpenRetriedLW = (TCPMaxHalfOpenRetried >> 1) +
+ (TCPMaxHalfOpenRetried >> 2);
+ }
+ else {
+ SynAttackProtect = FALSE;
+ }
+
+ }
+#endif
+
+ //
+ // If we fail, then set to same value as maxDataRexmit so that the
+ // max(pptpmaxDataRexmit,maxDataRexmit) is a decent value
+ // Need this since TCPInitializeParameter no longer "initializes"
+ // to a default value
+ //
+
+ if(TCPInitializeParameter(keyHandle,
+ L"PPTPTcpMaxDataRetransmissions",
+ &pptpmaxDataRexmits) != STATUS_SUCCESS)
+ {
+ pptpmaxDataRexmits = maxDataRexmits;
+ }
+
+ if (pptpmaxDataRexmits > 255) {
+ pptpmaxDataRexmits = 255;
+ }
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"TcpUseRFC1122UrgentPointer",
+ &useRFC1122UrgentPointer
+ );
+
+ if (useRFC1122UrgentPointer) {
+ BSDUrgent = FALSE;
+ }
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"TcpTimedWaitDelay",
+ &FinWait2TO
+ );
+
+ if (FinWait2TO < 30) {
+ FinWait2TO = 30;
+ }
+ if (FinWait2TO > 300) {
+ FinWait2TO = 300;
+ }
+ FinWait2TO = MS_TO_TICKS(FinWait2TO*1000);
+
+ NTWMaxConnectTime = MS_TO_TICKS(NTWMaxConnectTime*1000);
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"MaxUserPort",
+ &MaxUserPort
+ );
+
+ if (MaxUserPort < 5000) {
+ MaxUserPort = 5000;
+ }
+ if (MaxUserPort > 65534) {
+ MaxUserPort = 65534;
+ }
+
+ //
+ // Read a few IP optional (hidden) registry parameters that TCP
+ // cares about.
+ //
+ TCPInitializeParameter(
+ keyHandle,
+ L"EnablePMTUDiscovery",
+ &PMTUDiscovery
+ );
+
+ TCPInitializeParameter(
+ keyHandle,
+ L"EnableDeadGWDetect",
+ &DeadGWDetect
+ );
+
+#ifdef SECFLTR
+ TCPInitializeParameter(
+ keyHandle,
+ L"EnableSecurityFilters",
+ &SecurityFilteringEnabled
+ );
+#endif // SECFLTR
+
+ ZwClose(keyHandle);
+ }
+
+ MaxConnectRexmitCount = maxConnectRexmits;
+ MaxConnectResponseRexmitCount = maxConnectResponseRexmits;
+#ifdef SYN_ATTACK
+ MaxConnectResponseRexmitCountTmp = MaxConnectResponseRexmitCount;
+#endif
+
+ //
+ // Use the greater of the two, hence both values should be valid
+ //
+
+ MaxDataRexmitCount = (maxDataRexmits > pptpmaxDataRexmits ? maxDataRexmits : pptpmaxDataRexmits) ;
+
+ return(1);
+}
+
+
+#define WORK_BUFFER_SIZE 256
+
+NTSTATUS
+TCPInitializeParameter(
+ HANDLE KeyHandle,
+ PWCHAR ValueName,
+ PULONG Value
+ )
+
+/*++
+
+Routine Description:
+
+ Initializes a ULONG parameter from the registry or to a default
+ parameter if accessing the registry value fails.
+
+Arguments:
+
+ KeyHandle - An open handle to the registry key for the parameter.
+ ValueName - The UNICODE name of the registry value to read.
+ Value - The ULONG into which to put the data.
+ DefaultValue - The default to assign if reading the registry fails.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS status;
+ ULONG resultLength;
+ PKEY_VALUE_FULL_INFORMATION keyValueFullInformation;
+ UCHAR keybuf[WORK_BUFFER_SIZE];
+ UNICODE_STRING UValueName;
+
+
+ RtlInitUnicodeString(&UValueName, ValueName);
+
+ keyValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)keybuf;
+ RtlZeroMemory(keyValueFullInformation, sizeof(keyValueFullInformation));
+
+ status = ZwQueryValueKey(
+ KeyHandle,
+ &UValueName,
+ KeyValueFullInformation,
+ keyValueFullInformation,
+ WORK_BUFFER_SIZE,
+ &resultLength
+ );
+
+ if (status == STATUS_SUCCESS) {
+ if (keyValueFullInformation->Type == REG_DWORD) {
+ *Value = *((ULONG UNALIGNED *) ((PCHAR)keyValueFullInformation +
+ keyValueFullInformation->DataOffset));
+ }
+ }
+
+ return(status);
+}
+
+
+#ifdef SECFLTR
+
+TDI_STATUS
+GetSecurityFilterList(
+ NDIS_HANDLE ConfigHandle,
+ ulong Protocol,
+ PNDIS_STRING FilterList
+ )
+{
+ PWCHAR parameterName;
+ TDI_STATUS status;
+
+
+ if (Protocol == PROTOCOL_TCP) {
+ parameterName = L"TcpAllowedPorts";
+ }
+ else if (Protocol == PROTOCOL_UDP) {
+ parameterName = L"UdpAllowedPorts";
+ }
+ else {
+ parameterName = L"RawIpAllowedProtocols";
+ }
+
+ status = GetRegMultiSZValue(
+ ConfigHandle,
+ parameterName,
+ FilterList
+ );
+
+ if (!NT_SUCCESS(status)) {
+ FilterList->Length = 0;
+ }
+
+ return(status);
+}
+
+
+uint
+EnumSecurityFilterValue(
+ PNDIS_STRING FilterList,
+ ulong Index,
+ ulong *FilterValue
+ )
+{
+ PWCHAR valueString;
+ UNICODE_STRING unicodeString;
+ NTSTATUS status;
+
+
+ PAGED_CODE();
+
+
+ valueString = EnumRegMultiSz(
+ FilterList->Buffer,
+ FilterList->Length,
+ Index
+ );
+
+ if ((valueString == NULL) || (valueString[0] == UNICODE_NULL)) {
+ return(FALSE);
+ }
+
+ RtlInitUnicodeString(&unicodeString, valueString);
+
+ status = RtlUnicodeStringToInteger(&unicodeString, 0, FilterValue);
+
+ if (!(NT_SUCCESS(status))) {
+ TCPTRACE(("TCP: Invalid filter value %ws\n", valueString));
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+#endif // SECFLTR
diff --git a/private/ntos/tdi/tcpip/tcp/ppc/sources b/private/ntos/tdi/tcpip/tcp/ppc/sources
new file mode 100644
index 000000000..3fd3f6b3b
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/ppc/sources
@@ -0,0 +1,4 @@
+PPC_SOURCES= \
+ ..\ppc\xsum.s
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/ppc/xsum.s b/private/ntos/tdi/tcpip/tcp/ppc/xsum.s
new file mode 100644
index 000000000..831ef6b53
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/ppc/xsum.s
@@ -0,0 +1,257 @@
+// TITLE("Compute Checksum")
+//++
+//
+// Copyright (c) 1994 IBM Corporation
+//
+// Module Name:
+//
+// xsum.s
+//
+// Abstract:
+//
+// This module implement a function to compute the checksum of a buffer.
+//
+// Author:
+//
+// David N. Cutler (davec) 27-Jan-1992
+//
+// Environment:
+//
+// User mode.
+//
+// Revision History:
+//
+// Michael W. Thomas 02/14/94 Converted from MIPS
+// Peter L. Johnston 07/19/94 Updated for Daytona Lvl 734 and
+// optimized for PowerPC.
+//
+//--
+
+#include "ksppc.h"
+
+ SBTTL("Compute Checksum")
+//++
+//
+// ULONG
+// tcpxsum (
+// IN ULONG Checksum,
+// IN PUCHAR Source,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function computes the checksum of the specified buffer.
+//
+// N.B. The checksum is the 16 bit checksum of the 16 bit aligned
+// buffer. If the buffer is not 16 bit aligned the first byte is
+// moved to high order position to be added to the correct half.
+//
+// Arguments:
+//
+// Checksum (r3) - Supplies the initial checksum value.
+//
+// Source (r4) - Supplies a pointer to the checksum buffer.
+//
+// Length (r5) - Supplies the length of the buffer in bytes.
+//
+// Return Value:
+//
+// The computed checksum is returned as the function value.
+//
+//--
+
+ LEAF_ENTRY(tcpxsum)
+
+ cmpwi r.5, 0 // check if bytes to checksum
+ mtcrf 0x01, r.4 // set up for alignment check
+ li r.6, 0 // initialize partial checksum
+ beqlr- // return if no bytes to checksum
+
+ andi. r.7, r.5, 1 // check if length is even
+ crmove 7, 31 // remember original alignment
+ bf 31, evenalign // jif 16 bit aligned
+
+//
+// Initialize the checksum to the first byte shifted up a byte.
+//
+ lbz r.6, 0(r.4) // get first byte of buffer
+ subi r.5, r.5, 1 // reduce count of bytes to checksum
+ cmpwi cr.6, r.5, 0 // check if done
+ crnot eq, eq // invert odd/even length check
+ addi r.4, r.4, 1 // advance buffer address
+ mtcrf 0x01, r.4 // reset 32 bit alignment check
+ slwi r.6, r.6, 8 // shift byte up in computed checksum
+ // max current checksum is 0x0ff00
+ beq cr.6, combine // jif no more bytes to checksum
+
+evenalign:
+
+//
+// Check if the length of the buffer is an even number of bytes.
+//
+// If the buffer is not an even number of bytes, add the last byte to the
+// computed checksum.
+//
+
+ beq evenlength
+ subic. r.5, r.5, 1 // reduce count of bytes to checksum
+ lbzx r.7, r.4, r.5 // get last byte from buffer
+ add r.6, r.6, r.7 // add last byte to computed checksum
+ // max current checksum is 0x0ffff
+ beq combine // jif no more bytes in buffer
+
+evenlength:
+
+//
+// Check if we are 4 byte aligned, if not add first 2 byte word into
+// checksum so the buffer is then 4 byte aligned.
+//
+
+ bf 30, fourbytealigned // jif 4 byte aligned
+
+ lhz r.7, 0(r.4) // get 2 byte word
+ subic. r.5, r.5, 2 // reduce length
+ addi r.4, r.4, 2 // bump address
+ add r.6, r.6, r.7 // add 2 bytes to computed checksum
+ // max current checksum is 0x1fffe
+ beq combine // jif no more bytes to checksum
+
+//
+// Attempt to sum the remainder of the buffer in sets of 32 bytes. This
+// should achieve 2 bytes per clock on 601 and 603, and 3.2 bytes per clock
+// on 604. (A seperate implementation will be required to take advantage
+// of 64 bit loads on the 620).
+//
+
+fourbytealigned:
+
+ srwi. r.7, r.5, 5 // get count of 32 byte sets
+ mtcrf 0x03, r.5 // break length into block for
+ // various run lengths.
+ subi r.4, r.4, 4 // adjust buffer address for lwzu
+ mtctr r.7
+ addic r.6, r.6, 0 // clear carry bit
+ beq try16 // jif no 32 byte sets
+
+do32: lwz r.8, 4(r.4) // get 1st 4 bytes in set
+ lwz r.9, 8(r.4) // get 2nd 4
+ adde r.6, r.6, r.8 // add 1st 4 to checksum
+ lwz r.10, 12(r.4) // get 3rd 4
+ adde r.6, r.6, r.9 // add 2nd 4
+ lwz r.11, 16(r.4) // get 4th 4
+ adde r.6, r.6, r.10 // add 3rd 4
+ lwz r.8, 20(r.4) // get 5th 4
+ adde r.6, r.6, r.11 // add 4th 4
+ lwz r.9, 24(r.4) // get 6th 4
+ adde r.6, r.6, r.8 // add 5th 4
+ lwz r.10, 28(r.4) // get 7th 4
+ adde r.6, r.6, r.9 // add 6th 4
+ lwzu r.11, 32(r.4) // get 8th 4 and update address
+ adde r.6, r.6, r.10 // add 7th 4
+ adde r.6, r.6, r.11 // add 8th 4
+ bdnz do32
+
+try16: bf 27, try8 // jif no 16 byte block
+
+ lwz r.8, 4(r.4) // get 1st 4
+ lwz r.9, 8(r.4) // get 2nd 4
+ adde r.6, r.6, r.8 // add 1st 4
+ lwz r.10, 12(r.4) // get 3rd 4
+ adde r.6, r.6, r.9 // add 2nd 4
+ lwzu r.11, 16(r.4) // get 4th 4 and update address
+ adde r.6, r.6, r.10 // add 3rd 4
+ adde r.6, r.6, r.11 // add 4th 4
+
+try8: bf 28, try4 // jif no 8 byte block
+ lwz r.8, 4(r.4) // get 1st 4
+ lwzu r.9, 8(r.4) // get 2nd 4 and update address
+ adde r.6, r.6, r.8 // add 1st 4
+ adde r.6, r.6, r.9 // add 2nd 4
+
+try4: bf 29, try2 // jif no 4 byte block
+ lwzu r.8, 4(r.4) // get 4 bytes and update address
+ adde r.6, r.6, r.8
+
+try2: bf 30, fold // jif no 2 byte block
+
+//
+// At this point, r.4 is pointing at the last 4 byte block processed (or
+// not processed if there were no 4 byte blocks). We need to add when we
+// pull the last two bytes.
+//
+ lhz r.8, 4(r.4) // get last two bytes
+ adde r.6, r.6, r.8 // add last two bytes
+
+//
+// Collapse 33 bit (1 carry bit, 32 bits in r.6) into 17 bit checksum.
+//
+
+fold: rlwinm r.7, r.6, 16, 0xffff // get 16 most significant bits (upper)
+ rlwinm r.6, r.6, 0, 0xffff // get least significant 16 bits (lower)
+ adde r.6, r.6, r.7 // upper + lower + carry
+ // max current checksum is 0x1ffff
+
+//
+// Combine input checksum and partial checksum.
+//
+// If the input buffer was byte aligned, then word swap bytes in computed
+// checksum before combination with input chewcksum.
+//
+
+combine:
+
+ bf 7, waseven // jif original alignment was 16 bit
+
+//
+// Swap bytes within upper and lower halves.
+// eg: AA BB CC DD becomes BB AA DD CC
+//
+// As the current maximum partial checksum is 0x1ffff don't worry about AA.
+// ie: want BB 00 DD CC
+//
+
+ rlwimi r.6, r.6, 16, 0xff000000// r.7 = CC BB CC DD
+ rlwinm r.6, r.6, 8, 0xff00ffff// r.7 = BB 00 DD CC
+
+waseven:
+
+ add r.3, r.3, r.6 // combine checksums
+ // max current checksum is 0x101fffe
+ rotlwi r.4, r.3, 16 // swap checksum words
+ add r.3, r.3, r.4 // add words with carry into high word
+ srwi r.3, r.3, 16 // extract final checksum
+
+ LEAF_EXIT(tcpxsum)
+
+ .debug$S
+ .ualong 1
+
+ .uashort 15
+ .uashort 0x9 # S_OBJNAME
+ .ualong 0
+ .byte 8, "xsum.obj"
+
+ .uashort 24
+ .uashort 0x1 # S_COMPILE
+ .byte 0x42 # Target processor = PPC 604
+ .byte 3 # Language = ASM
+ .byte 0
+ .byte 0
+ .byte 17, "PowerPC Assembler"
+
+ .uashort 43
+ .uashort 0x205 # S_GPROC32
+ .ualong 0
+ .ualong 0
+ .ualong 0
+ .ualong tcpxsum.end-..tcpxsum
+ .ualong 0
+ .ualong tcpxsum.end-..tcpxsum
+ .ualong [secoff]..tcpxsum
+ .uashort [secnum]..tcpxsum
+ .uashort 0x1000
+ .byte 0x00
+ .byte 7, "tcpxsum"
+
+ .uashort 2, 0x6 # S_END
diff --git a/private/ntos/tdi/tcpip/tcp/raw.c b/private/ntos/tdi/tcpip/tcp/raw.c
new file mode 100644
index 000000000..6b59f9dd1
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/raw.c
@@ -0,0 +1,693 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** RAW.C - Raw IP interface code.
+//
+// This file contains the code for the Raw IP interface functions,
+// principally send and receive datagram.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#include "tdistat.h"
+#ifdef VXD
+#include "tdivxd.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "raw.h"
+#include "tlcommon.h"
+#include "info.h"
+#include "tcpcfg.h"
+#include "secfltr.h"
+
+
+#define NO_TCP_DEFS 1
+#include "tcpdeb.h"
+
+
+#ifdef NT
+
+#ifdef POOL_TAGGING
+
+#ifdef ExAllocatePool
+#undef ExAllocatePool
+#endif
+
+#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'rPCT')
+
+#ifndef CTEAllocMem
+#error "CTEAllocMem is not already defined - will override tagging"
+#else
+#undef CTEAllocMem
+#endif
+
+#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'rPCT')
+
+#endif // POOL_TAGGING
+
+#endif // NT
+
+EXTERNAL_LOCK(AddrObjTableLock)
+
+void *RawProtInfo = NULL;
+
+extern IPInfo LocalNetInfo;
+
+#ifdef CHICAGO
+extern uchar TransportName[];
+#endif
+
+#ifdef CHICAGO
+extern int RegisterAddrChangeHndlr(void *Handler, uint Add);
+extern void AddrChange(IPAddr Addr, IPMask Mask, void *Context,
+ uint Added);
+#endif
+
+
+
+//** RawSend - Send a datagram.
+//
+// The real send datagram routine. We assume that the busy bit is
+// set on the input AddrObj, and that the address of the SendReq
+// has been verified.
+//
+// We start by sending the input datagram, and we loop until there's
+// nothing left on the send q.
+//
+// Input: SrcAO - Pointer to AddrObj doing the send.
+// SendReq - Pointer to sendreq describing send.
+//
+// Returns: Nothing
+//
+void
+RawSend(AddrObj *SrcAO, DGSendReq *SendReq)
+{
+ PNDIS_BUFFER RawBuffer;
+ CTELockHandle HeaderHandle, AOHandle;
+ RouteCacheEntry *RCE; // RCE used for each send.
+ IPAddr SrcAddr; // Source address IP thinks we should
+ // use.
+ uchar DestType; // Type of destination address.
+ IP_STATUS SendStatus; // Status of send attempt.
+ ushort MSS;
+ uint AddrValid;
+ IPOptInfo *OptInfo;
+ IPAddr OrigSrc;
+ uchar protocol;
+
+
+ CTEStructAssert(SrcAO, ao);
+ CTEAssert(SrcAO->ao_usecnt != 0);
+
+ protocol = SrcAO->ao_prot;
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE((
+ "RawSend called, prot %u\n", protocol
+ ));
+ }
+
+ //* Loop while we have something to send, and can get
+ // resources to send.
+ for (;;) {
+
+ CTEStructAssert(SendReq, dsr);
+
+ // Make sure we have a Raw header buffer for this send. If we
+ // don't, try to get one.
+ if ((RawBuffer = SendReq->dsr_header) == NULL) {
+ // Don't have one, so try to get one.
+ CTEGetLock(&DGSendReqLock, &HeaderHandle);
+ RawBuffer = GetDGHeader();
+ if (RawBuffer != NULL)
+ SendReq->dsr_header = RawBuffer;
+ else {
+ // Couldn't get a header buffer. Push the send request
+ // back on the queue, and queue the addr object for when
+ // we get resources.
+ CTEGetLock(&SrcAO->ao_lock, &AOHandle);
+ PUSHQ(&SrcAO->ao_sendq, &SendReq->dsr_q);
+ PutPendingQ(SrcAO);
+ CTEFreeLock(&SrcAO->ao_lock, AOHandle);
+ CTEFreeLock(&DGSendReqLock, HeaderHandle);
+ return;
+ }
+ CTEFreeLock(&DGSendReqLock, HeaderHandle);
+ }
+
+ // At this point, we have the buffer we need. Call IP to get an
+ // RCE (along with the source address if we need it), then
+ // send the data.
+ CTEAssert(RawBuffer != NULL);
+
+ if (!CLASSD_ADDR(SendReq->dsr_addr)) {
+ // This isn't a multicast send, so we'll use the ordinary
+ // information.
+ OrigSrc = SrcAO->ao_addr;
+ OptInfo = &SrcAO->ao_opt;
+ } else {
+ OrigSrc = SrcAO->ao_mcastaddr;
+ OptInfo = &SrcAO->ao_mcastopt;
+ }
+
+ CTEAssert(!(SrcAO->ao_flags & AO_DHCP_FLAG));
+
+ SrcAddr = (*LocalNetInfo.ipi_openrce)(SendReq->dsr_addr,
+ OrigSrc, &RCE, &DestType, &MSS, OptInfo);
+
+ AddrValid = !IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR);
+
+ if (AddrValid) {
+ // The OpenRCE worked. Send it.
+
+ if (!IP_ADDR_EQUAL(OrigSrc, NULL_IP_ADDR))
+ SrcAddr = OrigSrc;
+
+ NdisBufferLength(RawBuffer) = 0;
+ NDIS_BUFFER_LINKAGE(RawBuffer) = SendReq->dsr_buffer;
+
+ // Now send the packet.
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("RawSend transmitting\n"));
+ }
+
+ UStats.us_outdatagrams++;
+ SendStatus = (*LocalNetInfo.ipi_xmit)(RawProtInfo, SendReq,
+ RawBuffer, (uint)SendReq->dsr_size, SendReq->dsr_addr, SrcAddr,
+ OptInfo, RCE, protocol);
+
+ (*LocalNetInfo.ipi_closerce)(RCE);
+
+ // If it completed immediately, give it back to the user.
+ // Otherwise we'll complete it when the SendComplete happens.
+ // Currently, we don't map the error code from this call - we
+ // might need to in the future.
+ if (SendStatus != IP_PENDING)
+ DGSendComplete(SendReq, RawBuffer);
+
+ } else {
+ TDI_STATUS Status;
+
+ if (DestType == DEST_INVALID)
+ Status = TDI_BAD_ADDR;
+ else
+ Status = TDI_DEST_UNREACHABLE;
+
+ // Complete the request with an error.
+ (*SendReq->dsr_rtn)(SendReq->dsr_context, Status, 0);
+ // Now free the request.
+ SendReq->dsr_rtn = NULL;
+ DGSendComplete(SendReq, RawBuffer);
+ }
+
+ CTEGetLock(&SrcAO->ao_lock, &AOHandle);
+
+ if (!EMPTYQ(&SrcAO->ao_sendq)) {
+ DEQUEUE(&SrcAO->ao_sendq, SendReq, DGSendReq, dsr_q);
+ CTEFreeLock(&SrcAO->ao_lock, AOHandle);
+ } else {
+ CLEAR_AO_REQUEST(SrcAO, AO_SEND);
+ CTEFreeLock(&SrcAO->ao_lock, AOHandle);
+ return;
+ }
+
+ }
+}
+
+
+//* RawDeliver - Deliver a datagram to a user.
+//
+// This routine delivers a datagram to a Raw user. We're called with
+// the AddrObj to deliver on, and with the AddrObjTable lock held.
+// We try to find a receive on the specified AddrObj, and if we do
+// we remove it and copy the data into the buffer. Otherwise we'll
+// call the receive datagram event handler, if there is one. If that
+// fails we'll discard the datagram.
+//
+// Input: RcvAO - AO to receive the datagram.
+// SrcIP - Source IP address of datagram.
+// IPH - IP Header
+// IPHLength - Bytes in IPH.
+// RcvBuf - The IPReceive buffer containing the data.
+// RcvSize - Size received, including the Raw header.
+// TableHandle - Lock handle for AddrObj table.
+//
+// Returns: Nothing.
+//
+void
+RawDeliver(AddrObj *RcvAO, IPAddr SrcIP, IPHeader UNALIGNED *IPH,
+ uint IPHLength, IPRcvBuf *RcvBuf, uint RcvSize, IPOptInfo *OptInfo,
+ CTELockHandle TableHandle)
+{
+ Queue *CurrentQ;
+ CTELockHandle AOHandle;
+ DGRcvReq *RcvReq;
+ uint BytesTaken = 0;
+ uchar AddressBuffer[TCP_TA_SIZE];
+ uint RcvdSize;
+ EventRcvBuffer *ERB = NULL;
+
+ CTEStructAssert(RcvAO, ao);
+
+ CTEGetLock(&RcvAO->ao_lock, &AOHandle);
+ CTEFreeLock(&AddrObjTableLock, AOHandle);
+
+ if (AO_VALID(RcvAO)) {
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE((
+ "Raw delivering %u byte header + %u data bytes to AO %lx\n",
+ IPHLength, RcvSize, RcvAO
+ ));
+ }
+
+ CurrentQ = QHEAD(&RcvAO->ao_rcvq);
+
+ // Walk the list, looking for a receive buffer that matches.
+ while (CurrentQ != QEND(&RcvAO->ao_rcvq)) {
+ RcvReq = QSTRUCT(DGRcvReq, CurrentQ, drr_q);
+
+ CTEStructAssert(RcvReq, drr);
+
+ // If this request is a wildcard request, or matches the source IP
+ // address, deliver it.
+
+ if (IP_ADDR_EQUAL(RcvReq->drr_addr, NULL_IP_ADDR) ||
+ IP_ADDR_EQUAL(RcvReq->drr_addr, SrcIP)) {
+
+ TDI_STATUS Status;
+ PNDIS_BUFFER DestBuf = RcvReq->drr_buffer;
+ uint DestOffset = 0;
+
+
+ // Remove this from the queue.
+ REMOVEQ(&RcvReq->drr_q);
+
+ // We're done. We can free the AddrObj lock now.
+ CTEFreeLock(&RcvAO->ao_lock, TableHandle);
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("Copying to posted receive\n"));
+ }
+
+ // Copy the header
+ DestBuf = CopyFlatToNdis(DestBuf, (uchar *)IPH, IPHLength,
+ &DestOffset, &RcvdSize);
+
+ // Copy the data and then complete the request.
+ RcvdSize += CopyRcvToNdis(RcvBuf, DestBuf,
+ RcvSize, 0, DestOffset);
+
+ CTEAssert(RcvdSize <= RcvReq->drr_size);
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("Copied %u bytes\n", RcvdSize));
+ }
+
+ Status = UpdateConnInfo(RcvReq->drr_conninfo, OptInfo,
+ SrcIP, 0);
+
+ UStats.us_indatagrams++;
+
+ (*RcvReq->drr_rtn)(RcvReq->drr_context, Status, RcvdSize);
+
+ FreeDGRcvReq(RcvReq);
+
+ return;
+
+ }
+
+ // Either the IP address or the port didn't match. Get the next
+ // one.
+ CurrentQ = QNEXT(CurrentQ);
+ }
+
+ // We've walked the list, and not found a buffer. Call the recv.
+ // handler now.
+
+ if (RcvAO->ao_rcvdg != NULL) {
+ PRcvDGEvent RcvEvent = RcvAO->ao_rcvdg;
+ PVOID RcvContext = RcvAO->ao_rcvdgcontext;
+ TDI_STATUS RcvStatus;
+ CTELockHandle OldLevel;
+ uint IndicateSize;
+ uint DestOffset;
+ PNDIS_BUFFER DestBuf;
+
+
+
+ REF_AO(RcvAO);
+ CTEFreeLock(&RcvAO->ao_lock, TableHandle);
+
+ BuildTDIAddress(AddressBuffer, SrcIP, 0);
+
+ IndicateSize = IPHLength;
+
+ if (((uchar *)IPH + IPHLength) == RcvBuf->ipr_buffer) {
+ //
+ // The header is contiguous with the data
+ //
+ IndicateSize += RcvBuf->ipr_size;
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("RawRcv: header & data are contiguous\n"));
+ }
+ }
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("Indicating %u bytes\n", IndicateSize));
+ }
+
+ UStats.us_indatagrams++;
+ RcvStatus = (*RcvEvent)(RcvContext, TCP_TA_SIZE,
+ (PTRANSPORT_ADDRESS)AddressBuffer, 0,
+ NULL, TDI_RECEIVE_COPY_LOOKAHEAD,
+ IndicateSize,
+ IPHLength + RcvSize, &BytesTaken,
+ (uchar *)IPH, &ERB);
+
+ if (RcvStatus == TDI_MORE_PROCESSING) {
+ CTEAssert(ERB != NULL);
+
+ // We were passed back a receive buffer. Copy the data in now.
+
+ // He can't have taken more than was in the indicated
+ // buffer, but in debug builds we'll check to make sure.
+
+ CTEAssert(BytesTaken <= RcvBuf->ipr_size);
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("ind took %u bytes\n", BytesTaken));
+ }
+
+#ifdef NT
+ {
+ PIO_STACK_LOCATION IrpSp;
+ PTDI_REQUEST_KERNEL_RECEIVEDG DatagramInformation;
+
+ IrpSp = IoGetCurrentIrpStackLocation(ERB);
+ DatagramInformation = (PTDI_REQUEST_KERNEL_RECEIVEDG)
+ &(IrpSp->Parameters);
+
+ DestBuf = ERB->MdlAddress;
+#else // NT
+ DestBuf = ERB->erb_buffer;
+#endif // NT
+ DestOffset = 0;
+
+ if (BytesTaken < IPHLength) {
+
+ // Copy the rest of the IP header
+ DestBuf = CopyFlatToNdis(
+ DestBuf,
+ (uchar *)IPH + BytesTaken,
+ IPHLength - BytesTaken,
+ &DestOffset,
+ &RcvdSize
+ );
+
+ BytesTaken = 0;
+ }
+ else {
+ BytesTaken -= IPHLength;
+ RcvdSize = 0;
+ }
+
+ // Copy the data
+ RcvdSize += CopyRcvToNdis(
+ RcvBuf,
+ DestBuf,
+ RcvSize - BytesTaken,
+ BytesTaken,
+ DestOffset
+ );
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("Copied %u bytes\n", RcvdSize));
+ }
+
+#ifdef NT
+ //
+ // Update the return address info
+ //
+ RcvStatus = UpdateConnInfo(
+ DatagramInformation->ReturnDatagramInformation,
+ OptInfo, SrcIP, 0);
+
+ //
+ // Complete the IRP.
+ //
+ ERB->IoStatus.Information = RcvdSize;
+ ERB->IoStatus.Status = RcvStatus;
+ IoCompleteRequest(ERB, 2);
+ }
+
+#else // NT
+ //
+ // Call the completion routine.
+ //
+ (*ERB->erb_rtn)(ERB->erb_context, TDI_SUCCESS, RcvdSize);
+
+#endif // NT
+
+ }
+ else {
+ CTEAssert(
+ (RcvStatus == TDI_SUCCESS) ||
+ (RcvStatus == TDI_NOT_ACCEPTED)
+ );
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE((
+ "Data %s taken\n",
+ (RcvStatus == TDI_SUCCESS) ? "all" : "not"
+ ));
+ }
+
+ CTEAssert(ERB == NULL);
+ }
+
+ DELAY_DEREF_AO(RcvAO);
+
+ return;
+
+ } else
+ UStats.us_inerrors++;
+
+ // When we get here, we didn't have a buffer to put this data into.
+ // Fall through to the return case.
+ } else
+ UStats.us_inerrors++;
+
+ CTEFreeLock(&RcvAO->ao_lock, TableHandle);
+
+}
+
+
+//* RawRcv - Receive a Raw datagram.
+//
+// The routine called by IP when a Raw datagram arrived. We
+// look up the port/local address pair in our address table,
+// and deliver the data to a user if we find one. For broadcast
+// frames we may deliver it to multiple users.
+//
+// Entry: IPContext - IPContext identifying physical i/f that
+// received the data.
+// Dest - IPAddr of destionation.
+// Src - IPAddr of source.
+// LocalAddr - Local address of network which caused this to be
+// received.
+// SrcAddr - Address of local interface which received the packet
+// IPH - IP Header.
+// IPHLength - Bytes in IPH.
+// RcvBuf - Pointer to receive buffer chain containing data.
+// Size - Size in bytes of data received.
+// IsBCast - Boolean indicator of whether or not this came in as
+// a bcast.
+// Protocol - Protocol this came in on - should be Raw.
+// OptInfo - Pointer to info structure for received options.
+//
+// Returns: Status of reception. Anything other than IP_SUCCESS will cause
+// IP to send a 'port unreachable' message.
+//
+IP_STATUS
+RawRcv(void *IPContext, IPAddr Dest, IPAddr Src, IPAddr LocalAddr,
+ IPAddr SrcAddr, IPHeader UNALIGNED *IPH, uint IPHLength, IPRcvBuf *RcvBuf,
+ uint Size, uchar IsBCast, uchar Protocol, IPOptInfo *OptInfo)
+{
+ CTELockHandle AOTableHandle;
+ AddrObj *ReceiveingAO;
+ uchar DType;
+ AOSearchContext Search;
+ IP_STATUS Status = IP_DEST_PROT_UNREACHABLE;
+
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("RawRcv prot %u size %u\n", IPH->iph_protocol, Size));
+ }
+
+ DType = (*LocalNetInfo.ipi_getaddrtype)(Src);
+
+ // The following code relies on DEST_INVALID being a broadcast dest type.
+ // If this is changed the code here needs to change also.
+ if (IS_BCAST_DEST(DType)) {
+ if (!IP_ADDR_EQUAL(Src, NULL_IP_ADDR) || !IsBCast) {
+ UStats.us_inerrors++;
+ return IP_SUCCESS; // Bad src address.
+ }
+ }
+
+ // Get the AddrObjTable lock, and then try to find some AddrObj(s) to give
+ // this to. We deliver to all addr objs registered for the protocol and
+ // address.
+ CTEGetLock(&AddrObjTableLock, &AOTableHandle);
+
+#ifdef SECFLTR
+
+ if ( !SecurityFilteringEnabled ||
+ IsPermittedSecurityFilter(SrcAddr, IPContext, PROTOCOL_RAW, Protocol)
+ )
+ {
+
+#endif // SECFLTR
+
+ ReceiveingAO = GetFirstAddrObj(
+ LocalAddr,
+ 0, // port is zero
+ Protocol,
+ &Search
+ );
+
+ if (ReceiveingAO != NULL) {
+ do {
+ RawDeliver(
+ ReceiveingAO, Src, IPH, IPHLength, RcvBuf, Size,
+ OptInfo, AOTableHandle
+ );
+ CTEGetLock(&AddrObjTableLock, &AOTableHandle);
+ ReceiveingAO = GetNextAddrObj(&Search);
+ } while (ReceiveingAO != NULL);
+ Status = IP_SUCCESS;
+ } else {
+ UStats.us_noports++;
+ }
+
+#ifdef SECFLTR
+
+ }
+#endif SECFLTR
+
+
+ CTEFreeLock(&AddrObjTableLock, AOTableHandle);
+
+ return Status;
+}
+
+
+//* RawStatus - Handle a status indication.
+//
+// This is the Raw status handler, called by IP when a status event
+// occurs. For most of these we do nothing. For certain severe status
+// events we will mark the local address as invalid.
+//
+// Entry: StatusType - Type of status (NET or HW). NET status
+// is usually caused by a received ICMP
+// message. HW status indicate a HW
+// problem.
+// StatusCode - Code identifying IP_STATUS.
+// OrigDest - If this is NET status, the original dest. of
+// DG that triggered it.
+// OrigSrc - " " " " " , the original src.
+// Src - IP address of status originator (could be local
+// or remote).
+// Param - Additional information for status - i.e. the
+// param field of an ICMP message.
+// Data - Data pertaining to status - for NET status, this
+// is the first 8 bytes of the original DG.
+//
+// Returns: Nothing
+//
+void
+RawStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest,
+ IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data)
+{
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("RawStatus called\n"));
+ }
+
+ // If this is a HW status, it could be because we've had an address go
+ // away.
+ if (StatusType == IP_HW_STATUS) {
+
+ if (StatusCode == IP_ADDR_DELETED) {
+
+ // An address has gone away. OrigDest identifies the address.
+
+#ifndef _PNP_POWER
+ //
+ // This is done via TDI notifications in the PNP world.
+ //
+ InvalidateAddrs(OrigDest);
+
+#endif // _PNP_POWER
+
+#ifdef SECFLTR
+ //
+ // Delete any security filters associated with this address
+ //
+ DeleteProtocolSecurityFilter(OrigDest, PROTOCOL_RAW);
+
+#endif // SECFLTR
+
+ return;
+ }
+
+ if (StatusCode == IP_ADDR_ADDED) {
+
+#ifdef SECFLTR
+ //
+ // An address has materialized. OrigDest identifies the address.
+ // Data is a handle to the IP configuration information for the
+ // interface on which the address is instantiated.
+ //
+ AddProtocolSecurityFilter(OrigDest, PROTOCOL_RAW,
+ (NDIS_HANDLE) Data);
+#endif // SECFLTR
+
+ return;
+ }
+
+#ifdef CHICAGO
+ if (StatusCode == IP_UNLOAD) {
+ // IP is telling us we're being unloaded. First, deregister
+ // with VTDI, and then call CTEUnload().
+ (void)TLRegisterProtocol(PROTOCOL_ANY, NULL, NULL, NULL, NULL);
+
+#ifdef UDP_ONLY
+ // Only do the following in the UDP_ONLY version. TCP does it in
+ // the generic version.
+ TLRegisterDispatch(TransportName, NULL);
+ (void)RegisterAddrChangeHndlr(AddrChange, FALSE);
+ CTEUnload(TransportName);
+#endif // UDP_ONLY
+
+ return;
+ }
+#endif // CHICAGO
+ }
+}
+
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/raw.h b/private/ntos/tdi/tcpip/tcp/raw.h
new file mode 100644
index 000000000..8619dc30f
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/raw.h
@@ -0,0 +1,34 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** RAW.H - Raw IP interface definitions.
+//
+// This file contains definitions for the Raw IP interface functions.
+//
+
+#include "dgram.h"
+
+
+//
+// This value is used to identify the RAW transport for security filtering.
+// It is out of the range of valid IP protocols.
+//
+#define PROTOCOL_RAW 255
+
+
+//* External definitions.
+extern IP_STATUS RawRcv(void *IPContext, IPAddr Dest, IPAddr Src,
+ IPAddr LocalAddr, IPAddr SrcAddr,
+ IPHeader UNALIGNED *IPH, uint IPHLength,
+ IPRcvBuf *RcvBuf, uint Size, uchar IsBCast,
+ uchar Protocol, IPOptInfo *OptInfo);
+
+extern void RawStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest,
+ IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data);
+
+extern void RawSend(AddrObj *SrcAO, DGSendReq *SendReq);
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/secfltr.c b/private/ntos/tdi/tcpip/tcp/secfltr.c
new file mode 100644
index 000000000..df64419e9
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/secfltr.c
@@ -0,0 +1,1425 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+#ifdef SECFLTR
+
+
+//** SECFLTR.C - Security Filter Support
+//
+//
+// Security filters provide a mechanism by which the transport protocol
+// traffic accepted on IP interfaces may be controlled. Security filtering
+// is globally enabled or disabled for all IP interfaces and transports.
+// If filtering is enabled, incoming traffic is filtered based on registered
+// {interface, protocol, transport value} tuples. The tuples specify
+// permissible traffic. All other values will be rejected. For UDP datagrams
+// and TCP connections, the transport value is the port number. For RawIP
+// datagrams, the transport value is the IP protocol number. An entry exists
+// in the filter database for all active interfaces and protocols in the
+// system.
+//
+// The initial status of security filtering - enabled or disabled, is
+// controlled by the registry parameter
+//
+// Services\Tcpip\Parameters\EnableSecurityFilters
+//
+// If the parameter is not found, filtering is disabled.
+//
+// The list of permissible values for each protocol is stored in the registry
+// under the <Adaptername>\Parameters\Tcpip key in MULTI_SZ parameters.
+// The parameter names are TCPAllowedPorts, UDPAllowedPorts and
+// RawIPAllowedProtocols. If no parameter is found for a particular protocol,
+// all values are permissible. If a parameter is found, the string identifies
+// the permissible values. If the string is empty, no values are permissible.
+//
+// Filter Operation (Filtering Enabled):
+//
+// IF ( Match(interface, protocol) AND ( AllValuesPermitted(Protocol) OR
+// Match(Value) ))
+// THEN operation permitted.
+// ELSE operation rejected.
+//
+// Database Implementation:
+//
+// The filter database is implemented as a three-level structure. The top
+// level is a list of interface entries. Each interface entry points to
+// a list of protocol entries. Each protocol entry contains a bucket hash
+// table used to store transport value entries.
+//
+
+// The following calls may be used to access the security filter database:
+//
+// InitializeSecurityFilters
+// CleanupSecurityFilters
+// IsSecurityFilteringEnabled
+// ControlSecurityFiltering
+// AddProtocolSecurityFilter
+// DeleteProtocolSecurityFilter
+// AddValueSecurityFilter
+// DeleteValueSecurityFilter
+// EnumerateSecurityFilters
+// IsPermittedSecurityFilter
+//
+
+#include "oscfg.h"
+#include "queue.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#include "tdistat.h"
+
+#ifdef NT
+
+#include "tdikrnl.h"
+#include "tdint.h"
+#include "tdistat.h"
+
+#endif // NT
+
+#include "addr.h"
+#include "tlcommon.h"
+#include "udp.h"
+#include "tcp.h"
+#include "raw.h"
+#include "tcpcfg.h"
+#include "tcpinfo.h"
+#include "secfltr.h"
+
+
+//
+// All of the init code can be discarded.
+//
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(INIT, InitializeSecurityFilters)
+
+#endif // ALLOC_PRAGMA
+#endif // NT
+
+//
+// The following routines must be supplied by each platform which implements
+// security filters.
+//
+extern TDI_STATUS
+GetSecurityFilterList(
+ IN NDIS_HANDLE ConfigHandle,
+ IN ulong Protocol,
+ IN OUT PNDIS_STRING FilterList
+ );
+
+extern uint
+EnumSecurityFilterValue(
+ IN PNDIS_STRING FilterList,
+ IN ulong Index,
+ OUT ulong *FilterValue
+ );
+
+//
+// Constants
+//
+
+#define DHCP_CLIENT_PORT 68
+
+
+//
+// Modification Opcodes
+//
+#define ADD_VALUE_SECURITY_FILTER 0
+#define DELETE_VALUE_SECURITY_FILTER 1
+
+
+//
+// Types
+//
+
+//
+// Structure for a transport value entry.
+//
+struct value_entry {
+ struct Queue ve_link;
+ ulong ve_value;
+};
+
+typedef struct value_entry VALUE_ENTRY, *PVALUE_ENTRY;
+
+
+#define VALUE_ENTRY_HASH_SIZE 16
+#define VALUE_ENTRY_HASH(value) (value % VALUE_ENTRY_HASH_SIZE)
+
+//
+// Structure for a protocol entry.
+//
+struct protocol_entry {
+ struct Queue pe_link;
+ ulong pe_protocol;
+ ULONG pe_accept_all; // TRUE if all values are accepted.
+ struct Queue pe_entries[VALUE_ENTRY_HASH_SIZE];
+};
+
+typedef struct protocol_entry PROTOCOL_ENTRY, *PPROTOCOL_ENTRY;
+
+//
+// Structure for an interface entry.
+//
+struct interface_entry {
+ struct Queue ie_link;
+ IPAddr ie_address;
+ struct Queue ie_protocol_list; // list of protocols to filter
+};
+
+typedef struct interface_entry INTERFACE_ENTRY, *PINTERFACE_ENTRY;
+
+
+//
+// Global Data
+//
+
+//
+// This list of interface entries is the root of the filter database.
+//
+struct Queue InterfaceEntryList;
+
+//
+// The filter operations are synchronized using the AddrObjTableLock.
+//
+EXTERNAL_LOCK(AddrObjTableLock)
+
+extern IPInfo LocalNetInfo;
+
+
+//
+// Filter Database Helper Functions
+//
+
+//* FindInterfaceEntry - Search for an interface entry.
+//
+// This utility routine searches the security filter database
+// for the specified interface entry.
+//
+//
+// Input: InterfaceAddress - The address of the interface to search for.
+//
+//
+// Returns: A pointer to the database entry for the Interface,
+// or NULL if no match was found.
+//
+//
+PINTERFACE_ENTRY
+FindInterfaceEntry(ULONG InterfaceAddress)
+{
+ PINTERFACE_ENTRY ientry;
+ struct Queue *qentry;
+
+
+ for ( qentry = InterfaceEntryList.q_next;
+ qentry != &InterfaceEntryList;
+ qentry = qentry->q_next
+ )
+ {
+ ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link);
+
+ if (ientry->ie_address == InterfaceAddress) {
+ return(ientry);
+ }
+ }
+
+ return(NULL);
+}
+
+
+//* FindProtocolEntry - Search for a protocol associated with an interface.
+//
+// This utility routine searches the security filter database
+// for the specified protocol registered under the specified interface.
+//
+//
+// Input: InterfaceEntry - A pointer to an interface entry to search under.
+// Protocol - The protocol value to search for.
+//
+//
+// Returns: A pointer to the database entry for the <Address, Protocol>,
+// or NULL if no match was found.
+//
+//
+PPROTOCOL_ENTRY
+FindProtocolEntry(PINTERFACE_ENTRY InterfaceEntry, ULONG Protocol)
+
+{
+ PPROTOCOL_ENTRY pentry;
+ struct Queue *qentry;
+
+
+ for ( qentry = InterfaceEntry->ie_protocol_list.q_next;
+ qentry != &(InterfaceEntry->ie_protocol_list);
+ qentry = qentry->q_next
+ )
+ {
+ pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link);
+
+ if (pentry->pe_protocol == Protocol) {
+ return(pentry);
+ }
+ }
+
+ return(NULL);
+}
+
+
+//* FindValueEntry - Search for a value on a particular protocol.
+//
+// This utility routine searches the security filter database
+// for the specified value registered under the specified protocol.
+//
+//
+// Input: ProtocolEntry - A pointer to the database structure for the
+// Protocol to search.
+// FilterValue - The value to search for.
+//
+//
+// Returns: A pointer to the database entry for the <Protocol, Value>,
+// or NULL if no match was found.
+//
+//
+PVALUE_ENTRY
+FindValueEntry(PPROTOCOL_ENTRY ProtocolEntry, ULONG FilterValue)
+{
+ PVALUE_ENTRY ventry;
+ ulong hash_value = VALUE_ENTRY_HASH(FilterValue);
+ struct Queue *qentry;
+
+
+ for ( qentry = ProtocolEntry->pe_entries[hash_value].q_next;
+ qentry != &(ProtocolEntry->pe_entries[hash_value]);
+ qentry = qentry->q_next
+ )
+ {
+ ventry = STRUCT_OF(VALUE_ENTRY, qentry, ve_link);
+
+ if (ventry->ve_value == FilterValue) {
+ return(ventry);
+ }
+ }
+
+ return(NULL);
+}
+
+
+//* DeleteProtocolValueEntries
+//
+// This utility routine deletes all the value entries associated with
+// a protocol filter entry.
+//
+//
+// Input: ProtocolEntry - The protocol filter entry for which to
+// delete the value entries.
+//
+//
+// Returns: Nothing
+//
+void
+DeleteProtocolValueEntries(PPROTOCOL_ENTRY ProtocolEntry)
+{
+ ulong i;
+ PVALUE_ENTRY entry;
+
+
+ for (i=0; i < VALUE_ENTRY_HASH_SIZE; i++) {
+ while (!EMPTYQ(&(ProtocolEntry->pe_entries[i]))) {
+
+ DEQUEUE(&(ProtocolEntry->pe_entries[i]), entry, VALUE_ENTRY, ve_link);
+ CTEFreeMem(entry);
+ }
+ }
+
+ return;
+}
+
+
+//* ModifyProtocolEntry
+//
+// This utility routine modifies one or more filter values associated
+// with a protocol.
+//
+//
+// Input: Operation - The operation to perform (add or delete)
+//
+// ProtocolEntry - A pointer to the protocol entry structure on
+// which to operate.
+//
+// FilterValue - The value to add or delete.
+//
+//
+// Returns: TDI_STATUS code
+//
+TDI_STATUS
+ModifyProtocolEntry(ulong Operation, PPROTOCOL_ENTRY ProtocolEntry,
+ ulong FilterValue)
+{
+ TDI_STATUS status = TDI_SUCCESS;
+
+
+ if (FilterValue == 0) {
+ if (Operation == ADD_VALUE_SECURITY_FILTER) {
+ //
+ // Accept all values for the protocol
+ //
+ ProtocolEntry->pe_accept_all = TRUE;
+ }
+ else {
+ //
+ // Reject all values for the protocol
+ //
+ ProtocolEntry->pe_accept_all = FALSE;
+ }
+
+ DeleteProtocolValueEntries(ProtocolEntry);
+ }
+ else {
+ PVALUE_ENTRY ventry;
+ ulong hash_value;
+
+ //
+ // This request modifies an individual entry.
+ //
+ ventry = FindValueEntry(ProtocolEntry, FilterValue);
+
+ if (Operation == ADD_VALUE_SECURITY_FILTER) {
+
+ if (ventry == NULL) {
+
+ ventry = CTEAllocMem(sizeof(VALUE_ENTRY));
+
+ if (ventry != NULL) {
+ ventry->ve_value = FilterValue;
+ hash_value = VALUE_ENTRY_HASH(FilterValue);
+
+ ENQUEUE(&(ProtocolEntry->pe_entries[hash_value]),
+ &(ventry->ve_link));
+
+ ProtocolEntry->pe_accept_all = FALSE;
+ }
+ else {
+ status = TDI_NO_RESOURCES;
+ }
+ }
+ }
+ else {
+ if (ventry != NULL) {
+ REMOVEQ(&(ventry->ve_link));
+ CTEFreeMem(ventry);
+ }
+ }
+ }
+
+ return(status);
+}
+
+
+//* ModifyInterfaceEntry
+//
+// This utility routine modifies the value entries of one or more protocol
+// entries associated with an interface.
+//
+//
+// Input: Operation - The operation to perform (add or delete)
+//
+// ProtocolEntry - A pointer to the interface entry structure on
+// which to operate.
+//
+// Protocol - The protocol on which to operate.
+//
+// FilterValue - The value to add or delete.
+//
+//
+// Returns: TDI_STATUS code
+//
+TDI_STATUS
+ModifyInterfaceEntry(ulong Operation, PINTERFACE_ENTRY InterfaceEntry,
+ ulong Protocol, ulong FilterValue)
+{
+ PPROTOCOL_ENTRY pentry;
+ TDI_STATUS status;
+ TDI_STATUS returnStatus = TDI_SUCCESS;
+
+
+ if (Protocol == 0) {
+ struct Queue *qentry;
+
+
+ //
+ // Modify all protocols on the interface
+ //
+ for ( qentry = InterfaceEntry->ie_protocol_list.q_next;
+ qentry != &(InterfaceEntry->ie_protocol_list);
+ qentry = qentry->q_next
+ )
+ {
+ pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link);
+ status = ModifyProtocolEntry(Operation, pentry, FilterValue);
+
+ if (status != TDI_SUCCESS) {
+ returnStatus = status;
+ }
+ }
+ }
+ else {
+ //
+ // Modify a specific protocol on the interface
+ //
+ pentry = FindProtocolEntry(InterfaceEntry, Protocol);
+
+ if (pentry != NULL) {
+ returnStatus = ModifyProtocolEntry(Operation, pentry, FilterValue);
+ }
+ else {
+ returnStatus = TDI_INVALID_PARAMETER;
+ }
+ }
+
+ return(returnStatus);
+}
+
+
+//* ModifySecurityFilter - Add or delete an entry.
+//
+// This routine adds or deletes an entry to/from the security filter database.
+//
+//
+// Input: Operation - The operation to perform (Add or Delete)
+// InterfaceAddress - The interface address to modify.
+// Protocol - The protocol to modify.
+// FilterValue - The transport value to add/delete.
+//
+// Returns: A TDI status code:
+// TDI_INVALID_PARAMETER if the protocol is not in the database.
+// TDI_ADDR_INVALID if the interface is not in the database.
+// TDI_NO_RESOURCES if memory could not be allocated.
+// TDI_SUCCESS otherwise
+//
+// NOTES:
+//
+TDI_STATUS
+ModifySecurityFilter(ulong Operation, IPAddr InterfaceAddress, ulong Protocol,
+ ulong FilterValue)
+{
+ PINTERFACE_ENTRY ientry;
+ TDI_STATUS status;
+ TDI_STATUS returnStatus = TDI_SUCCESS;
+
+
+ if (InterfaceAddress == 0) {
+ struct Queue *qentry;
+
+ //
+ // Modify on all interfaces
+ //
+ for ( qentry = InterfaceEntryList.q_next;
+ qentry != &InterfaceEntryList;
+ qentry = qentry->q_next
+ )
+ {
+ ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link);
+ status = ModifyInterfaceEntry(Operation, ientry, Protocol,
+ FilterValue);
+
+ if (status != TDI_SUCCESS) {
+ returnStatus = status;
+ }
+ }
+ }
+ else {
+ ientry = FindInterfaceEntry(InterfaceAddress);
+
+ if (ientry != NULL) {
+ returnStatus = ModifyInterfaceEntry(Operation, ientry, Protocol,
+ FilterValue);
+ }
+ else {
+ returnStatus = TDI_ADDR_INVALID;
+ }
+ }
+
+ return(returnStatus);
+}
+
+
+//* FillInEnumerationEntry
+//
+// This utility routine fills in an enumeration entry for a particular
+// filter value entry.
+//
+//
+// Input: InterfaceAddress - The address of the associated interface.
+//
+// Protocol - The associated protocol number.
+//
+// Value - The enumerated value.
+//
+// Buffer - Pointer to the user's enumeration buffer.
+//
+// BufferSize - Pointer to the size of the enumeration buffer.
+//
+// EntriesReturned - Pointer to a running count of enumerated
+// entries stored in Buffer.
+//
+// EntriesAvailable - Pointer to a running count of entries available
+// for enumeration.
+//
+// Returns: Nothing.
+//
+// Note: Values written to enumeration entry are in host byte order.
+//
+void
+FillInEnumerationEntry(IPAddr InterfaceAddress, ulong Protocol, ulong Value,
+ uchar **Buffer, ulong *BufferSize,
+ ulong *EntriesReturned, ulong *EntriesAvailable)
+{
+ TCPSecurityFilterEntry *entry = (TCPSecurityFilterEntry *) *Buffer;
+
+
+ if (*BufferSize >= sizeof(TCPSecurityFilterEntry)) {
+ entry->tsf_address = net_long(InterfaceAddress);
+ entry->tsf_protocol = Protocol;
+ entry->tsf_value = Value;
+
+ *Buffer += sizeof(TCPSecurityFilterEntry);
+ *BufferSize -= sizeof(TCPSecurityFilterEntry);
+ (*EntriesReturned)++;
+ }
+
+ (*EntriesAvailable)++;
+
+ return;
+}
+
+
+//* EnumerateProtocolValues
+//
+// This utility routine enumerates values associated with a
+// protocol on an interface.
+//
+//
+// Input: InterfaceEntry - Pointer to the associated interface entry.
+//
+// ProtocolEntry - Pointer to the protocol being enumerated.
+//
+// Value - The value to enumerate.
+//
+// Buffer - Pointer to the user's enumeration buffer.
+//
+// BufferSize - Pointer to the size of the enumeration buffer.
+//
+// EntriesReturned - Pointer to a running count of enumerated
+// entries stored in Buffer.
+//
+// EntriesAvailable - Pointer to a running count of entries available
+// for enumeration.
+//
+// Returns: Nothing.
+//
+void
+EnumerateProtocolValues(PINTERFACE_ENTRY InterfaceEntry,
+ PPROTOCOL_ENTRY ProtocolEntry, ulong Value,
+ uchar **Buffer, ulong *BufferSize,
+ ulong *EntriesReturned, ulong *EntriesAvailable)
+{
+ struct Queue *qentry;
+ TDI_STATUS status = TDI_SUCCESS;
+ PVALUE_ENTRY ventry;
+ ulong i;
+
+
+ if (Value == 0) {
+ //
+ // Enumerate all values
+ //
+ if (ProtocolEntry->pe_accept_all == TRUE) {
+ //
+ // All values permitted.
+ //
+ FillInEnumerationEntry(
+ InterfaceEntry->ie_address,
+ ProtocolEntry->pe_protocol,
+ 0,
+ Buffer,
+ BufferSize,
+ EntriesReturned,
+ EntriesAvailable
+ );
+ }
+ else {
+ for (i=0; i < VALUE_ENTRY_HASH_SIZE; i++) {
+ for ( qentry = ProtocolEntry->pe_entries[i].q_next;
+ qentry != &(ProtocolEntry->pe_entries[i]);
+ qentry = qentry->q_next
+ )
+ {
+ ventry = STRUCT_OF(VALUE_ENTRY, qentry, ve_link);
+
+ FillInEnumerationEntry(
+ InterfaceEntry->ie_address,
+ ProtocolEntry->pe_protocol,
+ ventry->ve_value,
+ Buffer,
+ BufferSize,
+ EntriesReturned,
+ EntriesAvailable
+ );
+ }
+ }
+ }
+ }
+ else {
+ //
+ // Enumerate a specific value, if it is registered.
+ //
+ ventry = FindValueEntry(ProtocolEntry, Value);
+
+ if (ventry != NULL) {
+ FillInEnumerationEntry(
+ InterfaceEntry->ie_address,
+ ProtocolEntry->pe_protocol,
+ ventry->ve_value,
+ Buffer,
+ BufferSize,
+ EntriesReturned,
+ EntriesAvailable
+ );
+ }
+ }
+
+ return;
+}
+
+
+//* EnumerateInterfaceProtocols
+//
+// This utility routine enumerates protocols associated with
+// an interface.
+//
+//
+// Input: InterfaceEntry - Pointer to the associated interface entry.
+//
+// Protocol - Protocol number to enumerate.
+//
+// Value - The filter value to enumerate.
+//
+// Buffer - Pointer to the user's enumeration buffer.
+//
+// BufferSize - Pointer to the size of the enumeration buffer.
+//
+// EntriesReturned - Pointer to a running count of enumerated
+// entries stored in Buffer.
+//
+// EntriesAvailable - Pointer to a running count of entries available
+// for enumeration.
+//
+// Returns: Nothing.
+//
+void
+EnumerateInterfaceProtocols(PINTERFACE_ENTRY InterfaceEntry, ulong Protocol,
+ ulong Value, uchar **Buffer, ulong *BufferSize,
+ ulong *EntriesReturned, ulong *EntriesAvailable)
+{
+ PPROTOCOL_ENTRY pentry;
+
+
+ if (Protocol == 0) {
+ struct Queue *qentry;
+
+ //
+ // Enumerate all protocols.
+ //
+ for ( qentry = InterfaceEntry->ie_protocol_list.q_next;
+ qentry != &(InterfaceEntry->ie_protocol_list);
+ qentry = qentry->q_next
+ )
+ {
+ pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link);
+
+ EnumerateProtocolValues(
+ InterfaceEntry,
+ pentry,
+ Value,
+ Buffer,
+ BufferSize,
+ EntriesReturned,
+ EntriesAvailable
+ );
+ }
+ }
+ else {
+ //
+ // Enumerate a specific protocol
+ //
+
+ pentry = FindProtocolEntry(InterfaceEntry, Protocol);
+
+ if (pentry != NULL) {
+ EnumerateProtocolValues(
+ InterfaceEntry,
+ pentry,
+ Value,
+ Buffer,
+ BufferSize,
+ EntriesReturned,
+ EntriesAvailable
+ );
+ }
+ }
+
+ return;
+}
+
+
+//
+// Filter Database Public API.
+//
+
+//* InitializeSecurityFilters - Initializes the security filter database.
+//
+// The routine performs the initialization necessary to enable the
+// security filter database for operation.
+//
+// Input: None.
+//
+// Returns: Nothing.
+//
+//
+void
+InitializeSecurityFilters(void)
+{
+ INITQ(&InterfaceEntryList);
+
+ return;
+}
+
+
+//* CleanupSecurityFilters - Deletes the entire security filter database.
+//
+// This routine deletes all entries from the security filter database.
+//
+//
+// Input: None.
+//
+// Returns: Nothing.
+//
+// NOTE: This routine acquires the AddrObjTableLock.
+//
+//
+void
+CleanupSecurityFilters(void)
+{
+ PPROTOCOL_ENTRY pentry;
+ PINTERFACE_ENTRY ientry;
+ CTELockHandle handle;
+
+
+ CTEGetLock(&AddrObjTableLock, &handle);
+
+ while (!EMPTYQ(&InterfaceEntryList)) {
+ DEQUEUE(&InterfaceEntryList, ientry, INTERFACE_ENTRY, ie_link);
+
+ while (!EMPTYQ(&(ientry->ie_protocol_list))) {
+ DEQUEUE(&(ientry->ie_protocol_list), pentry, PROTOCOL_ENTRY,
+ pe_link);
+
+ DeleteProtocolValueEntries(pentry);
+
+ CTEFreeMem(pentry);
+ }
+
+ CTEFreeMem(ientry);
+ }
+
+ SecurityFilteringEnabled = FALSE;
+
+ CTEFreeLock(&AddrObjTableLock, handle);
+
+ return;
+}
+
+
+//* IsSecurityFilteringEnabled
+//
+// This routine returns the current global status of security filtering.
+//
+// Entry: Nothing
+//
+// Returns: 0 if filtering is disabled, !0 if filtering is enabled.
+//
+extern uint
+IsSecurityFilteringEnabled(void)
+{
+ uint enabled;
+ CTELockHandle handle;
+
+
+ CTEGetLock(&AddrObjTableLock, &handle);
+
+ enabled = SecurityFilteringEnabled;
+
+ CTEFreeLock(&AddrObjTableLock, handle);
+
+ return(enabled);
+}
+
+
+//* ControlSecurityFiltering
+//
+// This routine globally enables/disables security filtering.
+//
+// Entry: IsEnabled - 0 disabled filtering, !0 enables filtering.
+//
+// Returns: Nothing
+//
+extern void
+ControlSecurityFiltering(uint IsEnabled)
+{
+ CTELockHandle handle;
+
+
+ CTEGetLock(&AddrObjTableLock, &handle);
+
+ if (IsEnabled) {
+ SecurityFilteringEnabled = TRUE;
+ }
+ else {
+ SecurityFilteringEnabled = FALSE;
+ }
+
+ CTEFreeLock(&AddrObjTableLock, handle);
+
+ return;
+}
+
+
+//* AddProtocolSecurityFilter
+//
+// This routine enables security filtering for a specified protocol
+// on a specified IP interface.
+//
+// Entry: InterfaceAddress - The interface on which to enable the protocol.
+// (in network byte order)
+// Protocol - The protocol to enable.
+// ConfigName - The configuration key from which to read
+// the filter value information.
+//
+// Returns: Nothing
+//
+void
+AddProtocolSecurityFilter(IPAddr InterfaceAddress, ulong Protocol,
+ NDIS_HANDLE ConfigHandle)
+{
+ NDIS_STRING filterList;
+ ulong filterValue;
+ ulong i;
+ PINTERFACE_ENTRY ientry;
+ PPROTOCOL_ENTRY pentry;
+ PVOID temp;
+ CTELockHandle handle;
+ TDI_STATUS status;
+
+
+ if ( IP_ADDR_EQUAL(InterfaceAddress, NULL_IP_ADDR) ||
+ IP_LOOPBACK_ADDR(InterfaceAddress)
+ )
+ {
+ return;
+ }
+
+ CTEAssert( (Protocol != 0) && (Protocol <= 0xFF) );
+
+ //
+ // Read the protocol-specific filter value list from the registry.
+ //
+ filterList.MaximumLength = filterList.Length = 0;
+ filterList.Buffer = NULL;
+
+ if (ConfigHandle != NULL) {
+ status = GetSecurityFilterList(ConfigHandle, Protocol, &filterList);
+ }
+
+ //
+ // Preallocate interface & protocol structures. We abort on failure.
+ // The interface & protocol will be protected by default.
+ //
+ ientry = CTEAllocMem(sizeof(INTERFACE_ENTRY));
+
+ if (ientry == NULL) {
+ return;
+ }
+
+ ientry->ie_address = InterfaceAddress;
+ INITQ(&(ientry->ie_protocol_list));
+
+ pentry = CTEAllocMem(sizeof(PROTOCOL_ENTRY));
+
+ if (pentry == NULL) {
+ CTEFreeMem(ientry);
+ return;
+ }
+
+ pentry->pe_protocol = Protocol;
+ pentry->pe_accept_all = FALSE;
+
+ for (i=0; i<VALUE_ENTRY_HASH_SIZE; i++) {
+ INITQ(&(pentry->pe_entries[i]));
+ }
+
+ //
+ // Now go set everything up. First create the interface and protocol
+ // structures.
+ //
+ CTEGetLock(&AddrObjTableLock, &handle);
+
+ temp = FindInterfaceEntry(InterfaceAddress);
+
+ if (temp == NULL) {
+ //
+ // New interface & protocol.
+ //
+ ENQUEUE(&InterfaceEntryList, &(ientry->ie_link));
+ ENQUEUE(&(ientry->ie_protocol_list), &(pentry->pe_link));
+ }
+ else {
+ //
+ // Existing interface
+ //
+ CTEFreeMem(ientry);
+ ientry = temp;
+
+ temp = FindProtocolEntry(ientry, Protocol);
+
+ if (temp == NULL) {
+ //
+ // New protocol
+ //
+ ENQUEUE(&(ientry->ie_protocol_list), &(pentry->pe_link));
+ }
+ else {
+ //
+ // Existing protocol
+ //
+ CTEFreeMem(pentry);
+ }
+ }
+
+ CTEFreeLock(&AddrObjTableLock, handle);
+
+ //
+ // At this point, the protocol entry is installed, but no values
+ // are permitted. This is the safest default.
+ //
+
+ if (ConfigHandle != NULL) {
+ //
+ // Process the filter value list.
+ //
+ if (status == TDI_SUCCESS) {
+ for ( i=0;
+ EnumSecurityFilterValue(&filterList, i, &filterValue);
+ i++
+ )
+ {
+ AddValueSecurityFilter(InterfaceAddress, Protocol,
+ filterValue);
+ }
+ }
+ else if (status == TDI_ITEM_NOT_FOUND) {
+ //
+ // No filter registered, permit everything.
+ //
+ AddValueSecurityFilter(InterfaceAddress, Protocol, 0);
+ }
+ }
+
+ if (filterList.Buffer != NULL) {
+ CTEFreeMem(filterList.Buffer);
+ }
+
+ return;
+}
+
+
+//* DeleteProtocolSecurityFilter
+//
+// This routine disables security filtering for a specified protocol
+// on a specified IP interface.
+//
+// Entry: InterfaceAddress - The interface on which to disable the protocol.
+// (in network byte order)
+// Protocol - The protocol to disable.
+//
+// Returns: Nothing
+//
+void
+DeleteProtocolSecurityFilter(IPAddr InterfaceAddress, ulong Protocol)
+{
+ PINTERFACE_ENTRY ientry;
+ PPROTOCOL_ENTRY pentry;
+ CTELockHandle handle;
+ BOOLEAN deleteInterface = FALSE;
+
+
+ CTEGetLock(&AddrObjTableLock, &handle);
+
+ ientry = FindInterfaceEntry(InterfaceAddress);
+
+ if (ientry != NULL) {
+
+ CTEAssert(!EMPTYQ(&(ientry->ie_protocol_list)));
+
+ pentry = FindProtocolEntry(ientry, Protocol);
+
+ if (pentry != NULL) {
+ REMOVEQ(&(pentry->pe_link));
+ }
+
+ if (EMPTYQ(&(ientry->ie_protocol_list))) {
+ //
+ // Last protocol, delete interface as well.
+ //
+ REMOVEQ(&(ientry->ie_link));
+ deleteInterface = TRUE;
+ }
+
+ CTEFreeLock(&AddrObjTableLock, handle);
+
+ DeleteProtocolValueEntries(pentry);
+ CTEFreeMem(pentry);
+
+ if (deleteInterface) {
+ CTEAssert(EMPTYQ(&(ientry->ie_protocol_list)));
+ CTEFreeMem(ientry);
+ }
+ }
+ else {
+ CTEFreeLock(&AddrObjTableLock, handle);
+ }
+
+ return;
+}
+
+
+//* AddValueSecurityFilter - Add an entry.
+//
+// This routine adds a value entry for a specified protocol on a specified
+// interface in the security filter database.
+//
+//
+// Input: InterfaceAddress - The interface address to which to add.
+// (in network byte order)
+// Protocol - The protocol to which to add.
+// FilterValue - The transport value to add.
+// (in host byte order)
+//
+// Returns: A TDI status code:
+// TDI_INVALID_PARAMETER if the protocol is not in the database.
+// TDI_ADDR_INVALID if the interface is not in the database.
+// TDI_NO_RESOURCES if memory could not be allocated.
+// TDI_SUCCESS otherwise
+//
+// NOTES:
+//
+// This routine acquires AddrObjTableLock.
+//
+// Zero is a wildcard value. Supplying a zero value for the
+// InterfaceAddress and/or Protocol causes the operation to be applied
+// to all interfaces and/or protocols, as appropriate. Supplying a
+// non-zero value causes the operation to be applied to only the
+// specified interface and/or protocol. Supplying a FilterValue parameter
+// of zero causes all values to be acceptable. Any previously
+// registered values are deleted from the database.
+//
+TDI_STATUS
+AddValueSecurityFilter(IPAddr InterfaceAddress, ulong Protocol, ulong FilterValue)
+{
+ CTELockHandle handle;
+ TDI_STATUS status;
+
+
+ CTEGetLock(&AddrObjTableLock, &handle);
+
+ status = ModifySecurityFilter(ADD_VALUE_SECURITY_FILTER, InterfaceAddress,
+ Protocol, FilterValue);
+
+ CTEFreeLock(&AddrObjTableLock, handle);
+
+ return(status);
+}
+
+
+//* DeleteValueSecurityFilter - Delete an entry.
+//
+// This routine deletes a value entry for a specified protocol on a specified
+// interface in the security filter database.
+//
+//
+// Input: InterfaceAddress - The interface address from which to delete.
+// (in network byte order)
+// Protocol - The protocol from which to delete.
+// FilterValue - The transport value to delete.
+// (in host byte order)
+//
+// Returns: A TDI status code:
+// TDI_INVALID_PARAMETER if the protocol is not in the database.
+// TDI_ADDR_INVALID if the interface is not in the database.
+// TDI_NO_RESOURCES if memory could not be allocated.
+// TDI_SUCCESS otherwise
+//
+// NOTES:
+//
+// This routine acquires AddrObjTableLock.
+//
+// Zero is a wildcard value. Supplying a zero value for the
+// InterfaceAddress and/or Protocol causes the operation to be applied
+// to all interfaces and/or protocols, as appropriate. Supplying a
+// non-zero value causes the operation to be applied to only the
+// specified interface and/or protocol. Supplying a FilterValue parameter
+// of zero causes all values to be rejected. Any previously
+// registered values are deleted from the database.
+//
+TDI_STATUS
+DeleteValueSecurityFilter(IPAddr InterfaceAddress, ulong Protocol,
+ ulong FilterValue)
+{
+ CTELockHandle handle;
+ TDI_STATUS status;
+
+
+ CTEGetLock(&AddrObjTableLock, &handle);
+
+ status = ModifySecurityFilter(DELETE_VALUE_SECURITY_FILTER,
+ InterfaceAddress, Protocol, FilterValue);
+
+ CTEFreeLock(&AddrObjTableLock, handle);
+
+ return(status);
+}
+
+
+//* EnumerateSecurityFilters - Enumerate security filter database.
+//
+// This routine enumerates the contents of the security filter database
+// for the specified protocol and IP interface.
+//
+// Input: InterfaceAddress - The interface address to enumerate. A value
+// of zero means enumerate all interfaces.
+// (in network byte order)
+//
+// Protocol - The protocol to enumerate. A value of zero
+// means enumerate all protocols.
+//
+// Value - The Protocol value to enumerate. A value of
+// zero means enumerate all protocol values.
+// (in host byte order)
+//
+// Buffer - A pointer to a buffer into which to put
+// the returned filter entries.
+//
+// BufferSize - On input, the size in bytes of Buffer.
+// On output, the number of bytes written.
+//
+// EntriesAvailable - On output, the total number of filter entries
+// available in the database.
+//
+// Returns: A TDI status code:
+//
+// TDI_ADDR_INVALID if the address is not a valid IP interface.
+// TDI_SUCCESS otherwise.
+//
+// NOTES:
+//
+// This routine acquires AddrObjTableLock.
+//
+// Entries written to output buffer are in host byte order.
+//
+void
+EnumerateSecurityFilters(IPAddr InterfaceAddress, ulong Protocol,
+ ulong Value, uchar *Buffer, ulong BufferSize,
+ ulong *EntriesReturned, ulong *EntriesAvailable)
+{
+ PINTERFACE_ENTRY ientry;
+ TDI_STATUS status = TDI_SUCCESS;
+ CTELockHandle handle;
+
+
+ *EntriesAvailable = *EntriesReturned = 0;
+
+ CTEGetLock(&AddrObjTableLock, &handle);
+
+ if (InterfaceAddress == 0) {
+ struct Queue *qentry;
+
+ //
+ // Enumerate all interfaces.
+ //
+ for ( qentry = InterfaceEntryList.q_next;
+ qentry != &InterfaceEntryList;
+ qentry = qentry->q_next
+ )
+ {
+ ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link);
+
+ EnumerateInterfaceProtocols(
+ ientry,
+ Protocol,
+ Value,
+ &Buffer,
+ &BufferSize,
+ EntriesReturned,
+ EntriesAvailable
+ );
+ }
+ }
+ else {
+ //
+ // Enumerate a specific interface.
+ //
+
+ ientry = FindInterfaceEntry(InterfaceAddress);
+
+ if (ientry != NULL) {
+ EnumerateInterfaceProtocols(
+ ientry,
+ Protocol,
+ Value,
+ &Buffer,
+ &BufferSize,
+ EntriesReturned,
+ EntriesAvailable
+ );
+ }
+ }
+
+ CTEFreeLock(&AddrObjTableLock, handle);
+
+ return;
+}
+
+
+//* IsPermittedSecurityFilter
+//
+// This routine determines if communications addressed to
+// {Protocol, InterfaceAddress, Value} are permitted by the security filters.
+// It looks up the tuple in the security filter database.
+//
+// Input: InterfaceAddress - The IP interface address to check
+// (in network byte order)
+// IPContext - The IPContext value passed to the transport
+// Protocol - The protocol to check
+// Value - The value to check (in host byte order)
+//
+// Returns: A boolean indicating whether or not the communication is permitted.
+//
+// NOTES:
+//
+// This routine must be called with AddrObjTableLock held.
+//
+//
+BOOLEAN
+IsPermittedSecurityFilter(IPAddr InterfaceAddress, void *IPContext,
+ ulong Protocol, ulong FilterValue)
+{
+ PINTERFACE_ENTRY ientry;
+ PPROTOCOL_ENTRY pentry;
+ PVALUE_ENTRY ventry;
+ ulong hash_value;
+ struct Queue *qentry;
+
+
+ CTEAssert(Protocol <= 0xFF);
+
+ for ( qentry = InterfaceEntryList.q_next;
+ qentry != &InterfaceEntryList;
+ qentry = qentry->q_next
+ )
+ {
+ ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link);
+
+ if (ientry->ie_address == InterfaceAddress) {
+
+ for ( qentry = ientry->ie_protocol_list.q_next;
+ qentry != &(ientry->ie_protocol_list);
+ qentry = qentry->q_next
+ )
+ {
+ pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link);
+
+ if (pentry->pe_protocol == Protocol) {
+
+ if (pentry->pe_accept_all == TRUE) {
+ //
+ // All values accepted. Permit operation.
+ //
+ return(TRUE);
+ }
+
+ hash_value = VALUE_ENTRY_HASH(FilterValue);
+
+ for ( qentry = pentry->pe_entries[hash_value].q_next;
+ qentry != &(pentry->pe_entries[hash_value]);
+ qentry = qentry->q_next
+ )
+ {
+ ventry = STRUCT_OF(VALUE_ENTRY, qentry, ve_link);
+
+ if (ventry->ve_value == FilterValue) {
+ //
+ // Found it. Operation is permitted.
+ //
+ return(TRUE);
+ }
+ }
+
+ //
+ // {Interface, Protocol} protected, but no value found.
+ // Reject operation.
+ //
+ return(FALSE);
+ }
+ }
+
+ //
+ // Protocol not registered. Reject operation
+ //
+ return(FALSE);
+ }
+ }
+
+ //
+ // If this packet is on the loopback interface, let it through.
+ //
+ if (IP_LOOPBACK_ADDR(InterfaceAddress)) {
+ return(TRUE);
+ }
+
+ //
+ // Special check to allow the DHCP client to receive its packets.
+ // It is safe to make this check all the time because IP will
+ // not permit a packet to get through on an NTE with a zero address
+ // unless DHCP is in the process of configuring that NTE.
+ //
+ if ( (Protocol == PROTOCOL_UDP) &&
+ (FilterValue == DHCP_CLIENT_PORT) &&
+ (*LocalNetInfo.ipi_isdhcpinterface)(IPContext)
+ )
+ {
+ return(TRUE);
+ }
+
+ //
+ // Interface not registered. Deny operation.
+ //
+ return(FALSE);
+}
+
+
+#endif // SECFLTR
+
diff --git a/private/ntos/tdi/tcpip/tcp/secfltr.h b/private/ntos/tdi/tcpip/tcp/secfltr.h
new file mode 100644
index 000000000..ef05de497
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/secfltr.h
@@ -0,0 +1,61 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+#ifndef _SECFLTR_INCLUDED
+#define _SECFLTR_INCLUDED 1
+
+#ifdef SECFLTR
+
+
+//** SECFLTR.H - Security filtering support
+//
+// This file contains definitions related to security filtering.
+//
+
+//
+// Functions
+//
+extern void
+InitializeSecurityFilters(void);
+
+extern void
+CleanupSecurityFilters(void);
+
+extern uint
+IsSecurityFilteringEnabled(void);
+
+extern void
+ControlSecurityFiltering(uint IsEnabled);
+
+extern void
+AddProtocolSecurityFilter(IPAddr InterfaceAddress, ulong Protocol,
+ NDIS_HANDLE ConfigHandle);
+
+extern void
+DeleteProtocolSecurityFilter(IPAddr InterfaceAddress, ulong Protocol);
+
+extern TDI_STATUS
+AddValueSecurityFilter(IPAddr InterfaceAddress, ulong Protocol,
+ ulong FilterValue);
+
+extern TDI_STATUS
+DeleteValueSecurityFilter(IPAddr InterfaceAddress, ulong Protocol,
+ ulong FilterValue);
+
+extern void
+EnumerateSecurityFilters(IPAddr InterfaceAddress, ulong Protocol,
+ ulong Value, uchar *Buffer, ulong BufferSize,
+ ulong *EntriesReturned, ulong *EntriesAvailable);
+
+extern BOOLEAN
+IsPermittedSecurityFilter(IPAddr InterfaceAddress, void *IPContext,
+ ulong Protocol, ulong FilterValue);
+
+
+#endif // SECFLTR
+
+#endif // _SECFLTR_INCLUDED
+
diff --git a/private/ntos/tdi/tcpip/tcp/sources.inc b/private/ntos/tdi/tcpip/tcp/sources.inc
new file mode 100644
index 000000000..ad8aeff0f
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/sources.inc
@@ -0,0 +1,67 @@
+!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=tcp
+
+NTPROFILEINPUT=yes
+
+TARGETNAME=tcpip
+TARGETTYPE=EXPORT_DRIVER
+
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \
+ $(BASEDIR)\public\sdk\lib\*\ndis.lib \
+ $(TARGETLIBS)
+
+INCLUDES=..\..\..\..\inc;..\..\..\..\..\inc;..\..\h
+
+C_DEFINES=$(C_DEFINES) -DNT -D_NTDRIVER_ -DRASAUTODIAL -D_PNP_POWER -DSECFLTR -DFAST_RETRANSMIT=1
+
+!IFDEF BUILD_FOR_3_51
+C_DEFINES= $(C_DEFINES) -D_NTIFS_
+!ENDIF
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ ..\tcpip.rc \
+ ..\addr.c \
+ ..\dgram.c \
+ ..\info.c \
+ ..\init.c \
+ ..\ntdisp.c \
+ ..\ntinit.c \
+ ..\ntautodl.c \
+ ..\raw.c \
+ ..\secfltr.c \
+ ..\tcb.c \
+ ..\tcpconn.c \
+ ..\tcpdeb.c \
+ ..\tcpdeliv.c \
+ ..\tcprcv.c \
+ ..\tcpsend.c \
+ ..\tlcommon.c \
+ ..\udp.c
+
+DLLDEF=..\tcpip.def
diff --git a/private/ntos/tdi/tcpip/tcp/tcb.c b/private/ntos/tdi/tcpip/tcp/tcb.c
new file mode 100644
index 000000000..4b01c66d3
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcb.c
@@ -0,0 +1,1524 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCB.C - TCP TCB management code.
+//
+// This file contains the code for managing TCBs.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#ifdef VXD
+#include "tdivxd.h"
+#include "tdistat.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "tcp.h"
+#include "tcb.h"
+#include "tcpconn.h"
+#include "tcpsend.h"
+#include "tcprcv.h"
+#include "info.h"
+#include "tcpcfg.h"
+#include "tcpdeliv.h"
+#ifdef RASAUTODIAL
+#include <acd.h>
+#include <acdapi.h>
+#endif // RASAUTODIAL
+
+DEFINE_LOCK_STRUCTURE(TCBTableLock)
+
+uint TCPTime;
+uint TCBWalkCount;
+
+TCB *TCBTable[TCB_TABLE_SIZE];
+
+TCB *LastTCB;
+
+TCB *PendingFreeList;
+
+#ifndef NT
+TCB *FreeTCBList = NULL; // TCB free list
+#else
+SLIST_HEADER FreeTCBList;
+#endif
+
+DEFINE_LOCK_STRUCTURE(FreeTCBListLock) // Lock to protect TCB free list.
+
+EXTERNAL_LOCK(AddrObjTableLock)
+
+uint CurrentTCBs = 0;
+uint MaxTCBs = 0xffffffff;
+
+#ifdef NT
+#define MAX_FREE_TCBS 1000
+#else
+#define MAX_FREE_TCBS 10
+#endif
+
+#define NUM_DEADMAN_TICKS MS_TO_TICKS(1000)
+
+uint MaxFreeTCBs = MAX_FREE_TCBS;
+uint DeadmanTicks;
+
+CTETimer TCBTimer;
+
+extern IPInfo LocalNetInfo;
+
+//
+// All of the init code can be discarded.
+//
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+
+int InitTCB(void);
+void UnInitTCB(void);
+
+#pragma alloc_text(INIT, InitTCB)
+#pragma alloc_text(INIT, UnInitTCB)
+
+#endif // ALLOC_PRAGMA
+#endif
+
+#ifdef RASAUTODIAL
+extern ACD_DRIVER AcdDriverG;
+
+VOID
+TCPNoteNewConnection(
+ IN TCB *pTCB,
+ IN CTELockHandle Handle
+ );
+#endif // RASAUTODIAL
+
+
+//* ReadNextTCB - Read the next TCB in the table.
+//
+// Called to read the next TCB in the table. The needed information
+// is derived from the incoming context, which is assumed to be valid.
+// We'll copy the information, and then update the context value with
+// the next TCB to be read.
+//
+// Input: Context - Poiner to a TCPConnContext.
+// Buffer - Pointer to a TCPConnTableEntry structure.
+//
+// Returns: TRUE if more data is available to be read, FALSE is not.
+//
+uint
+ReadNextTCB(void *Context, void *Buffer)
+{
+ TCPConnContext *TCContext = (TCPConnContext *)Context;
+ TCPConnTableEntry *TCEntry = (TCPConnTableEntry *)Buffer;
+ CTELockHandle Handle;
+ TCB *CurrentTCB;
+ uint i;
+
+
+ CurrentTCB = TCContext->tcc_tcb;
+ CTEStructAssert(CurrentTCB, tcb);
+
+ CTEGetLock(&CurrentTCB->tcb_lock, &Handle);
+ if (CLOSING(CurrentTCB))
+ TCEntry->tct_state = TCP_CONN_CLOSED;
+ else
+ TCEntry->tct_state = (uint)CurrentTCB->tcb_state +
+ TCB_STATE_DELTA;
+ TCEntry->tct_localaddr = CurrentTCB->tcb_saddr;
+ TCEntry->tct_localport = CurrentTCB->tcb_sport;
+ TCEntry->tct_remoteaddr = CurrentTCB->tcb_daddr;
+ TCEntry->tct_remoteport = CurrentTCB->tcb_dport;
+ CTEFreeLock(&CurrentTCB->tcb_lock, Handle);
+
+ // We've filled it in. Now update the context.
+ if (CurrentTCB->tcb_next != NULL) {
+ TCContext->tcc_tcb = CurrentTCB->tcb_next;
+ return TRUE;
+ } else {
+ // NextTCB is NULL. Loop through the TCBTable looking for a new
+ // one.
+ i = TCContext->tcc_index + 1;
+ while (i < TCB_TABLE_SIZE) {
+ if (TCBTable[i] != NULL) {
+ TCContext->tcc_tcb = TCBTable[i];
+ TCContext->tcc_index = i;
+ return TRUE;
+ break;
+ } else
+ i++;
+ }
+
+ TCContext->tcc_index = 0;
+ TCContext->tcc_tcb = NULL;
+ return FALSE;
+ }
+
+}
+
+//* ValidateTCBContext - Validate the context for reading a TCB table.
+//
+// Called to start reading the TCB table sequentially. We take in
+// a context, and if the values are 0 we return information about the
+// first TCB in the table. Otherwise we make sure that the context value
+// is valid, and if it is we return TRUE.
+// We assume the caller holds the TCB table lock.
+//
+// Input: Context - Pointer to a TCPConnContext.
+// Valid - Where to return information about context being
+// valid.
+//
+// Returns: TRUE if data in table, FALSE if not. *Valid set to true if the
+// context is valid.
+//
+uint
+ValidateTCBContext(void *Context, uint *Valid)
+{
+ TCPConnContext *TCContext = (TCPConnContext *)Context;
+ uint i;
+ TCB *TargetTCB;
+ TCB *CurrentTCB;
+
+ i = TCContext->tcc_index;
+ TargetTCB = TCContext->tcc_tcb;
+
+ // If the context values are 0 and NULL, we're starting from the beginning.
+ if (i == 0 && TargetTCB == NULL) {
+ *Valid = TRUE;
+ do {
+ if ((CurrentTCB = TCBTable[i]) != NULL) {
+ CTEStructAssert(CurrentTCB, tcb);
+ break;
+ }
+ i++;
+ } while (i < TCB_TABLE_SIZE);
+
+ if (CurrentTCB != NULL) {
+ TCContext->tcc_index = i;
+ TCContext->tcc_tcb = CurrentTCB;
+ return TRUE;
+ } else
+ return FALSE;
+
+ } else {
+
+ // We've been given a context. We just need to make sure that it's
+ // valid.
+
+ if (i < TCB_TABLE_SIZE) {
+ CurrentTCB = TCBTable[i];
+ while (CurrentTCB != NULL) {
+ if (CurrentTCB == TargetTCB) {
+ *Valid = TRUE;
+ return TRUE;
+ break;
+ } else {
+ CurrentTCB = CurrentTCB->tcb_next;
+ }
+ }
+
+ }
+
+ // If we get here, we didn't find the matching TCB.
+ *Valid = FALSE;
+ return FALSE;
+
+ }
+
+}
+
+//* FindNextTCB - Find the next TCB in a particular chain.
+//
+// This routine is used to find the 'next' TCB in a chain. Since we keep
+// the chain in ascending order, we look for a TCB which is greater than
+// the input TCB. When we find one, we return it.
+//
+// This routine is mostly used when someone is walking the table and needs
+// to free the various locks to perform some action.
+//
+// Input: Index - Index into TCBTable
+// Current - Current TCB - we find the one after this one.
+//
+// Returns: Pointer to the next TCB, or NULL.
+//
+TCB *
+FindNextTCB(uint Index, TCB *Current)
+{
+ TCB *Next;
+
+ CTEAssert(Index < TCB_TABLE_SIZE);
+
+ Next = TCBTable[Index];
+
+ while (Next != NULL && (Next <= Current))
+ Next = Next->tcb_next;
+
+ return Next;
+}
+
+//* ResetSendNext - Set the sendnext value of a TCB.
+//
+// Called to set the send next value of a TCB. We do that, and adjust all
+// pointers to the appropriate places. We assume the caller holds the lock
+// on the TCB.
+//
+// Input: SeqTCB - Pointer to TCB to be updated.
+// NewSeq - Sequence number to set.
+//
+// Returns: Nothing.
+//
+void
+ResetSendNext(TCB *SeqTCB, SeqNum NewSeq)
+{
+ TCPSendReq *SendReq;
+ uint AmtForward;
+ Queue *CurQ;
+ PNDIS_BUFFER Buffer;
+ uint Offset;
+
+ CTEStructAssert(SeqTCB, tcb);
+ CTEAssert(SEQ_GTE(NewSeq, SeqTCB->tcb_senduna));
+
+ // The new seq must be less than send max, or NewSeq, senduna, sendnext,
+ // and sendmax must all be equal. (The latter case happens when we're
+ // called exiting TIME_WAIT, or possibly when we're retransmitting
+ // during a flow controlled situation).
+ CTEAssert(SEQ_LT(NewSeq, SeqTCB->tcb_sendmax) ||
+ (SEQ_EQ(SeqTCB->tcb_senduna, SeqTCB->tcb_sendnext) &&
+ SEQ_EQ(SeqTCB->tcb_senduna, SeqTCB->tcb_sendmax) &&
+ SEQ_EQ(SeqTCB->tcb_senduna, NewSeq)));
+
+ AmtForward = NewSeq - SeqTCB->tcb_senduna;
+
+ SeqTCB->tcb_sendnext = NewSeq;
+
+ // If we're backing off send next, turn off the FIN_OUTSTANDING flag to
+ // maintain a consistent state.
+ if (!SEQ_EQ(NewSeq, SeqTCB->tcb_sendmax))
+ SeqTCB->tcb_flags &= ~FIN_OUTSTANDING;
+
+ if (SYNC_STATE(SeqTCB->tcb_state) && SeqTCB->tcb_state != TCB_TIME_WAIT) {
+ // In these states we need to update the send queue.
+
+ if (!EMPTYQ(&SeqTCB->tcb_sendq)) {
+ CurQ = QHEAD(&SeqTCB->tcb_sendq);
+
+ SendReq = (TCPSendReq *)STRUCT_OF(TCPReq, CurQ, tr_q);
+
+ // SendReq points to the first send request on the send queue.
+ // Move forward AmtForward bytes on the send queue, and set the
+ // TCB pointers to the resultant SendReq, buffer, offset, size.
+ while (AmtForward) {
+
+ CTEStructAssert(SendReq, tsr);
+
+ if (AmtForward >= SendReq->tsr_unasize) {
+ // We're going to move completely past this one. Subtract
+ // his size from AmtForward and get the next one.
+
+ AmtForward -= SendReq->tsr_unasize;
+ CurQ = QNEXT(CurQ);
+ CTEAssert(CurQ != QEND(&SeqTCB->tcb_sendq));
+ SendReq = (TCPSendReq *)STRUCT_OF(TCPReq, CurQ, tr_q);
+ } else {
+ // We're pointing at the proper send req now. Break out
+ // of this loop and save the information. Further down
+ // we'll need to walk down the buffer chain to find
+ // the proper buffer and offset.
+ break;
+ }
+ }
+
+ // We're pointing at the proper send req now. We need to go down
+ // the buffer chain here to find the proper buffer and offset.
+ SeqTCB->tcb_cursend = SendReq;
+ SeqTCB->tcb_sendsize = SendReq->tsr_unasize - AmtForward;
+ Buffer = SendReq->tsr_buffer;
+ Offset = SendReq->tsr_offset;
+
+ while (AmtForward) {
+ // Walk the buffer chain.
+ uint Length;
+
+ // We'll need the length of this buffer. Use the portable
+ // macro to get it. We have to adjust the length by the offset
+ // into it, also.
+ CTEAssert((Offset < NdisBufferLength(Buffer)) ||
+ ((Offset == 0) && (NdisBufferLength(Buffer) == 0)));
+
+ Length = NdisBufferLength(Buffer) - Offset;
+
+ if (AmtForward >= Length) {
+ // We're moving past this one. Skip over him, and 0 the
+ // Offset we're keeping.
+
+ AmtForward -= Length;
+ Offset = 0;
+ Buffer = NDIS_BUFFER_LINKAGE(Buffer);
+ CTEAssert(Buffer != NULL);
+ } else
+ break;
+ }
+
+ // Save the buffer we found, and the offset into that buffer.
+ SeqTCB->tcb_sendbuf = Buffer;
+ SeqTCB->tcb_sendofs = Offset + AmtForward;
+
+ } else {
+ CTEAssert(SeqTCB->tcb_cursend == NULL);
+ CTEAssert(AmtForward == 0);
+ }
+
+ }
+
+ CheckTCBSends(SeqTCB);
+
+}
+
+#ifdef NT
+
+//* TCPAbortAndIndicateDisconnect
+//
+// Abortively closes a TCB and issues a disconnect indication up the the
+// transport user. This function is used to support cancellation of
+// TDI send and receive requests.
+//
+// Input: ConnectionContext - The connection ID to find a TCB for.
+//
+// Returns: Nothing.
+//
+void
+TCPAbortAndIndicateDisconnect(
+ uint ConnectionContext
+ )
+{
+ TCB *AbortTCB;
+ CTELockHandle ConnTableHandle, TCBHandle;
+ TCPConn *Conn;
+
+
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+
+ Conn = GetConnFromConnID(ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ AbortTCB = Conn->tc_tcb;
+
+ if (AbortTCB != NULL) {
+
+ // If it's CLOSING or CLOSED, skip it.
+ if ((AbortTCB->tcb_state != TCB_CLOSED) && !CLOSING(AbortTCB)) {
+ CTEStructAssert(AbortTCB, tcb);
+ CTEGetLock(&AbortTCB->tcb_lock, &TCBHandle);
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+
+ AbortTCB->tcb_refcnt++;
+ AbortTCB->tcb_flags |= NEED_RST; // send a reset if connected
+ TryToCloseTCB(AbortTCB, TCB_CLOSE_ABORTED, ConnTableHandle);
+
+ RemoveTCBFromConn(AbortTCB);
+
+ IF_TCPDBG(TCP_DEBUG_IRP) {
+ TCPTRACE((
+ "TCPAbortAndIndicateDisconnect, indicating discon\n"
+ ));
+ }
+
+ NotifyOfDisc(AbortTCB, NULL, TDI_CONNECTION_ABORTED);
+
+ CTEGetLock(&AbortTCB->tcb_lock, &TCBHandle);
+ DerefTCB(AbortTCB, TCBHandle);
+
+ // TCB lock freed by DerefTCB.
+
+ return;
+ }
+ }
+ }
+
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+}
+
+#endif // NT
+
+
+//* TCBTimeout - Do timeout events on TCBs.
+//
+// Called every MS_PER_TICKS milliseconds to do timeout processing on TCBs.
+// We run throught the TCB table, decrementing timers. If one goes to zero
+// we look at it's state to decide what to do.
+//
+// Input: Timer - Event structure for timer that fired.
+// Context - Context for timer (NULL in this case.
+//
+// Returns: Nothing.
+//
+void
+TCBTimeout(CTEEvent *Timer, void *Context)
+{
+ CTELockHandle TableHandle, TCBHandle;
+ uint i;
+ TCB *CurrentTCB;
+ uint Delayed = FALSE;
+ uint CallRcvComplete;
+
+
+ // Update our free running counter.
+
+ TCPTime++;
+
+ CTEInterlockedAddUlong(&TCBWalkCount, 1, &TCBTableLock);
+
+
+#ifndef VXD
+ TCBHandle = DISPATCH_LEVEL;
+#endif
+
+ // Loop through each bucket in the table, going down the chain of
+ // TCBs on the bucket.
+ for (i = 0; i < TCB_TABLE_SIZE; i++) {
+ TCB *TempTCB;
+ uint maxRexmitCnt;
+
+ CurrentTCB = TCBTable[i];
+
+ while (CurrentTCB != NULL) {
+ CTEStructAssert(CurrentTCB, tcb);
+ CTEGetLockAtDPC(&CurrentTCB->tcb_lock, &TCBHandle);
+
+ // If it's CLOSING or CLOSED, skip it.
+ if (CurrentTCB->tcb_state == TCB_CLOSED || CLOSING(CurrentTCB)) {
+
+ TempTCB = CurrentTCB->tcb_next;
+ CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle);
+ CurrentTCB = TempTCB;
+ continue;
+ }
+
+ CheckTCBSends(CurrentTCB);
+ CheckTCBRcv(CurrentTCB);
+
+ // First check the rexmit timer.
+ if (TCB_TIMER_RUNNING(CurrentTCB->tcb_rexmittimer)) {
+ // The timer is running.
+ if (--(CurrentTCB->tcb_rexmittimer) == 0) {
+
+ // And it's fired. Figure out what to do now.
+
+ // If we've had too many retransits, abort now.
+ CurrentTCB->tcb_rexmitcnt++;
+
+ if (CurrentTCB->tcb_state == TCB_SYN_SENT) {
+ maxRexmitCnt = MaxConnectRexmitCount;
+ }
+ else {
+ if (CurrentTCB->tcb_state == TCB_SYN_RCVD) {
+#ifdef SYN_ATTACK
+ //
+ // Save on locking. Though MaxConnectRexmitCountTmp may
+ // be changing, we are assured that we will not use
+ // more than the MaxConnectRexmitCount.
+ //
+ maxRexmitCnt = MIN(MaxConnectResponseRexmitCountTmp, MaxConnectResponseRexmitCount);
+#else
+ maxRexmitCnt = MaxConnectResponseRexmitCount;
+
+#endif
+ }
+ else {
+ maxRexmitCnt = MaxDataRexmitCount;
+ }
+ }
+
+ // If we've run out of retransmits or we're in FIN_WAIT2,
+ // time out.
+ if (CurrentTCB->tcb_rexmitcnt > maxRexmitCnt) {
+
+ CTEAssert(CurrentTCB->tcb_state > TCB_LISTEN);
+
+ // This connection has timed out. Abort it. First
+ // reference him, then mark as closed, notify the
+ // user, and finally dereference and close him.
+
+TimeoutTCB:
+ CurrentTCB->tcb_refcnt++;
+ TryToCloseTCB(CurrentTCB, TCB_CLOSE_TIMEOUT, TCBHandle);
+
+ RemoveTCBFromConn(CurrentTCB);
+ NotifyOfDisc(CurrentTCB, NULL, TDI_TIMED_OUT);
+
+#ifdef SYN_ATTACK
+ if (SynAttackProtect) {
+
+ CTELockHandle Handle;
+
+ CTEGetLockAtDPC(&SynAttLock, &Handle);
+ //
+ // We have put the connection in the closed state.
+ // Decrement the counters for keeping track of half
+ // open connections
+ //
+ CTEAssert((TCPHalfOpen > 0) && (TCPHalfOpenRetried > 0));
+ TCPHalfOpen--;
+ TCPHalfOpenRetried--;
+ CTEFreeLockFromDPC(&SynAttLock, Handle);
+ }
+#endif
+
+ CTEGetLockAtDPC(&CurrentTCB->tcb_lock, &TCBHandle);
+ DerefTCB(CurrentTCB, TCBHandle);
+
+ CurrentTCB = FindNextTCB(i, CurrentTCB);
+ continue;
+ }
+
+ CurrentTCB->tcb_rtt = 0; // Stop round trip time
+ // measurement.
+
+
+ // Figure out what our new retransmit timeout should be. We
+ // double it each time we get a retransmit, and reset it
+ // back when we get an ack for new data.
+ CurrentTCB->tcb_rexmit = MIN(CurrentTCB->tcb_rexmit << 1,
+ MAX_REXMIT_TO);
+
+ // Reset the sequence number, and reset the congestion
+ // window.
+ ResetSendNext(CurrentTCB, CurrentTCB->tcb_senduna);
+
+ if (!(CurrentTCB->tcb_flags & FLOW_CNTLD)) {
+ // Don't let the slow start threshold go below 2
+ // segments
+ CurrentTCB->tcb_ssthresh =
+ MAX(
+ MIN(
+ CurrentTCB->tcb_cwin,
+ CurrentTCB->tcb_sendwin
+ ) / 2,
+ (uint) CurrentTCB->tcb_mss * 2
+ );
+ CurrentTCB->tcb_cwin = CurrentTCB->tcb_mss;
+ } else {
+ // We're probing, and the probe timer has fired. We
+ // need to set the FORCE_OUTPUT bit here.
+ CurrentTCB->tcb_flags |= FORCE_OUTPUT;
+ }
+
+ // See if we need to probe for a PMTU black hole.
+ if (PMTUBHDetect &&
+ CurrentTCB->tcb_rexmitcnt == ((maxRexmitCnt+1)/2)) {
+ // We may need to probe for a black hole. If we're
+ // doing MTU discovery on this connection and we
+ // are retransmitting more than a minimum segment
+ // size, or we are probing for a PMTU BH already, turn
+ // off the DF flag and bump the probe count. If the
+ // probe count gets too big we'll assume it's not
+ // a PMTU black hole, and we'll try to switch the
+ // router.
+ if ((CurrentTCB->tcb_flags & PMTU_BH_PROBE) ||
+ ((CurrentTCB->tcb_opt.ioi_flags & IP_FLAG_DF) &&
+ (CurrentTCB->tcb_sendmax - CurrentTCB->tcb_senduna)
+ > 8)) {
+ // May need to probe. If we haven't exceeded our
+ // probe count, do so, otherwise restore those
+ // values.
+ if (CurrentTCB->tcb_bhprobecnt++ < 2) {
+
+ // We're going to probe. Turn on the flag,
+ // drop the MSS, and turn off the don't
+ // fragment bit.
+ if (!(CurrentTCB->tcb_flags & PMTU_BH_PROBE)) {
+ CurrentTCB->tcb_flags |= PMTU_BH_PROBE;
+ CurrentTCB->tcb_slowcount++;
+ CurrentTCB->tcb_fastchk |= TCP_FLAG_SLOW;
+
+ // Drop the MSS to the minimum. Save the old
+ // one in case we need it later.
+ CurrentTCB->tcb_mss = MIN(MAX_REMOTE_MSS -
+ CurrentTCB->tcb_opt.ioi_optlength,
+ CurrentTCB->tcb_remmss);
+
+ CTEAssert(CurrentTCB->tcb_mss > 0);
+
+ CurrentTCB->tcb_cwin = CurrentTCB->tcb_mss;
+ CurrentTCB->tcb_opt.ioi_flags &= ~IP_FLAG_DF;
+ }
+
+ // Drop the rexmit count so we come here again,
+ // and don't retrigger DeadGWDetect.
+
+ CurrentTCB->tcb_rexmitcnt--;
+ } else {
+ // Too many probes. Stop probing, and allow fallover
+ // to the next gateway.
+ //
+ // Currently this code won't do BH probing on the 2nd
+ // gateway. The MSS will stay at the minimum size. This
+ // might be a little suboptimal, but it's
+ // easy to implement for the Sept. 95 service pack
+ // and will keep connections alive if possible.
+ //
+ // In the future we should investigate doing
+ // dead g/w detect on a per-connection basis, and then
+ // doing PMTU probing for each connection.
+
+ if (CurrentTCB->tcb_flags & PMTU_BH_PROBE) {
+ CurrentTCB->tcb_flags &= ~PMTU_BH_PROBE;
+ if (--(CurrentTCB->tcb_slowcount) == 0)
+ CurrentTCB->tcb_fastchk &=
+ ~TCP_FLAG_SLOW;
+
+ }
+ CurrentTCB->tcb_bhprobecnt = 0;
+ }
+ }
+ }
+
+ // Check to see if we're doing dead gateway detect. If we
+ // are, see if it's time to ask IP.
+ if (DeadGWDetect &&
+ (CurrentTCB->tcb_rexmitcnt == ((maxRexmitCnt+1)/2))) {
+ (*LocalNetInfo.ipi_checkroute)(CurrentTCB->tcb_daddr,
+ CurrentTCB->tcb_saddr);
+ }
+
+ // Now handle the various cases.
+ switch (CurrentTCB->tcb_state) {
+
+ // In SYN-SENT or SYN-RCVD we'll need to retransmit
+ // the SYN.
+ case TCB_SYN_SENT:
+ case TCB_SYN_RCVD:
+ SendSYN(CurrentTCB, TCBHandle);
+ CurrentTCB = FindNextTCB(i, CurrentTCB);
+ continue;
+
+ case TCB_FIN_WAIT1:
+ case TCB_CLOSING:
+ case TCB_LAST_ACK:
+ // The call to ResetSendNext (above) will have
+ // turned off the FIN_OUTSTANDING flag.
+ CurrentTCB->tcb_flags |= FIN_NEEDED;
+ case TCB_CLOSE_WAIT:
+ case TCB_ESTAB:
+ // In this state we have data to retransmit, unless
+ // the window is zero (in which case we need to
+ // probe), or we're just sending a FIN.
+
+ CheckTCBSends(CurrentTCB);
+
+ Delayed = TRUE;
+ DelayAction(CurrentTCB, NEED_OUTPUT);
+ break;
+
+
+ // If it's fired in TIME-WAIT, we're all done and
+ // can clean up. We'll call TryToCloseTCB even
+ // though he's already sort of closed. TryToCloseTCB
+ // will figure this out and do the right thing.
+ case TCB_TIME_WAIT:
+ TryToCloseTCB(CurrentTCB, TCB_CLOSE_SUCCESS,
+ TCBHandle);
+ CurrentTCB = FindNextTCB(i, CurrentTCB);
+ continue;
+ default:
+ break;
+ }
+ }
+ }
+
+
+ // Now check the SWS deadlock timer..
+ if (TCB_TIMER_RUNNING(CurrentTCB->tcb_swstimer)) {
+ // The timer is running.
+ if (--(CurrentTCB->tcb_swstimer) == 0) {
+ // And it's fired. Force output now.
+
+ CurrentTCB->tcb_flags |= FORCE_OUTPUT;
+ Delayed = TRUE;
+ DelayAction(CurrentTCB, NEED_OUTPUT);
+ }
+ }
+
+ // Check the push data timer.
+ if (TCB_TIMER_RUNNING(CurrentTCB->tcb_pushtimer)) {
+ // The timer is running. Decrement it.
+ if (--(CurrentTCB->tcb_pushtimer) == 0) {
+ // It's fired.
+ PushData(CurrentTCB);
+ Delayed = TRUE;
+ }
+ }
+
+ // Check the delayed ack timer.
+ if (TCB_TIMER_RUNNING(CurrentTCB->tcb_delacktimer)) {
+ // The timer is running.
+ if (--(CurrentTCB->tcb_delacktimer) == 0) {
+ // And it's fired. Set up to send an ACK.
+
+ Delayed = TRUE;
+ DelayAction(CurrentTCB, NEED_ACK);
+ }
+
+ }
+
+ // Finally check the keepalive timer.
+ if (CurrentTCB->tcb_state == TCB_ESTAB) {
+ if (CurrentTCB->tcb_flags & KEEPALIVE) {
+ uint Delta;
+
+ Delta = TCPTime - CurrentTCB->tcb_alive;
+ if (Delta > KeepAliveTime) {
+ Delta -= KeepAliveTime;
+ if (Delta > (CurrentTCB->tcb_kacount * KAInterval)) {
+ if (CurrentTCB->tcb_kacount < MaxDataRexmitCount) {
+ SendKA(CurrentTCB, TCBHandle);
+ CurrentTCB = FindNextTCB(i, CurrentTCB);
+ continue;
+ } else
+ goto TimeoutTCB;
+ }
+ } else
+ CurrentTCB->tcb_kacount = 0;
+ }
+ }
+
+
+
+ // If this is an active open connection in SYN-SENT or SYN-RCVD,
+ // or we have a FIN pending, check the connect timer.
+ if (CurrentTCB->tcb_flags & (ACTIVE_OPEN | FIN_NEEDED | FIN_SENT)) {
+ TCPConnReq *ConnReq = CurrentTCB->tcb_connreq;
+
+ CTEAssert(ConnReq != NULL);
+ if (TCB_TIMER_RUNNING(ConnReq->tcr_timeout)) {
+ // Timer is running.
+ if (--(ConnReq->tcr_timeout) == 0) {
+ // The connection timer has timed out.
+ TryToCloseTCB(CurrentTCB, TCB_CLOSE_TIMEOUT,
+ TCBHandle);
+ CurrentTCB = FindNextTCB(i, CurrentTCB);
+ continue;
+ }
+ }
+ }
+
+#ifdef RASAUTODIAL
+ //
+ // Check to see if we have to notify the
+ // automatic connection driver about this
+ // connection.
+ //
+ if (CurrentTCB->tcb_flags & ACD_CONN_NOTIF) {
+ BOOLEAN fEnabled;
+ CTELockHandle AcdHandle;
+
+ //
+ // Clear the ACD_CONN_NOTIF flag
+ // and release the TCB table lock.
+ //
+ CurrentTCB->tcb_flags &= ~ACD_CONN_NOTIF;
+ //
+ // Determine if we need to notify
+ // the automatic connection driver.
+ //
+ CTEGetLockAtDPC(&AcdDriverG.SpinLock, &AcdHandle);
+ fEnabled = AcdDriverG.fEnabled;
+ CTEFreeLockFromDPC(&AcdDriverG.SpinLock, AcdHandle);
+ if (fEnabled)
+ TCPNoteNewConnection(CurrentTCB, TCBHandle);
+ else
+ CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle);
+ //
+ // Reacquire the TCB table lock
+ // and get the next TCB to process.
+ //
+ CurrentTCB = FindNextTCB(i, CurrentTCB);
+ continue;
+ }
+#endif // RASAUTODIAL
+
+ // Timer isn't running, or didn't fire.
+ TempTCB = CurrentTCB->tcb_next;
+ CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle);
+ CurrentTCB = TempTCB;
+ }
+ }
+
+
+ // See if we need to call receive complete as part of deadman processing.
+ // We do this now because we want to restart the timer before calling
+ // receive complete, in case that takes a while. If we make this check
+ // while the timer is running we'd have to lock, so we'll check and save
+ // the result now before we start the timer.
+ if (DeadmanTicks == TCPTime) {
+ CallRcvComplete = TRUE;
+ DeadmanTicks += NUM_DEADMAN_TICKS;
+ } else
+ CallRcvComplete = FALSE;
+
+
+ // Now check the pending free list. If it's not null, walk down the
+ // list and decrement the walk count. If the count goes below 2, pull it
+ // from the list. If the count goes to 0, free the TCB. If the count is
+ // at 1 it'll be freed by whoever called RemoveTCB.
+
+ CTEGetLockAtDPC(&TCBTableLock, &TableHandle);
+ if (PendingFreeList != NULL) {
+ TCB *PrevTCB;
+
+ PrevTCB = STRUCT_OF(TCB, &PendingFreeList, tcb_delayq.q_next);
+
+ do {
+ CurrentTCB = (TCB *)PrevTCB->tcb_delayq.q_next;
+
+ CTEStructAssert(CurrentTCB, tcb);
+
+ CurrentTCB->tcb_walkcount--;
+ if (CurrentTCB->tcb_walkcount <= 1) {
+ *(TCB **)&PrevTCB->tcb_delayq.q_next =
+ (TCB *)CurrentTCB->tcb_delayq.q_next;
+
+ if (CurrentTCB->tcb_walkcount == 0) {
+ FreeTCB(CurrentTCB);
+ }
+ } else {
+ PrevTCB = CurrentTCB;
+ }
+ } while (PrevTCB->tcb_delayq.q_next != NULL);
+ }
+
+ TCBWalkCount--;
+ CTEFreeLockFromDPC(&TCBTableLock, TableHandle);
+
+ // Do AddrCheckTable cleanup
+
+ if (AddrCheckTable) {
+
+ TCPAddrCheckElement *Temp;
+
+ CTEGetLockAtDPC(&AddrObjTableLock, &TableHandle);
+
+ for (Temp=AddrCheckTable; Temp<AddrCheckTable+NTWMaxConnectCount; Temp++) {
+ if (Temp->TickCount > 0) {
+ if ((--(Temp->TickCount)) == 0) {
+ Temp->SourceAddress = 0;
+ }
+ }
+ }
+
+ CTEFreeLockFromDPC(&AddrObjTableLock, TableHandle);
+ }
+
+ // Restart the timer again.
+ CTEStartTimer(&TCBTimer, MS_PER_TICK, TCBTimeout, NULL);
+
+ if (Delayed)
+ ProcessTCBDelayQ();
+
+ if (CallRcvComplete)
+ TCPRcvComplete();
+
+}
+
+//* SetTCBMTU - Set TCB MTU values.
+//
+// A function called by TCBWalk to set the MTU values of all TCBs using
+// a particular path.
+//
+// Input: CheckTCB - TCB to be checked.
+// DestPtr - Ptr to destination address.
+// SrcPtr - Ptr to source address.
+// MTUPtr - Ptr to new MTU.
+//
+// Returns: TRUE.
+//
+uint
+SetTCBMTU(TCB *CheckTCB, void *DestPtr, void *SrcPtr, void *MTUPtr)
+{
+ IPAddr DestAddr = *(IPAddr *)DestPtr;
+ IPAddr SrcAddr = *(IPAddr *)SrcPtr;
+ CTELockHandle TCBHandle;
+
+ CTEStructAssert(CheckTCB, tcb);
+
+ CTEGetLock(&CheckTCB->tcb_lock, &TCBHandle);
+
+ if (IP_ADDR_EQUAL(CheckTCB->tcb_daddr,DestAddr) &&
+ IP_ADDR_EQUAL(CheckTCB->tcb_saddr,SrcAddr) &&
+ (CheckTCB->tcb_opt.ioi_flags & IP_FLAG_DF)) {
+ uint MTU = *(uint *)MTUPtr - CheckTCB->tcb_opt.ioi_optlength;;
+
+ CheckTCB->tcb_mss = (ushort)MIN(MTU, (uint)CheckTCB->tcb_remmss);
+
+ CTEAssert(CheckTCB->tcb_mss > 0);
+
+ //
+ // Reset the Congestion Window if necessary
+ //
+ if (CheckTCB->tcb_cwin < CheckTCB->tcb_mss) {
+ CheckTCB->tcb_cwin = CheckTCB->tcb_mss;
+
+ //
+ // Make sure the slow start threshold is at least
+ // 2 segments
+ //
+ if ( CheckTCB->tcb_ssthresh <
+ ((uint) CheckTCB->tcb_mss*2)
+ ) {
+ CheckTCB->tcb_ssthresh = CheckTCB->tcb_mss * 2;
+ }
+ }
+ }
+
+ CTEFreeLock(&CheckTCB->tcb_lock, TCBHandle);
+
+ return TRUE;
+}
+
+
+//* DeleteTCBWithSrc - Delete tcbs with a particular src address.
+//
+// A function called by TCBWalk to delete all TCBs with a particular source
+// address.
+//
+// Input: CheckTCB - TCB to be checked.
+// AddrPtr - Ptr to address.
+//
+// Returns: FALSE if CheckTCB is to be deleted, TRUE otherwise.
+//
+uint
+DeleteTCBWithSrc(TCB *CheckTCB, void *AddrPtr, void *Unused1, void *Unused3)
+{
+ IPAddr Addr = *(IPAddr *)AddrPtr;
+
+ CTEStructAssert(CheckTCB, tcb);
+
+ if (IP_ADDR_EQUAL(CheckTCB->tcb_saddr,Addr))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+//* TCBWalk - Walk the TCBs in the table, and call a function for each of them.
+//
+// Called when we need to repetively do something to each TCB in the table.
+// We call the specified function with a pointer to the TCB and the input
+// context for each TCB in the table. If the function returns FALSE, we
+// delete the TCB.
+//
+// Input: CallRtn - Routine to be called.
+// Context1 - Context to pass to CallRtn.
+// Context2 - Second context to pass to call routine.
+// Context3 - Third context to pass to call routine.
+//
+// Returns: Nothing.
+//
+void
+TCBWalk(uint (*CallRtn)(struct TCB *, void *, void *, void *), void *Context1,
+ void *Context2, void *Context3)
+{
+ uint i;
+ TCB *CurTCB;
+ CTELockHandle Handle, TCBHandle;
+
+ // Loop through each bucket in the table, going down the chain of
+ // TCBs on the bucket. For each one call CallRtn.
+ CTEGetLock(&TCBTableLock, &Handle);
+
+ for (i = 0; i < TCB_TABLE_SIZE; i++) {
+
+ CurTCB = TCBTable[i];
+
+ // Walk down the chain on this bucket.
+ while (CurTCB != NULL) {
+ if (!(*CallRtn)(CurTCB, Context1, Context2, Context3)) {
+ // He failed the call. Notify the client and close the
+ // TCB.
+ CTEGetLock(&CurTCB->tcb_lock, &TCBHandle);
+ if (!CLOSING(CurTCB)) {
+ CurTCB->tcb_refcnt++;
+ CTEFreeLock(&TCBTableLock, TCBHandle);
+ TryToCloseTCB(CurTCB, TCB_CLOSE_ABORTED, Handle);
+
+ RemoveTCBFromConn(CurTCB);
+ if (CurTCB->tcb_state != TCB_TIME_WAIT)
+ NotifyOfDisc(CurTCB, NULL, TDI_CONNECTION_ABORTED);
+
+ CTEGetLock(&CurTCB->tcb_lock, &TCBHandle);
+ DerefTCB(CurTCB, TCBHandle);
+ CTEGetLock(&TCBTableLock, &Handle);
+ } else
+ CTEFreeLock(&CurTCB->tcb_lock, TCBHandle);
+
+ CurTCB = FindNextTCB(i, CurTCB);
+ } else {
+ CurTCB = CurTCB->tcb_next;
+ }
+ }
+ }
+
+ CTEFreeLock(&TCBTableLock, Handle);
+}
+
+
+//* FindTCB - Find a TCB in the tcb table.
+//
+// Called when we need to find a TCB in the TCB table. We take a quick
+// look at the last TCB we found, and if it matches we return it. Otherwise
+// we hash into the TCB table and look for it. We assume the TCB table lock
+// is held when we are called.
+//
+// Input: Src - Source IP address of TCB to be found.
+// Dest - Dest. "" "" "" "" "" "" ""
+// DestPort - Destination port of TCB to be found.
+// SrcPort - Source port of TCB to be found.
+//
+// Returns: Pointer to TCB found, or NULL if none.
+//
+TCB *
+FindTCB(IPAddr Src, IPAddr Dest, ushort DestPort, ushort SrcPort)
+{
+ TCB *FoundTCB;
+
+ if (LastTCB != NULL) {
+ CTEStructAssert(LastTCB, tcb);
+ if (IP_ADDR_EQUAL(LastTCB->tcb_daddr, Dest) &&
+ LastTCB->tcb_dport == DestPort &&
+ IP_ADDR_EQUAL(LastTCB->tcb_saddr, Src) &&
+ LastTCB->tcb_sport == SrcPort)
+ return LastTCB;
+ }
+
+ // Didn't find it in our 1 element cache.
+ FoundTCB = TCBTable[TCB_HASH(Dest, Src, DestPort, SrcPort)];
+ while (FoundTCB != NULL) {
+ CTEStructAssert(FoundTCB, tcb);
+ if (IP_ADDR_EQUAL(FoundTCB->tcb_daddr, Dest) &&
+ FoundTCB->tcb_dport == DestPort &&
+ IP_ADDR_EQUAL(FoundTCB->tcb_saddr, Src) &&
+ FoundTCB->tcb_sport == SrcPort) {
+
+ // Found it. Update the cache for next time, and return.
+ LastTCB = FoundTCB;
+ return FoundTCB;
+ } else
+ FoundTCB = FoundTCB->tcb_next;
+ }
+
+ return FoundTCB;
+
+
+}
+
+
+//* InsertTCB - Insert a TCB in the tcb table.
+//
+// This routine inserts a TCB in the TCB table. No locks need to be held
+// when this routine is called. We insert TCBs in ascending address order.
+// Before inserting we make sure that the TCB isn't already in the table.
+//
+// Input: NewTCB - TCB to be inserted.
+//
+// Returns: TRUE if we inserted, false if we didn't.
+//
+uint
+InsertTCB(TCB *NewTCB)
+{
+ uint TCBIndex;
+ CTELockHandle TableHandle, TCBHandle;
+ TCB *PrevTCB, *CurrentTCB;
+ TCB *WhereToInsert;
+
+ CTEAssert(NewTCB != NULL);
+ CTEStructAssert(NewTCB, tcb);
+ TCBIndex = TCB_HASH(NewTCB->tcb_daddr, NewTCB->tcb_saddr,
+ NewTCB->tcb_dport, NewTCB->tcb_sport);
+
+ CTEGetLock(&TCBTableLock, &TableHandle);
+ CTEGetLockAtDPC(&NewTCB->tcb_lock, &TCBHandle);
+
+ // Find the proper place in the table to insert him. While
+ // we're walking we'll check to see if a dupe already exists.
+ // When we find the right place to insert, we'll remember it, and
+ // keep walking looking for a duplicate.
+
+ PrevTCB = STRUCT_OF(TCB, &TCBTable[TCBIndex], tcb_next);
+ WhereToInsert = NULL;
+
+ while (PrevTCB->tcb_next != NULL) {
+ CurrentTCB = PrevTCB->tcb_next;
+
+ if (IP_ADDR_EQUAL(CurrentTCB->tcb_daddr, NewTCB->tcb_daddr) &&
+ IP_ADDR_EQUAL(CurrentTCB->tcb_saddr, NewTCB->tcb_saddr) &&
+ (CurrentTCB->tcb_sport == NewTCB->tcb_sport) &&
+ (CurrentTCB->tcb_dport == NewTCB->tcb_dport)) {
+
+ CTEFreeLockFromDPC(&NewTCB->tcb_lock, TCBHandle);
+ CTEFreeLock(&TCBTableLock, TableHandle);
+ return FALSE;
+
+ } else {
+
+ if (WhereToInsert == NULL && CurrentTCB > NewTCB) {
+ WhereToInsert = PrevTCB;
+ }
+
+ CTEStructAssert(PrevTCB->tcb_next, tcb);
+ PrevTCB = PrevTCB->tcb_next;
+ }
+
+ }
+
+ if (WhereToInsert == NULL) {
+ WhereToInsert = PrevTCB;
+ }
+
+ NewTCB->tcb_next = WhereToInsert->tcb_next;
+ WhereToInsert->tcb_next = NewTCB;
+ NewTCB->tcb_flags |= IN_TCB_TABLE;
+ TStats.ts_numconns++;
+
+ CTEFreeLockFromDPC(&NewTCB->tcb_lock, TCBHandle);
+ CTEFreeLock(&TCBTableLock, TableHandle);
+ return TRUE;
+
+}
+
+//* RemoveTCB - Remove a TCB from the tcb table.
+//
+// Called when we need to remove a TCB from the TCB table. We assume the
+// TCB table lock and the TCB lock are held when we are called. If the
+// TCB isn't in the table we won't try to remove him.
+//
+// Input: RemovedTCB - TCB to be removed.
+//
+// Returns: TRUE if it's OK to free it, FALSE otherwise.
+//
+uint
+RemoveTCB(TCB *RemovedTCB)
+{
+ uint TCBIndex;
+ TCB *PrevTCB;
+#ifdef DEBUG
+ uint Found = FALSE;
+#endif
+
+ CTEStructAssert(RemovedTCB, tcb);
+
+ if (RemovedTCB->tcb_flags & IN_TCB_TABLE) {
+ TCBIndex = TCB_HASH(RemovedTCB->tcb_daddr, RemovedTCB->tcb_saddr,
+ RemovedTCB->tcb_dport, RemovedTCB->tcb_sport);
+
+ PrevTCB = STRUCT_OF(TCB, &TCBTable[TCBIndex], tcb_next);
+
+ do {
+ if (PrevTCB->tcb_next == RemovedTCB) {
+ // Found him.
+ PrevTCB->tcb_next = RemovedTCB->tcb_next;
+ RemovedTCB->tcb_flags &= ~IN_TCB_TABLE;
+ TStats.ts_numconns--;
+#ifdef DEBUG
+ Found = TRUE;
+#endif
+ break;
+ }
+ PrevTCB = PrevTCB->tcb_next;
+#ifdef DEBUG
+ if (PrevTCB != NULL)
+ CTEStructAssert(PrevTCB, tcb);
+#endif
+ } while (PrevTCB != NULL);
+
+ CTEAssert(Found);
+
+ }
+
+ if (LastTCB == RemovedTCB)
+ LastTCB = NULL;
+
+ if (TCBWalkCount == 0) {
+ return TRUE;
+ } else {
+ RemovedTCB->tcb_walkcount = TCBWalkCount + 1;
+ *(TCB **)&RemovedTCB->tcb_delayq.q_next = PendingFreeList;
+ PendingFreeList = RemovedTCB;
+ return FALSE;
+
+ }
+
+
+
+}
+
+
+//* ScavengeTCB - Scavenge a TCB that's in the TIME_WAIT state.
+//
+// Called when we're running low on TCBs, and need to scavenge one from
+// TIME_WAIT state. We'll walk through the TCB table, looking for the oldest
+// TCB in TIME_WAIT. We'll remove and return a pointer to that TCB. If we
+// don't find any TCBs in TIME_WAIT, we'll return NULL.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to a reusable TCB, or NULL.
+//
+TCB *
+ScavengeTCB(void)
+{
+ CTELockHandle TableHandle, TCBHandle, FoundLock;
+ uint Now = CTESystemUpTime();
+ uint Delta = 0;
+ uint i;
+ TCB *FoundTCB = NULL, *PrevFound;
+ TCB *CurrentTCB, *PrevTCB;
+
+ CTEGetLock(&TCBTableLock, &TableHandle);
+
+ if (TCBWalkCount != 0) {
+ CTEFreeLock(&TCBTableLock, TableHandle);
+ return NULL;
+ }
+
+ for (i = 0; i < TCB_TABLE_SIZE; i++) {
+
+ PrevTCB = STRUCT_OF(TCB, &TCBTable[i], tcb_next);
+ CurrentTCB = PrevTCB->tcb_next;
+
+ while (CurrentTCB != NULL) {
+ CTEStructAssert(CurrentTCB, tcb);
+
+ CTEGetLock(&CurrentTCB->tcb_lock, &TCBHandle);
+ if (CurrentTCB->tcb_state == TCB_TIME_WAIT &&
+ (CurrentTCB->tcb_refcnt == 0) && !CLOSING(CurrentTCB)){
+ if (FoundTCB == NULL || ((Now - CurrentTCB->tcb_alive) > Delta)) {
+ // Found a new 'older' TCB. If we already have one, free
+ // the lock on him and get the lock on the new one.
+ if (FoundTCB != NULL)
+ CTEFreeLock(&FoundTCB->tcb_lock, TCBHandle);
+ else
+ FoundLock = TCBHandle;
+
+ PrevFound = PrevTCB;
+ FoundTCB = CurrentTCB;
+ Delta = Now - FoundTCB->tcb_alive;
+ } else
+ CTEFreeLock(&CurrentTCB->tcb_lock, TCBHandle);
+ } else
+ CTEFreeLock(&CurrentTCB->tcb_lock, TCBHandle);
+
+ // Look at the next one.
+ PrevTCB = CurrentTCB;
+ CurrentTCB = PrevTCB->tcb_next;
+ }
+ }
+
+ // If we have one, pull him from the list.
+ if (FoundTCB != NULL) {
+ PrevFound->tcb_next = FoundTCB->tcb_next;
+ FoundTCB->tcb_flags &= ~IN_TCB_TABLE;
+ // Close the RCE on this guy.
+ (*LocalNetInfo.ipi_closerce)(FoundTCB->tcb_rce);
+ TStats.ts_numconns--;
+ if (LastTCB == FoundTCB) {
+ LastTCB = NULL;
+ }
+ CTEFreeLock(&FoundTCB->tcb_lock, FoundLock);
+ }
+
+ CTEFreeLock(&TCBTableLock, TableHandle);
+ return FoundTCB;
+}
+
+//* AllocTCB - Allocate a TCB.
+//
+// Called whenever we need to allocate a TCB. We try to pull one off the
+// free list, or allocate one if we need one. We then initialize it, etc.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to the new TCB, or NULL if we couldn't get one.
+//
+TCB *
+AllocTCB(void)
+{
+ TCB *NewTCB;
+
+ // First, see if we have one on the free list. The code for doing this
+ // is a little different between the NT and VxD worlds.
+#ifdef NT
+ PSINGLE_LIST_ENTRY BufferLink;
+
+ BufferLink = ExInterlockedPopEntrySList(&FreeTCBList, &FreeTCBListLock);
+
+ if (BufferLink != NULL) {
+ NewTCB = STRUCT_OF(TCB, BufferLink, tcb_next);
+ CTEStructAssert(NewTCB, tcb);
+ }
+#else // NT
+ NewTCB = FreeTCBList;
+ if (NewTCB != NULL) {
+ CTEStructAssert(NewTCB, tcb);
+ FreeTCBList = NewTCB->tcb_next;
+ }
+#endif // NT
+
+ else {
+
+ // We have none on the free list. If the total number of TCBs
+ // outstanding is more than we like to keep on the free list, try
+ // to scavenge a TCB from time wait.
+ if (CurrentTCBs < MaxFreeTCBs || ((NewTCB = ScavengeTCB()) == NULL)) {
+ if (CurrentTCBs < MaxTCBs) {
+ NewTCB = CTEAllocMem(sizeof(TCB));
+ if (NewTCB == NULL) {
+ return NewTCB;
+ }
+ else {
+ CTEInterlockedAddUlong(&CurrentTCBs, 1, &FreeTCBListLock);
+ }
+ } else
+ return NULL;
+ }
+ }
+
+ CTEAssert(NewTCB != NULL);
+
+ CTEMemSet(NewTCB, 0, sizeof(TCB));
+#ifdef DEBUG
+ NewTCB->tcb_sig = tcb_signature;
+#endif
+ INITQ(&NewTCB->tcb_sendq);
+ NewTCB->tcb_cursend = NULL;
+ NewTCB->tcb_alive = TCPTime;
+ // Initially we're not on the fast path because we're not established. Set
+ // the slowcount to one and set up the fastchk fields so we don't take the
+ // fast path.
+ NewTCB->tcb_slowcount = 1;
+ NewTCB->tcb_fastchk = TCP_FLAG_ACK | TCP_FLAG_SLOW;
+
+#if FAST_RETRANSMIT
+ NewTCB->tcb_dup = 0;
+#endif
+
+ CTEInitLock(&NewTCB->tcb_lock);
+
+ return NewTCB;
+}
+
+//* FreeTCB - Free a TCB.
+//
+// Called whenever we need to free a TCB.
+//
+// Note: This routine may be called with the TCBTableLock held.
+//
+// Input: FreedTCB - TCB to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeTCB(TCB *FreedTCB)
+{
+#ifdef NT
+ PSINGLE_LIST_ENTRY BufferLink;
+#endif // NT
+
+
+#ifndef NT
+ //
+ // Since we've moved to using sequenced lists for these resources,
+ // it's risky to actually free the memory here, so we won't do this
+ // for NT unless it becomes a problem.
+ if (CurrentTCBs > MaxFreeTCBs) {
+ CTEInterlockedAddUlong(&CurrentTCBs, (ulong) -1, &FreeTCBListLock);
+ CTEFreeMem(FreedTCB);
+ return;
+ }
+#endif
+
+#ifdef NT
+
+ CTEStructAssert(FreedTCB, tcb);
+
+ BufferLink = STRUCT_OF(SINGLE_LIST_ENTRY, &(FreedTCB->tcb_next), Next);
+ ExInterlockedPushEntrySList(
+ &FreeTCBList,
+ BufferLink,
+ &FreeTCBListLock
+ );
+
+#else // NT
+
+ CTEStructAssert(FreedTCB, tcb);
+
+ FreedTCB->tcb_next = FreeTCBList;
+ FreeTCBList = FreedTCB;
+
+#endif // NT
+}
+
+#pragma BEGIN_INIT
+
+//* InitTCB - Initialize our TCB code.
+//
+// Called during init time to initialize our TCB code. We initialize
+// the TCB table, etc, then return.
+//
+// Input: Nothing.
+//
+// Returns: TRUE if we did initialize, false if we didn't.
+//
+int
+InitTCB(void)
+{
+ int i;
+
+ for (i = 0; i < TCB_TABLE_SIZE; i++)
+ TCBTable[i] = NULL;
+
+ LastTCB = NULL;
+
+#ifdef NT
+ ExInitializeSListHead(&FreeTCBList);
+#endif
+
+ CTEInitLock(&TCBTableLock);
+ CTEInitLock(&FreeTCBListLock);
+
+ TCPTime = 0;
+ TCBWalkCount = 0;
+ DeadmanTicks = NUM_DEADMAN_TICKS;
+ CTEInitTimer(&TCBTimer);
+ CTEStartTimer(&TCBTimer, MS_PER_TICK, TCBTimeout, NULL);
+
+ return TRUE;
+}
+
+//* UnInitTCB - UnInitialize our TCB code.
+//
+// Called during init time if we're going to fail the init. We don't actually
+// do anything here.
+//
+// Input: Nothing.
+//
+// Returns: Nothing.
+//
+void
+UnInitTCB(void)
+{
+ CTEStopTimer(&TCBTimer);
+ return;
+}
+
+#pragma END_INIT
+
diff --git a/private/ntos/tdi/tcpip/tcp/tcb.h b/private/ntos/tdi/tcpip/tcp/tcb.h
new file mode 100644
index 000000000..3d273e50e
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcb.h
@@ -0,0 +1,67 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCB.H - TCB management definitions.
+//
+// This file contains the definitons needed for TCB management.
+//
+
+#define TCB_TABLE_SIZE 64
+
+#define MAX_REXMIT_CNT 5
+#define MAX_CONNECT_REXMIT_CNT 3
+#define MAX_CONNECT_RESPONSE_REXMIT_CNT 3
+#ifdef SYN_ATTACK
+#define ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT 1
+#endif
+
+extern uint TCPTime;
+
+#ifdef OLDHASH1
+#define TCB_HASH(DA,SA,DP,SP) ((uint)(*(uchar *)&(DA) + *((uchar *)&(DA) + 1) \
+ + *((uchar *)&(DA) + 2) + *((uchar *)&(DA) + 3)) % TCB_TABLE_SIZE)
+#endif
+
+#ifdef OLDHASH
+#define TCB_HASH(DA,SA,DP,SP) (((DA) + (SA) + (uint)(DP) + (uint)(SP)) % \
+ TCB_TABLE_SIZE)
+#endif
+
+#define ROR8(x) (uchar)(((uchar)(x) >> 1) | (uchar)(((uchar)(x) & 1) << 7))
+
+#define TCB_HASH(DA,SA,DP,SP) (((uint)(ROR8(ROR8(ROR8(ROR8(*((uchar *)&(DP) + 1) + \
+*((uchar *)&(DP))) + \
+*((uchar *)&(DA) + 3)) + \
+*((uchar *)&(DA) + 2)) + \
+*((uchar *)&(DA) + 1)) + \
+*((uchar *)&(DA)) )) % TCB_TABLE_SIZE)
+
+extern struct TCB *FindTCB(IPAddr Src, IPAddr Dest, ushort DestPort,
+ ushort SrcPort);
+extern uint InsertTCB(struct TCB *NewTCB);
+extern struct TCB *AllocTCB(void);
+extern void FreeTCB(struct TCB *FreedTCB);
+extern uint RemoveTCB(struct TCB *RemovedTCB);
+
+extern uint ValidateTCBContext(void *Context, uint *Valid);
+extern uint ReadNextTCB(void *Context, void *OutBuf);
+
+extern int InitTCB(void);
+extern void UnInitTCB(void);
+extern void TCBWalk(uint (*CallRtn)(struct TCB *, void *, void *,
+ void *), void *Context1, void *Context2,
+ void *Context3);
+extern uint DeleteTCBWithSrc(struct TCB *CheckTCB, void *AddrPtr,
+ void *Unused1, void *Unused2);
+extern uint SetTCBMTU(struct TCB *CheckTCB, void *DestPtr,
+ void *SrcPtr, void *MTUPtr);
+extern void ReetSendNext(struct TCB *SeqTCB, SeqNum DropSeq);
+
+extern uint TCBWalkCount;
+
+
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/tcp.h b/private/ntos/tdi/tcpip/tcp/tcp.h
new file mode 100644
index 000000000..d88d3443e
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcp.h
@@ -0,0 +1,426 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+#ifndef _TCP_INCLUDED_
+#define _TCP_INCLUDED_
+
+//** TCP.H - TCP definitions.
+//
+// This file contains the definitions of TCP protocol specific options, such
+// as the sequence numbers and TCB.
+//
+
+#define PROTOCOL_TCP 6
+#define MAX_REMOTE_MSS 536
+
+//* Timer stuff. We keep timers as ticks.
+#define MS_PER_TICK 100
+#define MS_TO_TICKS(m) ((m) / MS_PER_TICK)
+#define MIN_RETRAN_TICKS 3
+
+#define DEL_ACK_TICKS 2
+// Define MAX_REXMIT_TO to be number of ticks in 2MSL (=240 seconds)
+
+#define MAX_REXMIT_TO ((ushort)FinWait2TO)
+
+#define SWS_TO MS_TO_TICKS(5000)
+
+#define FIN_WAIT2_TO 240
+#define PUSH_TO MS_TO_TICKS(500)
+
+typedef ulong TCP_TIME;
+#define MAX_CONN_TO_TICKS 0xffff
+#define INFINITE_CONN_TO(t) ((t) == 0)
+#define TCP_TIME_TO_TICKS(t) (((t)/MS_PER_TICK)+1)
+
+
+// Sequence numbers are kept as signed 32 bit quantities, with macros
+// defined to do wraparound comparisons on them.
+
+typedef int SeqNum; // A sequence number.
+
+//* Macros for comparions on sequence numbers.
+
+#define SEQ_GT(a, b) (((a) - (b)) > 0)
+#define SEQ_GTE(a, b) (((a) - (b)) >= 0)
+#define SEQ_LT(a, b) (((a) - (b)) < 0)
+#define SEQ_LTE(a, b) (((a) - (b)) <= 0)
+#define SEQ_EQ(a, b) ((a) == (b))
+
+//* The TCB - transport control block structure. This is the
+// structure that contains all of the state for the transport
+// connection, including sequence numbers, flow control information,
+// pending sends and receives, etc.
+
+#define tcb_signature 0x20424354 // 'TCB '
+
+struct TCB {
+
+#ifdef DEBUG
+ ulong tcb_sig; // Debug signature.
+#endif
+ struct TCB *tcb_next; // Next pointer in TCB table.
+ DEFINE_LOCK_STRUCTURE(tcb_lock)
+ // Send sequence variables.
+ SeqNum tcb_senduna; // Sequence number of first unack'd
+ // data.
+ SeqNum tcb_sendnext; // Sequence number of next byte to
+ // send.
+ SeqNum tcb_sendmax; // Max value of sendnext this
+ // epoch.
+ uint tcb_sendwin; // Send window.
+ uint tcb_unacked; // Total number of bytes of unacked
+ // data.
+ uint tcb_maxwin; // Max send window seen.
+ uint tcb_cwin; // Congestion window.
+ uint tcb_ssthresh; // Slow start threshold.
+ uint tcb_phxsum; // Precomputed pseudo-header xsum.
+ struct TCPSendReq *tcb_cursend; // Current send in use.
+ PNDIS_BUFFER tcb_sendbuf; // Current buffer chain being sent.
+ uint tcb_sendofs; // Offset into start of chain.
+ uint tcb_sendsize; // Number of bytes unsent in current
+ // send.
+ Queue tcb_sendq; // Queue of send requests.
+
+ // Receive sequence variables.
+ SeqNum tcb_rcvnext; // Next byte we expect to receive.
+ int tcb_rcvwin; // Receive window we're offering.
+ SeqNum tcb_sendwl1; // Window update sequence number.
+ SeqNum tcb_sendwl2; // Window update ack number.
+ struct TCPRcvReq *tcb_currcv; // Current receive buffer.
+ uint tcb_indicated; // Bytes of data indicated.
+ uint tcb_flags; // Flags for this TCB.
+ uint tcb_fastchk; // Fast receive path check field.
+ uint (*tcb_rcvhndlr)(struct TCB *, uint,
+ struct IPRcvBuf *, uint Size);
+ // Addressing info.
+ IPAddr tcb_daddr; // Destination IP address.
+ IPAddr tcb_saddr; // Source IP address.
+ ushort tcb_dport; // Destination port.
+ ushort tcb_sport; // Source port.
+
+ ushort tcb_mss; // MSS for this connection.
+ ushort tcb_rexmit; // Rexmit value.
+ uint tcb_refcnt; // Reference count for TCB.
+ SeqNum tcb_rttseq; // Sequence number being measured
+ // for RTT.
+
+ // Retransmit timer information. These are stored as ticks, where by
+ // default each tick is 100ms.
+ ushort tcb_smrtt; // Smoothed rtt value.
+ ushort tcb_delta; // Delta value.
+
+ ushort tcb_remmss; // MSS advertised by peer.
+ uchar tcb_slowcount; // Count of reasons why we're on
+ // the slow path.
+ uchar tcb_pushtimer; // The 'push' timer.
+
+ // State information.
+ uchar tcb_state; // State of this TCB.
+ uchar tcb_rexmitcnt; // Count of rexmits on this TCB.
+ uchar tcb_pending; // Pending actions on this TCB.
+ uchar tcb_kacount; // Count of keep alive probes sent.
+ IP_STATUS tcb_error; // Last error we heard about from
+ // IP.
+
+ uint tcb_rtt; // Current round trip time TS.
+
+ ushort tcb_rexmittimer; // Timer for rexmit.
+ ushort tcb_delacktimer; // Timer for delayed ack.
+
+
+ uint tcb_defaultwin; // Default rcv. window.
+ uint tcb_alive; // Keep alive time value.
+
+ struct TCPRAHdr *tcb_raq; // Reassembly queue.
+ struct TCPRcvReq *tcb_rcvhead; // Head of recv. buffer queue.
+ struct TCPRcvReq *tcb_rcvtail; // Tail of recv. buffer queue.
+ uint tcb_pendingcnt; // Bytes waiting to be received.
+ struct IPRcvBuf *tcb_pendhead; // Head of pending recv. queue.
+ struct IPRcvBuf *tcb_pendtail; // Tail of pending recv. queue.
+
+ struct TCPConnReq *tcb_connreq; // Connection-type request for
+ // this connection.
+ void *tcb_conncontext; // Connection context for this
+ // connection.
+
+ uint tcb_bcountlow; // Low part of byte count.
+ uint tcb_bcounthi; // High part of bytecount.
+ uint tcb_totaltime; // Total number of ticks spent
+ // sending.
+ struct TCB *tcb_aonext; // Next pointer on AddrObj.
+ struct TCPConn *tcb_conn; // Back pointer to conn for TCB.
+ Queue tcb_delayq; // Queue linkage for delay queue.
+ uchar tcb_closereason; // Reason we're closing.
+ uchar tcb_bhprobecnt; // BH probe count.
+ ushort tcb_swstimer; // Timer for SWS override.
+ void *tcb_rcvind; // Receive indication handler.
+ void *tcb_ricontext; // Receive indication context.
+ // Miscellaneous info, for IP.
+ IPOptInfo tcb_opt; // Option information.
+ RouteCacheEntry *tcb_rce; // RCE for this connection.
+ struct TCPConnReq *tcb_discwait; // Disc-Wait req., if there is one.
+ struct TCPRcvReq *tcb_exprcv; // Head of expedited recv. buffer
+ // queue.
+ struct IPRcvBuf *tcb_urgpending; // Urgent data queue.
+ uint tcb_urgcnt; // Byte count of data on urgent q.
+ uint tcb_urgind; // Urgent bytes indicated.
+ SeqNum tcb_urgstart; // Start of urgent data.
+ SeqNum tcb_urgend; // End of urgent data.
+ uint tcb_walkcount; // Count of number of people
+ // 'walking' this TCB.
+#if FAST_RETRANSMIT
+ ushort tcb_dup; // For Fast recovery algorithm
+ ushort tcb_force; // Force next send after fast send
+#endif
+
+};
+
+//* Definitions for TCP states.
+#define TCB_CLOSED 0 // Closed.
+#define TCB_LISTEN 1 // Listening.
+#define TCB_SYN_SENT 2 // SYN Sent.
+#define TCB_SYN_RCVD 3 // SYN received.
+#define TCB_ESTAB 4 // Established.
+#define TCB_FIN_WAIT1 5 // FIN-WAIT-1
+#define TCB_FIN_WAIT2 6 // FIN-WAIT-2
+#define TCB_CLOSE_WAIT 7 // Close waiting.
+#define TCB_CLOSING 8 // Closing state.
+#define TCB_LAST_ACK 9 // Last ack state.
+#define TCB_TIME_WAIT 10 // Time wait state.
+
+typedef struct TCB TCB;
+
+#define SYNC_STATE(s) ((s) > TCB_SYN_RCVD)
+#define GRACEFUL_CLOSED_STATE(s) ((s) >= TCB_LAST_ACK)
+#define DATA_RCV_STATE(s) ((s) >= TCB_ESTAB && (s) <= TCB_FIN_WAIT2)
+#define DATA_SEND_STATE(s) ((s) == TCB_ESTAB || (s) == TCB_CLOSE_WAIT)
+
+//* Definitions for flags.
+#define WINDOW_SET 0x00000001 // Window explictly set.
+#define CLIENT_OPTIONS 0x00000002 // Have client IP options on conn.
+#define CONN_ACCEPTED 0x00000004 // Connection was accepted.
+#define ACTIVE_OPEN 0x00000008 // Connection came from an active
+ // open.
+#define DISC_NOTIFIED 0x00000010 // Client has been notified of a
+ // disconnect.
+#define IN_DELAY_Q 0x00000020 // We're in the delayed action Q.
+#define RCV_CMPLTING 0x00000040 // We're completeing rcvs.
+#define IN_RCV_IND 0x00000080 // We're calling a rcv. indicate
+ // handler.
+#define NEED_RCV_CMPLT 0x00000100 // We need to have recvs. completed.
+#define NEED_ACK 0x00000200 // We need to send an ACK.
+#define NEED_OUTPUT 0x00000400 // We need to output.
+
+#define DELAYED_FLAGS (NEED_RCV_CMPLT | NEED_ACK | NEED_OUTPUT)
+
+
+#define ACK_DELAYED 0x00000800 // We've delayed sending an ACK.
+
+#define PMTU_BH_PROBE 0x00001000 // We're probing for a PMTU BH.
+#define BSD_URGENT 0x00002000 // We're using BSD urgent semantics.
+#define IN_DELIV_URG 0x00004000 // We're in the DeliverUrgent routine.
+#define URG_VALID 0x00008000 // We've seen urgent data, and
+ // the urgent data fields are valid.
+
+#define FIN_NEEDED 0x00010000 // We need to send a FIN.
+#define NAGLING 0x00020000 // We are using Nagle's algorithm.
+#define IN_TCP_SEND 0x00040000 // We're in TCPSend.
+#define FLOW_CNTLD 0x00080000 // We've received a zero window
+ // from our peer.
+#define DISC_PENDING 0x00100000 // A disconnect notification is
+ // pending.
+#define TW_PENDING 0x00200000 // We're waiting to finish going
+ // to TIME-WAIT.
+#define FORCE_OUTPUT 0x00400000 // Output is being forced.
+#define FORCE_OUT_SHIFT 22 // Shift to get FORCE_OUTPUT into
+ // low bit.
+#define SEND_AFTER_RCV 0x00800000 // We need to send after we get out
+ // of recv.
+#define GC_PENDING 0x01000000 // A graceful close is pending.
+#define KEEPALIVE 0x02000000 // Doing keepalives on this TCB.
+#define URG_INLINE 0x04000000 // Urgent data to be processed
+ // inline.
+
+#ifdef RASAUTODIAL
+#define ACD_CONN_NOTIF 0x08000000 // inform automatic connection
+ // driver about this connection
+#endif // RASAUTODIAL
+
+#define FIN_OUTSTANDING 0x10000000 // We've sent a FIN 'recently', i.e.
+ // since the last retransmit. When
+ // this flag is set sendnext ==
+ // sendmax.
+
+#define FIN_OUTS_SHIFT 28 // Shift to FIN_OUTSTANDING bit into
+ // low bit.
+#define FIN_SENT 0x20000000 // We've sent a FIN that hasn't
+ // been acknowledged. Once this
+ // bit has been turned on in
+ // FIN-WAIT-1 the sequence number
+ // of the FIN will be sendmax-1.
+#define NEED_RST 0x40000000 // We need to send a RST when
+ // closing.
+#define IN_TCB_TABLE 0x80000000 // TCB is in the TCB table.
+
+//* The defintion of the 'slow flags'. If any of these flags are set we'll
+// be forced off of the fast path.
+
+#define TCP_SLOW_FLAGS (URG_VALID | FLOW_CNTLD | GC_PENDING | \
+ TW_PENDING | DISC_NOTIFIED | IN_DELIV_URG | \
+ FIN_NEEDED | FIN_SENT | FIN_OUTSTANDING | \
+ DISC_PENDING | PMTU_BH_PROBE)
+
+//* Close reasons.
+#define TCB_CLOSE_RST 0x80 // Received a RST segment.
+#define TCB_CLOSE_ABORTED 0x40 // Had a local abort.
+#define TCB_CLOSE_TIMEOUT 0x20 // Connection timed out.
+#define TCB_CLOSE_REFUSED 0x10 // Connect attempt was refused.
+#define TCB_CLOSE_UNREACH 0x08 // Remote destination unreachable.
+#define TCB_CLOSE_SUCCESS 0x01 // Successfull close.
+
+//* TCB Timer macros.
+#define START_TCB_TIMER(t, v) (t) = (v)
+#define STOP_TCB_TIMER(t) (t) = 0
+#define TCB_TIMER_RUNNING(t) ((t) != 0)
+
+// Macro to compute retransmit timeout.
+#define REXMIT_TO(t) ((((t)->tcb_smrtt >> 2) + (t)->tcb_delta) >> 1)
+
+//* Definitons for pending actions. We define a PENDING_ACTION macro
+// that can be used to decide whether or not we can proceed with an
+// activity. The only pending action we really care about is DELETE - others
+// are low priority and can be put off.
+#define PENDING_ACTION(t) ((t)->tcb_pending & DEL_PENDING)
+#define DEL_PENDING 0x01 // Delete is pending.
+#define OPT_PENDING 0x02 // Option set is pending.
+
+//* Macro to see if a TCB is closing.
+#define CLOSING(t) ((t)->tcb_pending & DEL_PENDING)
+
+//* Structure of a TCP packet header.
+
+struct TCPHeader {
+ ushort tcp_src; // Source port.
+ ushort tcp_dest; // Destination port.
+ SeqNum tcp_seq; // Sequence number.
+ SeqNum tcp_ack; // Ack number.
+ ushort tcp_flags; // Flags and data offset.
+ ushort tcp_window; // Window offered.
+ ushort tcp_xsum; // Checksum.
+ ushort tcp_urgent; // Urgent pointer.
+};
+
+typedef struct TCPHeader TCPHeader;
+
+//* Definitions for header flags.
+#define TCP_FLAG_FIN 0x00000100
+#define TCP_FLAG_SYN 0x00000200
+#define TCP_FLAG_RST 0x00000400
+#define TCP_FLAG_PUSH 0x00000800
+#define TCP_FLAG_ACK 0x00001000
+#define TCP_FLAG_URG 0x00002000
+
+#define TCP_FLAGS_ALL (TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST | \
+ TCP_FLAG_ACK | TCP_FLAG_URG)
+
+//* Flags in the tcb_fastchk field that are not in the TCP header proper.
+// Setting these flags forces us off the fast path.
+#define TCP_FLAG_SLOW 0x00000001 // Need to be on slow path.
+#define TCP_FLAG_IN_RCV 0x00000002 // In recv. path already.
+
+#if FAST_RETRANSMIT
+#define TCP_FLAG_FASTREC 0x00000004 // This is used to mark tcb
+#endif // when Fast retransmit is in progress
+ // Debugging purpose only
+
+#define TCP_OFFSET_MASK 0xf0
+#define TCP_HDR_SIZE(t) (uint)(((*(uchar *)&(t)->tcp_flags) & TCP_OFFSET_MASK) >> 2)
+
+#define MAKE_TCP_FLAGS(o, f) ((f) | ((o) << 4))
+
+#define TCP_OPT_EOL 0
+#define TCP_OPT_NOP 1
+#define TCP_OPT_MSS 2
+#define MSS_OPT_SIZE 4
+
+//* Convenient byte swapped structure for receives.
+struct TCPRcvInfo {
+ SeqNum tri_seq; // Sequence number.
+ SeqNum tri_ack; // Ack number.
+ uint tri_window; // Window.
+ uint tri_urgent; // Urgent pointer.
+ uint tri_flags; // Flags.
+};
+
+typedef struct TCPRcvInfo TCPRcvInfo;
+
+
+
+//* General structure, at the start of all command specific request structures.
+
+#define tr_signature 0x20205254 // 'TR '
+
+struct TCPReq {
+#ifdef DEBUG
+ ulong tr_sig;
+#endif
+ struct Queue tr_q; // Q linkage.
+ CTEReqCmpltRtn tr_rtn; // Completion routine.
+ PVOID tr_context; // User context.
+ int tr_status; // Final complete status.
+};
+
+typedef struct TCPReq TCPReq;
+
+
+#ifdef NT
+
+#ifdef POOL_TAGGING
+
+#ifdef ExAllocatePool
+#undef ExAllocatePool
+#endif
+
+#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'tPCT')
+
+#ifndef CTEAllocMem
+#error "CTEAllocMem is not already defined - will override tagging"
+#else
+#undef CTEAllocMem
+#endif
+
+#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'tPCT')
+
+#endif // POOL_TAGGING
+
+//
+// TCP endpoint context structure allocated for each open of TCP/UDP.
+// A pointer to this structure is stored in FileObject->FsContext.
+//
+typedef struct _TCP_CONTEXT {
+ union {
+ HANDLE AddressHandle;
+ CONNECTION_CONTEXT ConnectionContext;
+ HANDLE ControlChannel;
+ } Handle;
+ ULONG ReferenceCount;
+ BOOLEAN CancelIrps;
+#if DBG
+ LIST_ENTRY PendingIrpList;
+ LIST_ENTRY CancelledIrpList;
+#endif
+ KEVENT CleanupEvent;
+} TCP_CONTEXT, *PTCP_CONTEXT;
+
+#endif // NT
+
+
+#include "tcpdeb.h"
+
+#endif // _TCP_INCLUDED_
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/tcpcfg.h b/private/ntos/tdi/tcpip/tcp/tcpcfg.h
new file mode 100644
index 000000000..e3ee3ab01
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcpcfg.h
@@ -0,0 +1,86 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//* TCPCFG.H - Definitions of configuration information for TCP.
+//
+
+/*NOINC*/
+extern uint DeadGWDetect;
+extern uint PMTUDiscovery;
+extern uint PMTUBHDetect;
+extern uint KeepAliveTime;
+extern uint KAInterval;
+extern uint DefaultRcvWin;
+extern uint MaxConnections;
+extern uint MaxConnectRexmitCount;
+extern uint MaxConnectResponseRexmitCount;
+extern uint MaxDataRexmitCount;
+
+#ifdef SYN_ATTACK
+
+extern BOOLEAN SynAttackProtect;
+extern uint TCPHalfOpen;
+extern uint TCPHalfOpenRetried;
+extern uint TCPMaxHalfOpen;
+extern uint TCPMaxHalfOpenRetried;
+extern uint TCPMaxHalfOpenRetriedLW;
+extern uint TCPPortsExhausted;
+extern uint TCPMaxPortsExhausted;
+extern uint TCPMaxPortsExhaustedLW;
+extern uint MaxConnectResponseRexmitCountTmp;
+EXTERNAL_LOCK(SynAttLock)
+#endif
+
+
+extern uint BSDUrgent;
+extern uint PreloadCount;
+extern uint FinWait2TO;
+extern uint NTWMaxConnectCount;
+extern uint NTWMaxConnectTime;
+extern uint MaxUserPort;
+
+#ifdef SECFLTR
+extern uint SecurityFilteringEnabled;
+#endif // SECFLTR
+
+/*INC*/
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#define DEFAULT_DEAD_GW_DETECT TRUE
+#define DEFAULT_PMTU_DISCOVERY TRUE
+#define DEFAULT_PMTU_BHDETECT FALSE
+#define DEFAULT_KA_TIME 7200000
+#define DEFAULT_KA_INTERVAL 1000
+#define DEFAULT_RCV_WIN 8192
+#define DEFAULT_PRELOAD_COUNT 0
+#define MAX_PRELOAD_COUNT 32
+#define PRELOAD_BLOCK_SIZE 16384
+
+/*NOINC*/
+#ifndef VXD
+#define DEFAULT_MAX_CONNECTIONS (INVALID_CONN_INDEX - 1)
+#define NTW_MAX_CONNECT_TIME 600
+#define NTW_MAX_CONNECT_COUNT 15
+#else
+
+/*INC*/
+#define DEFAULT_MAX_CONNECTIONS 100
+
+/*NOINC*/
+#endif
+/*INC*/
+
+#define DEFAULT_CONNECT_REXMIT_CNT 3
+#define DEFAULT_DATA_REXMIT_CNT 5
+#define DEFAULT_BSD_URGENT TRUE
+
diff --git a/private/ntos/tdi/tcpip/tcp/tcpconn.c b/private/ntos/tdi/tcpip/tcp/tcpconn.c
new file mode 100644
index 000000000..daeadf85c
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcpconn.c
@@ -0,0 +1,2344 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCPCONN.C - TCP connection mgmt code.
+//
+// This file contains the code handling TCP connection related requests,
+// such as connecting and disconnecting.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#ifdef VXD
+#include "tdivxd.h"
+#include "tdistat.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "tcp.h"
+#include "tcb.h"
+#include "tcpconn.h"
+#include "tcpsend.h"
+#include "tcprcv.h"
+#include "tcpdeliv.h"
+#include "tlcommon.h"
+#include "info.h"
+#include "tcpcfg.h"
+
+#define CONN_INDEX(c) ((c) & 0xffffff)
+#define CONN_INST(c) ((uchar)((c) >> 24))
+#define MAKE_CONN_ID(i, s) ((((uint)(s)) << 24) | ((uint)(i)))
+#define GROW_DELTA 16
+#define INVALID_CONN_ID MAKE_CONN_ID(INVALID_CONN_INDEX, 0xff)
+
+
+#ifndef NT
+TCPConnReq *ConnReqFree; // Connection request free list.
+#else
+SLIST_HEADER ConnReqFree; // Connection request free list.
+extern PDRIVER_OBJECT TCPDriverObject;
+#endif
+
+DEFINE_LOCK_STRUCTURE(ConnReqFreeLock) // Lock to protect conn req free list.
+uint NumConnReq; // Current number of ConnReqs in system.
+uint MaxConnReq = 0xffffffff; // Maximum allowed number of ConnReqs.
+
+TCPConnTable *ConnTable = NULL; // The current connection table.
+
+uint ConnTableSize; // Current number of entries in the
+ // ConnTable.
+uchar ConnInst; // Current conn inst in use.
+uint NextConnIndex; // Next conn. index to use.
+
+DEFINE_LOCK_STRUCTURE(ConnTableLock)
+EXTERNAL_LOCK(AddrObjTableLock)
+EXTERNAL_LOCK(TCBTableLock)
+
+TCPAddrCheckElement *AddrCheckTable = NULL; // The current check table
+
+extern IPInfo LocalNetInfo;
+extern void RemoveConnFromAO(AddrObj *AO, TCPConn *Conn);
+
+
+//
+// All of the init code can be discarded.
+//
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+
+int InitTCPConn(void);
+void UnInitTCPConn(void);
+void NotifyConnLimitProc(CTEEvent *Event, void *Context);
+
+typedef struct ConnLimitExceededStruct {
+ CTEEvent cle_event;
+ IPAddr cle_addr;
+ ulong cle_port;
+} ConnLimitExceededStruct;
+
+
+#pragma alloc_text(INIT, InitTCPConn)
+#pragma alloc_text(INIT, UnInitTCPConn)
+#pragma alloc_text(PAGE, NotifyConnLimitProc)
+
+#endif // ALLOC_PRAGMA
+#endif
+
+void CompleteConnReq(TCB *CmpltTCB, IPOptInfo *OptInfo, TDI_STATUS Status);
+
+
+//** Routines for handling conn refcount going to 0.
+
+//* DummyDone - Called when nothing to do.
+//
+// Input: Conn - Conn goint to 0.
+// Handle - Lock handle for conn table lock.
+//
+// Returns: Nothing.
+//
+void
+DummyDone(TCPConn *Conn, CTELockHandle Handle)
+{
+ CTEFreeLock(&ConnTableLock, Handle);
+}
+
+//* DummyCmplt - Dummy close completion routine.
+void
+DummyCmplt(PVOID Dummy1, uint Dummy2, uint Dummy3)
+{
+}
+
+//* CloseDone - Called when we need to complete a close.
+//
+// Input: Conn - Conn going to 0.
+// Handle - Lock handle for conn table lock.
+//
+// Returns: Nothing.
+//
+void
+CloseDone(TCPConn *Conn, CTELockHandle Handle)
+{
+ CTEReqCmpltRtn Rtn; // Completion routine.
+ PVOID Context; // User context for completion routine.
+ CTELockHandle AOTableHandle, ConnTableHandle, AOHandle;
+ AddrObj *AO;
+
+ CTEAssert(Conn->tc_flags & CONN_CLOSING);
+
+ Rtn = Conn->tc_rtn;
+ Context = Conn->tc_rtncontext;
+ CTEFreeLock(&ConnTableLock, Handle);
+
+ CTEGetLock(&AddrObjTableLock, &AOTableHandle);
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+
+ if ((AO = Conn->tc_ao) != NULL) {
+
+ CTEStructAssert(AO, ao);
+
+ // It's associated.
+ CTEGetLock(&AO->ao_lock, &AOHandle);
+ RemoveConnFromAO(AO, Conn);
+ // We've pulled him from the AO, we can free the lock now.
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+ }
+
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ CTEFreeLock(&AddrObjTableLock, AOTableHandle);
+
+ CTEFreeMem(Conn);
+
+ (*Rtn)(Context, TDI_SUCCESS, 0);
+
+}
+
+//* DisassocDone - Called when we need to complete a disassociate.
+//
+// Input: Conn - Conn going to 0.
+// Handle - Lock handle for conn table lock.
+//
+// Returns: Nothing.
+//
+void
+DisassocDone(TCPConn *Conn, CTELockHandle Handle)
+{
+ CTEReqCmpltRtn Rtn; // Completion routine.
+ PVOID Context; // User context for completion routine.
+ AddrObj *AO;
+ CTELockHandle AOTableHandle, ConnTableHandle, AOHandle;
+ uint NeedClose = FALSE;
+
+ CTEAssert(Conn->tc_flags & CONN_DISACC);
+ CTEAssert(!(Conn->tc_flags & CONN_CLOSING));
+ CTEAssert(Conn->tc_refcnt == 0);
+
+ Rtn = Conn->tc_rtn;
+ Context = Conn->tc_rtncontext;
+ Conn->tc_refcnt = 1;
+ CTEFreeLock(&ConnTableLock, Handle);
+
+ CTEGetLock(&AddrObjTableLock, &AOTableHandle);
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+ if (!(Conn->tc_flags & CONN_CLOSING)) {
+
+ AO = Conn->tc_ao;
+ if (AO != NULL) {
+ CTEGetLock(&AO->ao_lock, &AOHandle);
+ RemoveConnFromAO(AO, Conn);
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+ }
+
+ CTEAssert(Conn->tc_refcnt == 1);
+ Conn->tc_flags &= ~CONN_DISACC;
+ } else
+ NeedClose = TRUE;
+
+ Conn->tc_refcnt = 0;
+ CTEFreeLock(&AddrObjTableLock, ConnTableHandle);
+
+ if (NeedClose) {
+ CloseDone(Conn, AOTableHandle);
+ } else {
+ CTEFreeLock(&ConnTableLock, AOTableHandle);
+ (*Rtn)(Context, TDI_SUCCESS, 0);
+ }
+
+}
+
+
+//* FreeConnReq - Free a connection request structure.
+//
+// Called to free a connection request structure.
+//
+// Input: FreedReq - Connection request structure to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeConnReq(TCPConnReq *FreedReq)
+{
+#ifdef NT
+ PSINGLE_LIST_ENTRY BufferLink;
+
+ CTEStructAssert(FreedReq, tcr);
+
+ BufferLink = STRUCT_OF(
+ SINGLE_LIST_ENTRY,
+ &(FreedReq->tcr_req.tr_q.q_next),
+ Next
+ );
+
+ ExInterlockedPushEntrySList(
+ &ConnReqFree,
+ BufferLink,
+ &ConnReqFreeLock
+ );
+
+#else // NT
+ TCPConnReq **Temp;
+
+ CTEStructAssert(FreedReq, tcr);
+
+ Temp = (TCPConnReq **)&FreedReq->tcr_req.tr_q.q_next;
+ *Temp = ConnReqFree;
+ ConnReqFree = FreedReq;
+
+#endif // NT
+}
+
+//* GetConnReq - Get a connection request structure.
+//
+// Called to get a connection request structure.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to ConnReq structure, or NULL if none.
+//
+TCPConnReq *
+GetConnReq(void)
+{
+ TCPConnReq *Temp;
+
+#ifdef NT
+ PSINGLE_LIST_ENTRY BufferLink;
+ Queue *QueuePtr;
+ TCPReq *ReqPtr;
+
+ BufferLink = ExInterlockedPopEntrySList(
+ &ConnReqFree,
+ &ConnReqFreeLock
+ );
+
+ if (BufferLink != NULL) {
+ QueuePtr = STRUCT_OF(Queue, BufferLink, q_next);
+ ReqPtr = STRUCT_OF(TCPReq, QueuePtr, tr_q);
+ Temp = STRUCT_OF(TCPConnReq, ReqPtr, tcr_req);
+ CTEStructAssert(Temp, tcr);
+ }
+ else {
+ if (NumConnReq < MaxConnReq)
+ Temp = CTEAllocMem(sizeof(TCPConnReq));
+ else
+ Temp = NULL;
+
+ if (Temp != NULL) {
+ ExInterlockedAddUlong(&NumConnReq, 1, &ConnReqFreeLock);
+#ifdef DEBUG
+ Temp->tcr_req.tr_sig = tr_signature;
+ Temp->tcr_sig = tcr_signature;
+#endif
+ }
+ }
+
+#else // NT
+
+ Temp = ConnReqFree;
+ if (Temp != NULL)
+ ConnReqFree = (TCPConnReq *)Temp->tcr_req.tr_q.q_next;
+ else {
+ if (NumConnReq < MaxConnReq)
+ Temp = CTEAllocMem(sizeof(TCPConnReq));
+ else
+ Temp = NULL;
+
+ if (Temp != NULL) {
+ NumConnReq++;
+#ifdef DEBUG
+ Temp->tcr_req.tr_sig = tr_signature;
+ Temp->tcr_sig = tcr_signature;
+#endif
+ }
+ }
+
+#endif // NT
+
+ return Temp;
+}
+
+//* GetConnFromConnID - Get a Connection from a connection ID.
+//
+// Called to obtain a Connection pointer from a ConnID. We don't actually
+// check the connection pointer here, but we do bounds check the input ConnID
+// and make sure the instance fields match.
+// We assume the caller has taken the ConnTable lock.
+//
+// Input: ConnID - Connection ID to find a pointer for.
+//
+// Returns: Pointer to the TCPConn, or NULL.
+//
+TCPConn *
+GetConnFromConnID(uint ConnID)
+{
+ uint ConnIndex = CONN_INDEX(ConnID);
+ TCPConn *MatchingConn;
+
+ if (ConnIndex < ConnTableSize) {
+ MatchingConn = (*ConnTable)[ConnIndex];
+ if (MatchingConn != NULL) {
+ CTEStructAssert(MatchingConn, tc);
+ if (MatchingConn->tc_inst != CONN_INST(ConnID))
+ MatchingConn = NULL;
+ }
+ } else
+ MatchingConn = NULL;
+
+ return MatchingConn;
+
+
+}
+
+//* GetConnID - Get a ConnTable slot.
+//
+// Called during OpenConnection to find a free slot in the ConnTable and
+// set it up with a connection. We assume the caller holds the lock on the
+// TCB ConnTable when we are called.
+//
+// Input: NewConn - Connection to enter into slot..
+//
+// Returns: A ConnId to use.
+//
+uint
+GetConnID(TCPConn *NewConn)
+{
+ uint CurrConnID;
+ uint i; // Index variable.
+
+ // Keep doing this until it works.
+ for (;;) {
+ CurrConnID = NextConnIndex;
+
+ for (i = 0; i < ConnTableSize; i++ ) {
+ if (CurrConnID == ConnTableSize)
+ CurrConnID = 0; // Wrapped, start at 0.
+
+ if ((*ConnTable)[CurrConnID] == NULL)
+ break; // Found a free one.
+
+ ++CurrConnID;
+ }
+
+ if (i < ConnTableSize) {
+ // We found a free slot.
+ (*ConnTable)[CurrConnID] = NewConn;
+ NextConnIndex = CurrConnID + 1;
+ ConnInst++;
+ NewConn->tc_inst = ConnInst;
+ return MAKE_CONN_ID(CurrConnID, ConnInst);
+ }
+
+ // Didn't find a free slot. Grow the table.
+ if (ConnTableSize != MaxConnections) {
+ uint NewTableSize;
+ TCPConnTable *NewTable;
+
+ NewTableSize = MIN(ConnTableSize + GROW_DELTA, MaxConnections);
+ NewTable = CTEAllocMem(NewTableSize * sizeof(TCPConn *));
+ if (NewTable != NULL) {
+ TCPConnTable *OldTable;
+ // We allocated it. Copy the old table in, and update ptrs and
+ // size.
+ CTEMemSet(NewTable, 0, NewTableSize * sizeof(TCPConn *));
+ CTEMemCopy(NewTable, ConnTable, ConnTableSize *
+ (sizeof (TCPConn *)));
+ OldTable = ConnTable;
+ ConnTable = NewTable;
+ ConnTableSize = NewTableSize;
+ if (OldTable != NULL)
+ CTEFreeMem(OldTable);
+ // Try it again, from the top.
+ continue;
+ } else {
+ // Couldn't grow the table.
+ return INVALID_CONN_ID;
+ }
+
+ } else {
+ // Table's already at the maximum allowable size.
+ return INVALID_CONN_ID;
+ }
+ }
+
+
+}
+
+//* FreeConnID - Free a ConnTable slot.
+//
+// Called when we're done with a ConnID. We assume the caller holds the lock
+// on the TCB ConnTable when we are called.
+//
+// Input: ConnID - Connection ID to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeConnID(uint ConnID)
+{
+ uint Index = CONN_INDEX(ConnID); // Index into conn table.
+
+ CTEAssert(Index < ConnTableSize);
+ CTEAssert((*ConnTable)[Index] != NULL);
+ CTEStructAssert((*ConnTable)[Index], tc);
+
+ FREE_CONN_INDEX(Index);
+
+}
+
+//* MapIPError - Map an IP error to a TDI error.
+//
+// Called to map an input IP error code to a TDI error code. If we can't,
+// we return the provided default.
+//
+// Input: IPError - Error code to be mapped.
+// Default - Default error code to return.
+//
+// Returns: Mapped TDI error.
+//
+TDI_STATUS
+MapIPError(IP_STATUS IPError, TDI_STATUS Default)
+{
+ switch (IPError) {
+
+ case IP_DEST_NET_UNREACHABLE:
+ return TDI_DEST_NET_UNREACH;
+ case IP_DEST_HOST_UNREACHABLE:
+ return TDI_DEST_HOST_UNREACH;
+ case IP_DEST_PROT_UNREACHABLE:
+ return TDI_DEST_PROT_UNREACH;
+ case IP_DEST_PORT_UNREACHABLE:
+ return TDI_DEST_PORT_UNREACH;
+ default:
+ return Default;
+ }
+}
+
+//* FinishRemoveTCBFromConn - Finish removing a TCB from a conn structure.
+//
+// Called when we have the locks we need and we just want to pull the
+// TCB off the connection. The caller must hold the ConnTableLock before
+// calling this.
+//
+// Input: RemovedTCB - TCB to be removed.
+//
+// Returns: Nothing.
+//
+void
+FinishRemoveTCBFromConn(TCB *RemovedTCB)
+{
+ TCPConn *Conn;
+ CTELockHandle AOHandle, TCBHandle;
+ AddrObj *AO;
+
+ if ((( Conn = RemovedTCB->tcb_conn ) != NULL ) &&
+ ( Conn->tc_tcb == RemovedTCB ) ) {
+ CTEStructAssert(Conn, tc);
+
+ AO = Conn->tc_ao;
+
+ if (AO != NULL) {
+ CTEGetLockAtDPC(&AO->ao_lock, &AOHandle);
+ CTEGetLockAtDPC(&RemovedTCB->tcb_lock, &TCBHandle);
+
+ // Need to double check this is still correct.
+
+ if (Conn == RemovedTCB->tcb_conn) {
+ // Everything still looks good.
+ REMOVEQ(&Conn->tc_q);
+ ENQUEUE(&AO->ao_idleq, &Conn->tc_q);
+ } else
+ Conn = RemovedTCB->tcb_conn;
+
+ CTEFreeLockFromDPC(&AO->ao_lock, TCBHandle);
+ } else {
+ CTEGetLockAtDPC(&RemovedTCB->tcb_lock, &AOHandle);
+ Conn = RemovedTCB->tcb_conn;
+ }
+
+ if (Conn != NULL) {
+ if (Conn->tc_tcb == RemovedTCB)
+ Conn->tc_tcb = NULL;
+ else
+ CTEAssert(Conn->tc_tcb == NULL);
+ }
+
+ CTEFreeLockFromDPC(&RemovedTCB->tcb_lock, AOHandle);
+ }
+}
+
+//* RemoveTCBFromConn - Remove a TCB from a Conn structure.
+//
+// Called when we need to disassociate a TCB from a connection structure.
+// All we do is get the appropriate locks and call FinishRemoveTCBFromConn.
+//
+// Input: RemovedTCB - TCB to be removed.
+//
+// Returns: Nothing.
+//
+void
+RemoveTCBFromConn(TCB *RemovedTCB)
+{
+ CTELockHandle ConnHandle, TCBHandle;
+
+ CTEStructAssert(RemovedTCB, tcb);
+
+ CTEGetLock(&ConnTableLock, &ConnHandle);
+
+ FinishRemoveTCBFromConn(RemovedTCB);
+
+ CTEFreeLock(&ConnTableLock, ConnHandle);
+
+}
+
+//* RemoveConnFromTCB - Remove a conn from a TCB.
+//
+// Called when we want to break the final association between a connection
+// and a TCB.
+//
+// Input: RemoveTCB - TCB to be removed.
+//
+// Returns: Nothing.
+//
+void
+RemoveConnFromTCB(TCB *RemoveTCB)
+{
+ ConnDoneRtn DoneRtn = NULL;
+ CTELockHandle ConnHandle, TCBHandle;
+ TCPConn *Conn;
+
+ CTEGetLock(&ConnTableLock, &ConnHandle);
+ CTEGetLock(&RemoveTCB->tcb_lock, &TCBHandle);
+
+
+ if ((Conn = RemoveTCB->tcb_conn) != NULL) {
+
+ CTEStructAssert(Conn, tc);
+
+ if (--(Conn->tc_refcnt) == 0)
+ DoneRtn = Conn->tc_donertn;
+
+ RemoveTCB->tcb_conn = NULL;
+ }
+
+ CTEFreeLock(&RemoveTCB->tcb_lock, TCBHandle);
+
+ if (DoneRtn != NULL)
+ (*DoneRtn)(Conn, ConnHandle);
+ else
+ CTEFreeLock(&ConnTableLock, ConnHandle);
+}
+
+
+//* CloseTCB - Close a TCB.
+//
+// Called when we are done with a TCB, and want to free it. We'll remove
+// him from any tables that he's in, and destroy any outstanding requests.
+//
+// Input: ClosedTCB - TCB to be closed.
+// Handle - Lock handle for TCB.
+//
+// Returns: Nothing.
+//
+void
+CloseTCB(TCB *ClosedTCB, CTELockHandle Handle)
+{
+ CTELockHandle ConnTableHandle, TCBTableHandle;
+ uchar OrigState = ClosedTCB->tcb_state;
+ TDI_STATUS Status;
+ uint OKToFree;
+
+
+ CTEStructAssert(ClosedTCB, tcb);
+ CTEAssert(ClosedTCB->tcb_refcnt == 0);
+ CTEAssert(ClosedTCB->tcb_state != TCB_CLOSED);
+ CTEAssert(ClosedTCB->tcb_pending & DEL_PENDING);
+
+ CTEFreeLock(&ClosedTCB->tcb_lock, Handle);
+
+ // We need to get the ConnTable, TCBTable, and TCB locks to pull
+ // this guy from all the appropriate tables.
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+
+ // We'll check to make sure that our state isn't CLOSED. This should never
+ // happen, since nobody should call TryToCloseTCB when the state is
+ // closed, or take the reference count if we're closing. Nevertheless,
+ // we'll double check as a safety measure.
+ if (ClosedTCB->tcb_state == TCB_CLOSED) {
+ DEBUGCHK;
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ return;
+ }
+
+ // Update SNMP counters. If we're in SYN-SENT or SYN-RCVD, this is a failed
+ // connection attempt. If we're in ESTABLISED or CLOSE-WAIT, treat this
+ // as an 'Established Reset' event.
+ if (ClosedTCB->tcb_state == TCB_SYN_SENT ||
+ ClosedTCB->tcb_state == TCB_SYN_RCVD)
+ TStats.ts_attemptfails++;
+ else
+ if (ClosedTCB->tcb_state == TCB_ESTAB ||
+ ClosedTCB->tcb_state == TCB_CLOSE_WAIT) {
+ TStats.ts_estabresets++;
+ TStats.ts_currestab--;
+ CTEAssert(*(int *)&TStats.ts_currestab >= 0);
+ }
+
+ ClosedTCB->tcb_state = TCB_CLOSED;
+
+
+ // Remove the TCB from it's associated TCPConn structure, if it has one.
+ FinishRemoveTCBFromConn(ClosedTCB);
+
+ CTEGetLockAtDPC(&TCBTableLock, &TCBTableHandle);
+ CTEGetLockAtDPC(&ClosedTCB->tcb_lock, &Handle);
+
+ OKToFree = RemoveTCB(ClosedTCB);
+
+ // He's been pulled from the appropriate places so nobody can find him.
+ // Free the locks, and proceed to destroy any requests, etc.
+ CTEFreeLockFromDPC(&ClosedTCB->tcb_lock, Handle);
+ CTEFreeLockFromDPC(&TCBTableLock, TCBTableHandle);
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+
+ if (SYNC_STATE(OrigState) && !GRACEFUL_CLOSED_STATE(OrigState)) {
+ if (ClosedTCB->tcb_flags & NEED_RST)
+ SendRSTFromTCB(ClosedTCB);
+ }
+
+ (*LocalNetInfo.ipi_freeopts)(&ClosedTCB->tcb_opt);
+ (*LocalNetInfo.ipi_closerce)(ClosedTCB->tcb_rce);
+
+ if (ClosedTCB->tcb_closereason & TCB_CLOSE_RST)
+ Status = TDI_CONNECTION_RESET;
+ else if (ClosedTCB->tcb_closereason & TCB_CLOSE_ABORTED)
+ Status = TDI_CONNECTION_ABORTED;
+ else if (ClosedTCB->tcb_closereason & TCB_CLOSE_TIMEOUT)
+ Status = MapIPError(ClosedTCB->tcb_error, TDI_TIMED_OUT);
+ else if (ClosedTCB->tcb_closereason & TCB_CLOSE_REFUSED)
+ Status = TDI_CONN_REFUSED;
+ else if (ClosedTCB->tcb_closereason & TCB_CLOSE_UNREACH)
+ Status = MapIPError(ClosedTCB->tcb_error, TDI_DEST_UNREACHABLE);
+ else
+ Status = TDI_SUCCESS;
+
+ // Now complete any outstanding requests on the TCB.
+ if (ClosedTCB->tcb_connreq != NULL) {
+ TCPConnReq *ConnReq = ClosedTCB->tcb_connreq;
+ CTEStructAssert(ConnReq, tcr);
+
+ (*ConnReq->tcr_req.tr_rtn)(ConnReq->tcr_req.tr_context, Status, 0);
+ FreeConnReq(ConnReq);
+ }
+
+ if (ClosedTCB->tcb_discwait != NULL) {
+ TCPConnReq *ConnReq = ClosedTCB->tcb_discwait;
+ CTEStructAssert(ConnReq, tcr);
+
+ (*ConnReq->tcr_req.tr_rtn)(ConnReq->tcr_req.tr_context, Status, 0);
+ FreeConnReq(ConnReq);
+ }
+
+ while (!EMPTYQ(&ClosedTCB->tcb_sendq)) {
+ TCPReq *Req;
+ TCPSendReq *SendReq;
+ long Result;
+
+ DEQUEUE(&ClosedTCB->tcb_sendq, Req, TCPReq, tr_q);
+
+ CTEStructAssert(Req, tr);
+ SendReq = (TCPSendReq *)Req;
+ CTEStructAssert(SendReq, tsr);
+
+ // Decrement the initial reference put on the buffer when it was
+ // allocated. This reference would have been decremented if the
+ // send had been acknowledged, but then the send would not still
+ // be on the tcb_sendq.
+ Result = CTEInterlockedDecrementLong(
+ &(SendReq->tsr_refcnt)
+ );
+
+ CTEAssert(Result >= 0);
+
+ if (Result <= 0) {
+ // If we've sent directly from this send, NULL out the next
+ // pointer for the last buffer in the chain.
+ if (SendReq->tsr_lastbuf != NULL) {
+ NDIS_BUFFER_LINKAGE(SendReq->tsr_lastbuf) = NULL;
+ SendReq->tsr_lastbuf = NULL;
+ }
+
+ (*Req->tr_rtn)(Req->tr_context, Status, 0);
+ FreeSendReq(SendReq);
+ } else {
+ // The send request will be freed when all outstanding references
+ // to it have completed.
+ SendReq->tsr_req.tr_status = Status;
+ }
+ }
+
+ while (ClosedTCB->tcb_rcvhead != NULL) {
+ TCPRcvReq *RcvReq;
+
+ RcvReq = ClosedTCB->tcb_rcvhead;
+ CTEStructAssert(RcvReq, trr);
+ ClosedTCB->tcb_rcvhead = RcvReq->trr_next;
+ (*RcvReq->trr_rtn)(RcvReq->trr_context, Status, 0);
+ FreeRcvReq(RcvReq);
+ }
+
+ while (ClosedTCB->tcb_exprcv != NULL) {
+ TCPRcvReq *RcvReq;
+
+ RcvReq = ClosedTCB->tcb_exprcv;
+ CTEStructAssert(RcvReq, trr);
+ ClosedTCB->tcb_exprcv = RcvReq->trr_next;
+ (*RcvReq->trr_rtn)(RcvReq->trr_context, Status, 0);
+ FreeRcvReq(RcvReq);
+ }
+
+ if (ClosedTCB->tcb_pendhead != NULL)
+ FreeRBChain(ClosedTCB->tcb_pendhead);
+
+ if (ClosedTCB->tcb_urgpending != NULL)
+ FreeRBChain(ClosedTCB->tcb_urgpending);
+
+ while (ClosedTCB->tcb_raq != NULL) {
+ TCPRAHdr *Hdr;
+
+ Hdr = ClosedTCB->tcb_raq;
+ CTEStructAssert(Hdr, trh);
+ ClosedTCB->tcb_raq = Hdr->trh_next;
+ if (Hdr->trh_buffer != NULL)
+ FreeRBChain(Hdr->trh_buffer);
+
+ CTEFreeMem(Hdr);
+ }
+
+ RemoveConnFromTCB(ClosedTCB);
+
+ if (OKToFree) {
+ FreeTCB(ClosedTCB);
+ } else {
+ CTEGetLock(&TCBTableLock, &TCBTableHandle);
+ ClosedTCB->tcb_walkcount--;
+ if (ClosedTCB->tcb_walkcount == 0) {
+ FreeTCB(ClosedTCB);
+ }
+ CTEFreeLock(&TCBTableLock, TCBTableHandle);
+ }
+
+}
+
+//* TryToCloseTCB - Try to close a TCB.
+//
+// Called when we need to close a TCB, but don't know if we can. If
+// the reference count is 0, we'll call CloseTCB to deal with it.
+// Otherwise we'll set the DELETE_PENDING bit and deal with it when
+// the ref. count goes to 0. We assume the TCB is locked when we are called.
+//
+// Input: ClosedTCB - TCB to be closed.
+// Reason - Reason we're closing.
+// Handle - Lock handle for TCB.
+//
+// Returns: Nothing.
+//
+void
+TryToCloseTCB(TCB *ClosedTCB, uchar Reason, CTELockHandle Handle)
+{
+ CTEStructAssert(ClosedTCB, tcb);
+ CTEAssert(ClosedTCB->tcb_state != TCB_CLOSED);
+
+ ClosedTCB->tcb_closereason |= Reason;
+
+ if (ClosedTCB->tcb_pending & DEL_PENDING) {
+ DEBUGCHK;
+ CTEFreeLock(&ClosedTCB->tcb_lock, Handle);
+ return;
+ }
+
+ ClosedTCB->tcb_pending |= DEL_PENDING;
+ ClosedTCB->tcb_slowcount++;
+ ClosedTCB->tcb_fastchk |= TCP_FLAG_SLOW;
+
+ if (ClosedTCB->tcb_refcnt == 0)
+ CloseTCB(ClosedTCB, Handle);
+ else {
+ CTEFreeLock(&ClosedTCB->tcb_lock, Handle);
+ }
+}
+
+
+//* DerefTCB - Dereference a TCB.
+//
+// Called when we're done with a TCB, and want to let exclusive user
+// have a shot. We dec. the refcount, and if it goes to zero and there
+// are pending actions, we'll perform one of the pending actions.
+//
+// Input: DoneTCB - TCB to be dereffed.
+// Handle - Lock handle to be used when freeing TCB lock.
+//
+// Returns: Nothing.
+//
+void
+DerefTCB(TCB *DoneTCB, CTELockHandle Handle)
+{
+
+ CTEAssert(DoneTCB->tcb_refcnt != 0);
+ if (--DoneTCB->tcb_refcnt == 0) {
+ if (DoneTCB->tcb_pending == 0) {
+ CTEFreeLock(&DoneTCB->tcb_lock, Handle);
+ return;
+ } else {
+ // BUGBUG handle pending actions.
+ if (DoneTCB->tcb_pending & DEL_PENDING)
+ CloseTCB(DoneTCB, Handle);
+ else
+ DEBUGCHK;
+ return;
+ }
+ }
+
+ CTEFreeLock(&DoneTCB->tcb_lock, Handle);
+ return;
+}
+
+
+//** TdiOpenConnection - Open a connection.
+//
+// This is the TDI Open Connection entry point. We open a connection,
+// and save the caller's connection context. A TCPConn structure is allocated
+// here, but a TCB isn't allocated until the Connect or Listen is done.
+//
+// Input: Request - Pointed to a TDI request structure.
+// Context - Connection context to be saved for connection.
+//
+// Returns: Status of attempt to open the connection.
+//
+TDI_STATUS
+TdiOpenConnection(PTDI_REQUEST Request, PVOID Context)
+{
+ TCPConn *NewConn; // The newly opened connection.
+ CTELockHandle Handle; // Lock handle for TCPConnTable.
+ uint ConnID; // New ConnID.
+ TDI_STATUS Status; // Status of this request.
+
+ NewConn = CTEAllocMem(sizeof(TCPConn));
+
+ if (NewConn != NULL) { // We allocated a connection.
+ CTEMemSet(NewConn, 0, sizeof(TCPConn));
+#ifdef DEBUG
+ NewConn->tc_sig = tc_signature;
+#endif
+ NewConn->tc_tcb = NULL;
+ NewConn->tc_ao = NULL;
+ NewConn->tc_context = Context;
+
+ CTEGetLock(&ConnTableLock, &Handle);
+ ConnID = GetConnID(NewConn);
+ if (ConnID != INVALID_CONN_ID) {
+ // We successfully got a ConnID.
+ Request->Handle.ConnectionContext = (CONNECTION_CONTEXT)ConnID;
+ NewConn->tc_refcnt = 0;
+ NewConn->tc_flags = 0;
+ NewConn->tc_tcbflags = NAGLING | (BSDUrgent ? BSD_URGENT : 0);
+ if (DefaultRcvWin != 0) {
+ NewConn->tc_window = DefaultRcvWin;
+ NewConn->tc_flags |= CONN_WINSET;
+ } else
+ NewConn->tc_window = DEFAULT_RCV_WIN;
+
+ NewConn->tc_donertn = DummyDone;
+ Status = TDI_SUCCESS;
+ } else {
+ CTEFreeMem(NewConn);
+ Status = TDI_NO_RESOURCES;
+ }
+
+ CTEFreeLock(&ConnTableLock, Handle);
+ return Status;
+ }
+
+ // Couldn't get a connection.
+ return TDI_NO_RESOURCES;
+
+}
+
+//* RemoveConnFromAO - Remove a connection from an AddrObj.
+//
+// A little utility routine to remove a connection from an AddrObj.
+// We run down the connections on the AO, and when we find him we splice
+// him out. We assume the caller holds the locks on the AddrObj and the
+// TCPConnTable lock.
+//
+// Input: AO - AddrObj to remove from.
+// Conn - Conn to remove.
+//
+// Returns: Nothing.
+//
+void
+RemoveConnFromAO(AddrObj *AO, TCPConn *Conn)
+{
+
+ CTEStructAssert(AO, ao);
+ CTEStructAssert(Conn, tc);
+
+ REMOVEQ(&Conn->tc_q);
+ Conn->tc_ao = NULL;
+
+
+}
+
+//* TdiCloseConnection - Close a connection.
+//
+// Called when the user is done with a connection, and wants to close it.
+// We look the connection up in our table, and if we find it we'll remove
+// the connection from the AddrObj it's associate with (if any). If there's
+// a TCB associated with the connection we'll close it also.
+//
+// There are some interesting wrinkles related to closing while a TCB
+// is still referencing the connection (i.e. tc_refcnt != 0) or while a
+// disassociate address is in progress. See below for more details.
+//
+// Input: Request - Request identifying connection to be closed.
+//
+// Returns: Status of attempt to close.
+//
+TDI_STATUS
+TdiCloseConnection(PTDI_REQUEST Request)
+{
+ uint ConnID = (uint)Request->Handle.ConnectionContext;
+ CTELockHandle TableHandle;
+ TCPConn *Conn;
+ TDI_STATUS Status;
+
+ CTEGetLock(&ConnTableLock, &TableHandle);
+
+ // We have the locks we need. Try to find a connection.
+ Conn = GetConnFromConnID(ConnID);
+
+ if (Conn != NULL) {
+ CTELockHandle TCBHandle;
+ TCB *ConnTCB;
+
+ // We found the connection. Free the ConnID and mark the connection
+ // as closing.
+
+ CTEStructAssert(Conn, tc);
+
+ FreeConnID(ConnID);
+
+ Conn->tc_flags |= CONN_CLOSING;
+
+ // See if there's a TCB referencing this connection.
+ // If there is, we'll need to wait until he's done before closing him.
+ // We'll hurry the process along if we still have a pointer to him.
+
+ if (Conn->tc_refcnt != 0) {
+ CTEReqCmpltRtn Rtn;
+ PVOID Context;
+
+ // A connection still references him. Save the current rtn stuff
+ // in case we are in the middle of disassociating him from an
+ // address, and store the caller's callback routine and our done
+ // routine.
+ Rtn = Conn->tc_rtn;
+ Context = Conn->tc_rtncontext;
+
+ Conn->tc_rtn = Request->RequestNotifyObject;
+ Conn->tc_rtncontext = Request->RequestContext;
+ Conn->tc_donertn = CloseDone;
+
+ // See if we're in the middle of disassociating him
+ if (Conn->tc_flags & CONN_DISACC) {
+
+ // We are disassociating him. We'll free the conn table lock
+ // now and fail the disassociate request. Note that when
+ // we free the lock the refcount could go to zero. This is
+ // OK, because we've already stored the neccessary info. in
+ // the connection so the caller will get called back if it
+ // does. From this point out we return PENDING, so a callback
+ // is OK. We've marked him as closing, so the disassoc done
+ // routine will bail out if we've interrupted him. If the ref.
+ // count does go to zero, Conn->tc_tcb would have to be NULL,
+ // so in that case we'll just fall out of this routine.
+
+ CTEFreeLock(&ConnTableLock, TableHandle);
+ (*Rtn)(Context, (uint) TDI_REQ_ABORTED, 0);
+ CTEGetLock(&ConnTableLock, &TableHandle);
+ }
+
+
+ ConnTCB = Conn->tc_tcb;
+ if (ConnTCB != NULL) {
+ CTEStructAssert(ConnTCB, tcb);
+ // We have a TCB. Take the lock on him and get ready to
+ // close him.
+ CTEGetLock(&ConnTCB->tcb_lock, &TCBHandle);
+ if (ConnTCB->tcb_state != TCB_CLOSED) {
+ ConnTCB->tcb_flags |= NEED_RST;
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+ if (!CLOSING(ConnTCB))
+ TryToCloseTCB(ConnTCB, TCB_CLOSE_ABORTED, TableHandle);
+ else
+ CTEFreeLock(&ConnTCB->tcb_lock, TableHandle);
+ return TDI_PENDING;
+ } else {
+ // He's already closing. This should be harmless, but check
+ // this case.
+ DEBUGCHK;
+ CTEFreeLock(&ConnTCB->tcb_lock, TCBHandle);
+ }
+ }
+ Status = TDI_PENDING;
+
+ } else {
+ // We have a connection that we can close. Finish the close.
+ Conn->tc_rtn = DummyCmplt;
+ CloseDone(Conn, TableHandle);
+ return TDI_SUCCESS;
+ }
+
+
+ } else
+ Status = TDI_INVALID_CONNECTION;
+
+ // We're done with the connection. Go ahead and free him.
+ CTEFreeLock(&ConnTableLock, TableHandle);
+
+ return Status;
+
+}
+
+
+//* TdiAssociateAddress - Associate an address with a connection.
+//
+// Called to associate an address with a connection. We do a minimal
+// amount of sanity checking, and then put the connection on the AddrObj's
+// list.
+//
+// Input: Request - Pointer to request structure for this request.
+// AddrHandle - Address handle to associate connection with.
+//
+// Returns: Status of attempt to associate.
+//
+TDI_STATUS
+TdiAssociateAddress(PTDI_REQUEST Request, HANDLE AddrHandle)
+{
+ CTELockHandle TableHandle, AOHandle;
+ AddrObj *AO;
+ uint ConnID = (uint)Request->Handle.ConnectionContext;
+ TCPConn *Conn;
+ TDI_STATUS Status;
+
+#ifdef VXD
+ AO = GetIndexedAO((uint)AddrHandle);
+ if (AO == NULL)
+ return TDI_ADDR_INVALID;
+#else
+ AO = (AddrObj *)AddrHandle;
+#endif
+ CTEStructAssert(AO, ao);
+
+ CTEGetLock(&ConnTableLock, &TableHandle);
+ CTEGetLock(&AO->ao_lock, &AOHandle);
+
+ Conn = GetConnFromConnID(ConnID);
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ if (Conn->tc_ao != NULL) {
+ // It's already associated. Error out.
+ DEBUGCHK;
+ Status = TDI_ALREADY_ASSOCIATED;
+ } else {
+ Conn->tc_ao = AO;
+ CTEAssert(Conn->tc_tcb == NULL);
+ ENQUEUE(&AO->ao_idleq, &Conn->tc_q);
+ Status = TDI_SUCCESS;
+ }
+ } else
+ Status = TDI_INVALID_CONNECTION;
+
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+ CTEFreeLock(&ConnTableLock, TableHandle);
+ return Status;
+}
+
+//* TdiDisAssociateAddress - Disassociate a connection from an address.
+//
+// The TDI entry point to disassociate a connection from an address. The
+// connection must actually be associated and not connected to anything.
+//
+// Input: Request - Pointer to the request structure for this
+// command.
+//
+// Returns: Status of request.
+//
+TDI_STATUS
+TdiDisAssociateAddress(PTDI_REQUEST Request)
+{
+ uint ConnID = (uint)Request->Handle.ConnectionContext;
+ CTELockHandle AOTableHandle, ConnTableHandle, AOHandle;
+ TCPConn *Conn;
+ AddrObj *AO;
+ TDI_STATUS Status;
+
+ CTEGetLock(&AddrObjTableLock, &AOTableHandle);
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+ Conn = GetConnFromConnID(ConnID);
+
+ if (Conn != NULL) {
+ // The connection actually exists!
+
+ CTEStructAssert(Conn, tc);
+ AO = Conn->tc_ao;
+ if (AO != NULL) {
+ CTEStructAssert(AO, ao);
+ // And it's associated.
+ CTEGetLock(&AO->ao_lock, &AOHandle);
+ // If there's no connection currently active, go ahead and remove
+ // him from the AddrObj. If a connection is active error the
+ // request out.
+ if (Conn->tc_tcb == NULL) {
+ if (Conn->tc_refcnt == 0) {
+ RemoveConnFromAO(AO, Conn);
+ Status = TDI_SUCCESS;
+ } else {
+ // He shouldn't be closing, or we couldn't have found him.
+ CTEAssert(!(Conn->tc_flags & CONN_CLOSING));
+
+ Conn->tc_rtn = Request->RequestNotifyObject;
+ Conn->tc_rtncontext = Request->RequestContext;
+ Conn->tc_donertn = DisassocDone;
+ Conn->tc_flags |= CONN_DISACC;
+ Status = TDI_PENDING;
+ }
+
+ } else
+ Status = TDI_CONNECTION_ACTIVE;
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+ } else
+ Status = TDI_NOT_ASSOCIATED;
+ } else
+ Status = TDI_INVALID_CONNECTION;
+
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ CTEFreeLock(&AddrObjTableLock, AOTableHandle);
+
+ return Status;
+
+}
+
+//* ProcessUserOptions - Process options from the user.
+//
+// A utility routine to process options from the user. We fill in the
+// optinfo structure, and if we have options we call ip to check on them.
+//
+// Input: Info - Info structure containing options to be processed.
+// OptInfo - Info structure to be filled in.
+//
+// Returns: TDI_STATUS of attempt.
+//
+TDI_STATUS
+ProcessUserOptions(PTDI_CONNECTION_INFORMATION Info, IPOptInfo *OptInfo)
+{
+ TDI_STATUS Status;
+
+ (*LocalNetInfo.ipi_initopts)(OptInfo);
+
+ if (Info != NULL && Info->Options != NULL) {
+ IP_STATUS OptStatus;
+
+ OptStatus = (*LocalNetInfo.ipi_copyopts)(Info->Options,
+ Info->OptionsLength, OptInfo);
+ if (OptStatus != IP_SUCCESS) {
+ if (OptStatus == IP_NO_RESOURCES)
+ Status = TDI_NO_RESOURCES;
+ else
+ Status = TDI_BAD_OPTION;
+ } else
+ Status = TDI_SUCCESS;
+ } else {
+ Status = TDI_SUCCESS;
+ }
+
+ return Status;
+
+
+}
+
+//* InitTCBFromConn - Initialize a TCB from information in a Connection.
+//
+// Called from Connect and Listen processing to initialize a new TCB from
+// information in the connection. We assume the AddrObjTableLock and
+// ConnTableLocks are held when we are called, or that the caller has some
+// other way of making sure that the referenced AO doesn't go away in the middle
+// of operation.
+//
+// Input: Conn - Connection to initialize from.
+// NewTCB - TCB to be initialized.
+// Addr - Remote addressing and option info for NewTCB.
+// AOLocked - True if the called has the address object locked.
+//
+// Returns: TDI_STATUS of init attempt.
+//
+TDI_STATUS
+InitTCBFromConn(TCPConn *Conn, TCB *NewTCB,
+ PTDI_CONNECTION_INFORMATION Addr, uint AOLocked)
+{
+ CTELockHandle AOHandle;
+ TDI_STATUS Status;
+
+ CTEStructAssert(Conn, tc);
+
+ // We have a connection. Make sure it's associated with an address and
+ // doesn't already have a TCB attached.
+
+ if (Conn->tc_flags & CONN_INVALID)
+ return TDI_INVALID_CONNECTION;
+
+ if (Conn->tc_tcb == NULL) {
+ AddrObj *ConnAO;
+
+ ConnAO = Conn->tc_ao;
+ if (ConnAO != NULL) {
+ CTEStructAssert(ConnAO, ao);
+
+ if (!AOLocked) {
+ CTEGetLock(&ConnAO->ao_lock, &AOHandle);
+ }
+ NewTCB->tcb_saddr = ConnAO->ao_addr;
+ NewTCB->tcb_sport = ConnAO->ao_port;
+ NewTCB->tcb_rcvind = ConnAO->ao_rcv;
+ NewTCB->tcb_ricontext = ConnAO->ao_rcvcontext;
+ if (NewTCB->tcb_rcvind == NULL)
+ NewTCB->tcb_rcvhndlr = PendData;
+ else
+ NewTCB->tcb_rcvhndlr = IndicateData;
+
+ NewTCB->tcb_conncontext = Conn->tc_context;
+ NewTCB->tcb_flags |= Conn->tc_tcbflags;
+ NewTCB->tcb_defaultwin = Conn->tc_window;
+ NewTCB->tcb_rcvwin = Conn->tc_window;
+
+ if (Conn->tc_flags & CONN_WINSET)
+ NewTCB->tcb_flags |= WINDOW_SET;
+
+ if (NewTCB->tcb_flags & KEEPALIVE) {
+ NewTCB->tcb_alive = TCPTime;
+ NewTCB->tcb_kacount = 0;
+ }
+
+ if (!AOLocked) {
+ CTEFreeLock(&ConnAO->ao_lock, AOHandle);
+ }
+
+ // If we've been given options, we need to process them now.
+ if (Addr != NULL && Addr->Options != NULL)
+ NewTCB->tcb_flags |= CLIENT_OPTIONS;
+ Status = ProcessUserOptions(Addr, &NewTCB->tcb_opt);
+
+ return Status;
+ } else
+ return TDI_NOT_ASSOCIATED;
+ } else
+ return TDI_CONNECTION_ACTIVE;
+
+}
+
+//* TdiConnect - Establish a connection.
+//
+// The TDI connection establishment routine. Called when the client wants to
+// establish a connection, we validate his incoming parameters and kick
+// things off by sending a SYN.
+//
+// Input: Request - The request structure for this command.
+// Timeout - How long to wait for the request. The format
+// of this time is system specific - we use
+// a macro to convert to ticks.
+// RequestAddr - Pointer to a TDI_CONNECTION_INFORMATION
+// structure describing the destination.
+// ReturnAddr - Pointer to where to return information.
+//
+// Returns: Status of attempt to connect.
+//
+TDI_STATUS
+TdiConnect(PTDI_REQUEST Request, void *TO,
+ PTDI_CONNECTION_INFORMATION RequestAddr,
+ PTDI_CONNECTION_INFORMATION ReturnAddr)
+{
+ TCPConnReq *ConnReq; // Connection request to use.
+ IPAddr DestAddr;
+ ushort DestPort;
+ uchar AddrType;
+ TCPConn *Conn;
+ TCB *NewTCB;
+ uint ConnID = (uint)Request->Handle.ConnectionContext;
+ CTELockHandle AOTableHandle, ConnTableHandle, AOHandle;
+ AddrObj *AO;
+ TDI_STATUS Status;
+ CTELockHandle TCBHandle;
+ IPAddr SrcAddr;
+ ushort MSS;
+ TCP_TIME *Timeout;
+
+ // First, get and validate the remote address.
+ if (RequestAddr == NULL || RequestAddr->RemoteAddress == NULL ||
+ !GetAddress((PTRANSPORT_ADDRESS)RequestAddr->RemoteAddress, &DestAddr,
+ &DestPort))
+ return TDI_BAD_ADDR;
+
+ AddrType = (*LocalNetInfo.ipi_getaddrtype)(DestAddr);
+
+ if (AddrType == DEST_INVALID || IS_BCAST_DEST(AddrType) || DestPort == 0)
+ return TDI_BAD_ADDR;
+
+ // Now get a connection request. If we can't, bail out now.
+ ConnReq = GetConnReq();
+ if (ConnReq == NULL)
+ return TDI_NO_RESOURCES;
+
+ // Get a TCB, assuming we'll need one.
+ NewTCB = AllocTCB();
+ if (NewTCB == NULL) {
+ // Couldn't get a TCB.
+ FreeConnReq(ConnReq);
+ return TDI_NO_RESOURCES;
+ }
+
+ Timeout = (TCP_TIME *)TO;
+
+ if (Timeout != NULL && !INFINITE_CONN_TO(*Timeout)) {
+ ulong Ticks = TCP_TIME_TO_TICKS(*Timeout);
+ if (Ticks > MAX_CONN_TO_TICKS)
+ Ticks = MAX_CONN_TO_TICKS;
+ else
+ Ticks++;
+ ConnReq->tcr_timeout = (ushort)Ticks;
+ } else
+ ConnReq->tcr_timeout = 0;
+
+ ConnReq->tcr_flags = 0;
+ ConnReq->tcr_conninfo = ReturnAddr;
+ ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject;
+ ConnReq->tcr_req.tr_context = Request->RequestContext;
+ NewTCB->tcb_daddr = DestAddr;
+ NewTCB->tcb_dport = DestPort;
+
+ // Now find the real connection. If we find it, we'll make sure it's
+ // associated.
+ CTEGetLock(&AddrObjTableLock, &AOTableHandle);
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+ Conn = GetConnFromConnID(ConnID);
+ if (Conn != NULL) {
+ uint Inserted;
+
+ CTEStructAssert(Conn, tc);
+
+ AO = Conn->tc_ao;
+
+ if (AO != NULL) {
+ CTEGetLock(&AO->ao_lock, &AOHandle);
+
+ CTEStructAssert(AO, ao);
+ Status = InitTCBFromConn(Conn, NewTCB, RequestAddr, TRUE);
+ if (Status == TDI_SUCCESS) {
+
+ // We've processed the options, and we know the destination
+ // address is good, and we have all the resources we need,
+ // so we can go ahead and open an RCE. If this works we'll
+ // put the TCB into the Connection and send a SYN.
+
+ // We're done with the AddrObjTable now, so we can free it's
+ // lock.
+ NewTCB->tcb_flags |= ACTIVE_OPEN;
+
+ CTEFreeLock(&AddrObjTableLock, AOHandle);
+
+ SrcAddr = (*LocalNetInfo.ipi_openrce)(DestAddr,
+ NewTCB->tcb_saddr, &NewTCB->tcb_rce, &AddrType, &MSS,
+ &NewTCB->tcb_opt);
+
+ if (IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR)) {
+ // The request failed. We know the destination is good
+ // (we verified it above), so it must be unreachable.
+ CTEFreeLock(&AO->ao_lock, ConnTableHandle);
+ Status = TDI_DEST_UNREACHABLE;
+ goto error;
+ }
+
+ // OK, the RCE open worked. Enter the TCB into the connection.
+ CTEGetLock(&NewTCB->tcb_lock, &TCBHandle);
+ Conn->tc_tcb = NewTCB;
+ Conn->tc_refcnt++;
+ NewTCB->tcb_conn = Conn;
+ REMOVEQ(&Conn->tc_q);
+ ENQUEUE(&AO->ao_activeq, &Conn->tc_q);
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+ CTEFreeLock(&AO->ao_lock, ConnTableHandle);
+
+
+ // If the caller didn't specify a local address, use what
+ // IP provided.
+ if (IP_ADDR_EQUAL(NewTCB->tcb_saddr, NULL_IP_ADDR))
+ NewTCB->tcb_saddr = SrcAddr;
+
+ // Until we have MTU discovery in place, hold the MSS down
+ // to 536 if we're going off net.
+ MSS -= sizeof(TCPHeader);
+
+ if (!PMTUDiscovery && IS_OFFNET_DEST(AddrType)) {
+ NewTCB->tcb_mss = MIN(MSS, MAX_REMOTE_MSS) -
+ NewTCB->tcb_opt.ioi_optlength;
+
+ CTEAssert(NewTCB->tcb_mss > 0);
+ }
+ else {
+ if (PMTUDiscovery)
+ NewTCB->tcb_opt.ioi_flags = IP_FLAG_DF;
+ NewTCB->tcb_mss = MSS - NewTCB->tcb_opt.ioi_optlength;
+
+ CTEAssert(NewTCB->tcb_mss > 0);
+ }
+
+ //
+ // Initialize the remote mss in case we receive an MTU change
+ // from IP before the remote SYN arrives. The remmms will
+ // be replaced when the remote SYN is processed.
+ //
+ NewTCB->tcb_remmss = NewTCB->tcb_mss;
+
+ // Now initialize our send state.
+ InitSendState(NewTCB);
+ NewTCB->tcb_refcnt = 1;
+ NewTCB->tcb_state = TCB_SYN_SENT;
+ TStats.ts_activeopens++;
+
+ // Need to put the ConnReq on the TCB now, in case the timer fires
+ // after we've inserted.
+
+ NewTCB->tcb_connreq = ConnReq;
+ CTEFreeLock(&NewTCB->tcb_lock, AOTableHandle);
+
+ Inserted = InsertTCB(NewTCB);
+ CTEGetLock(&NewTCB->tcb_lock, &TCBHandle);
+
+ if (!Inserted) {
+ // Insert failed. We must already have a connection. Pull
+ // the connreq from the TCB first, so we can return the correct
+ // error code for it.
+ NewTCB->tcb_connreq = NULL;
+ NewTCB->tcb_refcnt--;
+ TryToCloseTCB(NewTCB, TCB_CLOSE_ABORTED, TCBHandle);
+ FreeConnReq(ConnReq);
+ return TDI_ADDR_IN_USE;
+ }
+
+ // If it's closing somehow, stop now. It can't have gone to
+ // closed, as we hold a reference on it. It could have gone
+ // to some other state (for example SYN-RCVD) so we need to
+ // check that now too.
+ if (!CLOSING(NewTCB) && NewTCB->tcb_state == TCB_SYN_SENT) {
+ SendSYN(NewTCB, TCBHandle);
+ CTEGetLock(&NewTCB->tcb_lock, &TCBHandle);
+ }
+ DerefTCB(NewTCB, TCBHandle);
+
+ return TDI_PENDING;
+ } else
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+
+ } else
+ Status = TDI_NOT_ASSOCIATED;
+ } else
+ Status = TDI_INVALID_CONNECTION;
+
+ CTEFreeLock(&AddrObjTableLock, ConnTableHandle);
+error:
+ CTEFreeLock(&ConnTableLock, AOTableHandle);
+ FreeConnReq(ConnReq);
+ FreeTCB(NewTCB);
+ return Status;
+
+
+}
+
+//* TdiListen - Listen for a connection.
+//
+// The TDI listen handling routine. Called when the client wants to
+// post a listen, we validate his incoming parameters, allocate a TCB
+// and return.
+//
+// Input: Request - The request structure for this command.
+// Flags - Listen flags for the listen.
+// AcceptableAddr - Pointer to a TDI_CONNECTION_INFORMATION
+// structure describing acceptable remote
+// addresses.
+// ConnectedAddr - Pointer to where to return information
+// about the address we connected to.
+//
+// Returns: Status of attempt to connect.
+//
+TDI_STATUS
+TdiListen(PTDI_REQUEST Request, ushort Flags,
+ PTDI_CONNECTION_INFORMATION AcceptableAddr,
+ PTDI_CONNECTION_INFORMATION ConnectedAddr)
+{
+ TCPConnReq *ConnReq; // Connection request to use.
+ IPAddr RemoteAddr; // Remote address to take conn. from.
+ ushort RemotePort; // Acceptable remote port.
+ uchar AddrType; // Type of remote address.
+ TCPConn *Conn; // Pointer to the Connection being
+ // listened upon.
+ TCB *NewTCB; // Pointer to the new TCB we'll use.
+ uint ConnID = (uint)Request->Handle.ConnectionContext;
+ CTELockHandle AOTableHandle, ConnTableHandle;
+ TDI_STATUS Status;
+
+ // If we've been given remote addressing criteria, check it out.
+ if (AcceptableAddr != NULL && AcceptableAddr->RemoteAddress != NULL) {
+ if (!GetAddress((PTRANSPORT_ADDRESS)AcceptableAddr->RemoteAddress,
+ &RemoteAddr, &RemotePort))
+ return TDI_BAD_ADDR;
+
+ if (!IP_ADDR_EQUAL(RemoteAddr, NULL_IP_ADDR)) {
+ AddrType = (*LocalNetInfo.ipi_getaddrtype)(RemoteAddr);
+
+ if (AddrType == DEST_INVALID || IS_BCAST_DEST(AddrType))
+ return TDI_BAD_ADDR;
+ }
+ } else {
+ RemoteAddr = NULL_IP_ADDR;
+ RemotePort = 0;
+ }
+
+ // The remote address is valid. Get a ConnReq, and maybe a TCB.
+ ConnReq = GetConnReq();
+ if (ConnReq == NULL)
+ return TDI_NO_RESOURCES; // Couldn't get one.
+
+ // Now try to get a TCB.
+ NewTCB = AllocTCB();
+ if (NewTCB == NULL) {
+ // Couldn't get a TCB. Return an error.
+ FreeConnReq(ConnReq);
+ return TDI_NO_RESOURCES;
+ }
+
+ // We have the resources we need. Initialize them, and then check the
+ // state of the connection.
+ ConnReq->tcr_flags = Flags;
+ ConnReq->tcr_conninfo = ConnectedAddr;
+ ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject;
+ ConnReq->tcr_req.tr_context = Request->RequestContext;
+ NewTCB->tcb_connreq = ConnReq;
+ NewTCB->tcb_daddr = RemoteAddr;
+ NewTCB->tcb_dport = RemotePort;
+ NewTCB->tcb_state = TCB_LISTEN;
+
+ // Now find the real connection. If we find it, we'll make sure it's
+ // associated.
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+ Conn = GetConnFromConnID(ConnID);
+ if (Conn != NULL) {
+ CTELockHandle AddrHandle;
+ AddrObj *ConnAO;
+
+ CTEStructAssert(Conn, tc);
+ // We have a connection. Make sure it's associated with an address and
+ // doesn't already have a TCB attached.
+ ConnAO = Conn->tc_ao;
+
+ if (ConnAO != NULL) {
+ CTEStructAssert(ConnAO, ao);
+ CTEGetLockAtDPC(&ConnAO->ao_lock, &AddrHandle);
+
+ Status = InitTCBFromConn(Conn, NewTCB, AcceptableAddr, TRUE);
+
+ if (Status == TDI_SUCCESS) {
+
+ // The initialization worked. Assign the new TCB to the connection,
+ // and return.
+
+ REMOVEQ(&Conn->tc_q);
+ ENQUEUE(&ConnAO->ao_listenq, &Conn->tc_q);
+
+ Conn->tc_tcb = NewTCB;
+ NewTCB->tcb_conn = Conn;
+ Conn->tc_refcnt++;
+
+ ConnAO->ao_listencnt++;
+ CTEFreeLockFromDPC(&ConnAO->ao_lock, AddrHandle);
+
+ Status = TDI_PENDING;
+ } else {
+ FreeTCB(NewTCB);
+ CTEFreeLockFromDPC(&ConnAO->ao_lock, AddrHandle);
+ }
+ } else {
+ FreeTCB(NewTCB);
+ Status = TDI_NOT_ASSOCIATED;
+ }
+ } else {
+ FreeTCB(NewTCB);
+ Status = TDI_INVALID_CONNECTION;
+ }
+
+ // We're all done. Free the locks and get out.
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ return Status;
+
+}
+
+//* InitRCE - Initialize an RCE.
+//
+// A utility routine to open and RCE and determine the maximum segment size
+// for a connections. This function is called with the TCB lock held
+// when transitioning out of the SYN_SENT or LISTEN states.
+//
+// Input: NewTCB - TCB for which an RCE is to be opened.
+//
+// Returns: Nothing.
+//
+void
+InitRCE(TCB *NewTCB)
+{
+ uchar DType;
+ ushort MSS;
+
+ // Open an RCE for this connection.
+ (*LocalNetInfo.ipi_openrce)(NewTCB->tcb_daddr, NewTCB->tcb_saddr,
+ &NewTCB->tcb_rce, &DType, &MSS, &NewTCB->tcb_opt);
+
+ // Until we have Dynamic MTU discovery in place, force MTU down.
+ MSS -= sizeof(TCPHeader);
+ if (!PMTUDiscovery && (DType & DEST_OFFNET_BIT)) {
+ NewTCB->tcb_mss = MIN(NewTCB->tcb_remmss, MIN(MSS, MAX_REMOTE_MSS)
+ - NewTCB->tcb_opt.ioi_optlength);
+
+ CTEAssert(NewTCB->tcb_mss > 0);
+ }
+ else {
+ if (PMTUDiscovery)
+ NewTCB->tcb_opt.ioi_flags = IP_FLAG_DF;
+ MSS -= NewTCB->tcb_opt.ioi_optlength;
+ NewTCB->tcb_mss = MIN(NewTCB->tcb_remmss, MSS);
+
+ CTEAssert(NewTCB->tcb_mss > 0);
+
+ }
+
+
+}
+
+//* AcceptConn - Accept a connection on a TCB.
+//
+// Called to accept a connection on a TCB, either from an incoming
+// receive segment or via a user's accept. We initialize the RCE
+// and the send state, and send out a SYN. We assume the TCB is locked
+// and referenced when we get it.
+//
+// Input: AcceptTCB - TCB to accept on.
+// Handle - Lock handle for TCB.
+//
+// Returns: Nothing.
+//
+void
+AcceptConn(TCB *AcceptTCB, CTELockHandle Handle)
+{
+ CTEStructAssert(AcceptTCB, tcb);
+ CTEAssert(AcceptTCB->tcb_refcnt != 0);
+
+ InitRCE(AcceptTCB);
+ InitSendState(AcceptTCB);
+
+ AdjustRcvWin(AcceptTCB);
+ SendSYN(AcceptTCB, Handle);
+
+ CTEGetLock(&AcceptTCB->tcb_lock, &Handle);
+
+ DerefTCB(AcceptTCB, Handle);
+}
+
+//* TdiAccept - Accept a connection.
+//
+// The TDI accept routine. Called when the client wants to
+// accept a connection for which a listen had previously completed. We
+// examine the state of the connection - it has to be in SYN-RCVD, with
+// a TCB, with no pending connreq, etc.
+//
+// Input: Request - The request structure for this command.
+// AcceptInfo - Pointer to a TDI_CONNECTION_INFORMATION
+// structure describing option information
+// for this accept.
+// ConnectedIndo - Pointer to where to return information
+// about the address we connected to.
+//
+// Returns: Status of attempt to connect.
+//
+TDI_STATUS
+TdiAccept(PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION AcceptInfo,
+ PTDI_CONNECTION_INFORMATION ConnectedInfo)
+{
+ TCPConnReq *ConnReq; // ConnReq we'll use for this connection.
+ uint ConnID = (uint)Request->Handle.ConnectionContext;
+ TCPConn *Conn; // Connection being accepted upon.
+ TCB *AcceptTCB; // TCB for Conn.
+ CTELockHandle ConnTableHandle; // Lock handle for connection table.
+ CTELockHandle TCBHandle; // Lock handle for TCB.
+ TDI_STATUS Status;
+
+ // First, get the ConnReq we'll need.
+ ConnReq = GetConnReq();
+ if (ConnReq == NULL)
+ return TDI_NO_RESOURCES;
+
+ ConnReq->tcr_conninfo = ConnectedInfo;
+ ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject;
+ ConnReq->tcr_req.tr_context = Request->RequestContext;
+
+ // Now look up the connection.
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+ Conn = GetConnFromConnID(ConnID);
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ // We have the connection. Make sure is has a TCB, and that the
+ // TCB is in the SYN-RCVD state, etc.
+ AcceptTCB = Conn->tc_tcb;
+
+ if (AcceptTCB != NULL) {
+ CTEStructAssert(AcceptTCB, tcb);
+
+ CTEGetLock(&AcceptTCB->tcb_lock, &TCBHandle);
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+
+ if (!CLOSING(AcceptTCB) && AcceptTCB->tcb_state == TCB_SYN_RCVD) {
+ // State is valid. Make sure this TCB had a delayed accept on
+ // it, and that there is currently no connect request pending.
+ if (!(AcceptTCB->tcb_flags & CONN_ACCEPTED) &&
+ AcceptTCB->tcb_connreq == NULL) {
+
+ // If the caller gave us options, they'll override any
+ // that are already present, if they're valid.
+ if (AcceptInfo != NULL && AcceptInfo->Options != NULL) {
+ IPOptInfo TempOptInfo;
+
+ // We have options. Copy them to make sure they're valid.
+ Status = ProcessUserOptions(AcceptInfo, &TempOptInfo);
+ if (Status == TDI_SUCCESS) {
+ (*LocalNetInfo.ipi_freeopts)(&AcceptTCB->tcb_opt);
+ AcceptTCB->tcb_opt = TempOptInfo;
+ AcceptTCB->tcb_flags |= CLIENT_OPTIONS;
+ } else
+ goto connerror;
+ }
+
+ AcceptTCB->tcb_connreq = ConnReq;
+ AcceptTCB->tcb_flags |= CONN_ACCEPTED;
+ AcceptTCB->tcb_refcnt++;
+ // Everything's set. Accept the connection now.
+ AcceptConn(AcceptTCB, ConnTableHandle);
+ return TDI_PENDING;
+ }
+ }
+connerror:
+ CTEFreeLock(&AcceptTCB->tcb_lock, ConnTableHandle);
+ Status = TDI_INVALID_CONNECTION;
+ goto error;
+ }
+ }
+ Status = TDI_INVALID_CONNECTION;
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+
+error:
+ FreeConnReq(ConnReq);
+ return Status;
+
+}
+
+//* TdiDisConnect - Disconnect a connection.
+//
+// The TDI disconnection routine. Called when the client wants to disconnect
+// a connection. There are two types of disconnection we support, graceful
+// and abortive. A graceful close will cause us to send a FIN and not complete
+// the request until we get the ACK back. An abortive close causes us to send
+// a RST. In that case we'll just get things going and return immediately.
+//
+// Input: Request - The request structure for this command.
+// Timeout - How long to wait for the request. The format
+// of this time is system specific - we use
+// a macro to convert to ticks.
+// Flags - Flags indicating type of disconnect.
+// DiscConnInfo - Pointer to a TDI_CONNECTION_INFORMATION
+// structure giving disconnection info. Ignored
+// for this request.
+// ReturnInfo - Pointer to where to return information.
+// Ignored for this request.
+//
+// Returns: Status of attempt to disconnect.
+//
+TDI_STATUS
+TdiDisconnect(PTDI_REQUEST Request, void *TO, ushort Flags,
+ PTDI_CONNECTION_INFORMATION DiscConnInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo)
+{
+ TCPConnReq *ConnReq; // Connection request to use.
+ TCPConn *Conn;
+ TCB *DiscTCB;
+ CTELockHandle ConnTableHandle, TCBHandle;
+ TDI_STATUS Status;
+ TCP_TIME *Timeout;
+
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+
+ Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ DiscTCB = Conn->tc_tcb;
+ if (DiscTCB != NULL) {
+ CTEStructAssert(DiscTCB, tcb);
+ CTEGetLock(&DiscTCB->tcb_lock, &TCBHandle);
+
+ // We have the TCB. See what kind of disconnect this is.
+ if (Flags & TDI_DISCONNECT_ABORT) {
+ // This is an abortive disconnect. If we're not already
+ // closed or closing, blow the connection away.
+ if (DiscTCB->tcb_state != TCB_CLOSED) {
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+
+ if (!CLOSING(DiscTCB)) {
+ DiscTCB->tcb_flags |= NEED_RST;
+ TryToCloseTCB(DiscTCB, TCB_CLOSE_ABORTED,
+ ConnTableHandle);
+ } else
+ CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle);
+
+ return TDI_SUCCESS;
+ } else {
+ // The TCB isn't connected.
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+ CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle);
+ DEBUGCHK;
+ return TDI_INVALID_STATE;
+ }
+ } else {
+ // This is not an abortive close. For graceful close we'll need
+ // a ConnReq.
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+
+ // Make sure we aren't in the middle of an abortive close.
+ if (CLOSING(DiscTCB)) {
+ CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle);
+ return TDI_INVALID_CONNECTION;
+ }
+
+ ConnReq = GetConnReq();
+ if (ConnReq != NULL) {
+ // Got the ConnReq. See if this is a DISCONNECT_WAIT
+ // primitive or not.
+
+ ConnReq->tcr_flags = 0;
+ ConnReq->tcr_conninfo = NULL;
+ ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject;
+ ConnReq->tcr_req.tr_context = Request->RequestContext;
+
+ if (!(Flags & TDI_DISCONNECT_WAIT)) {
+ Timeout = (TCP_TIME *)TO;
+
+ if (Timeout != NULL && !INFINITE_CONN_TO(*Timeout)) {
+ ulong Ticks = TCP_TIME_TO_TICKS(*Timeout);
+ if (Ticks > MAX_CONN_TO_TICKS)
+ Ticks = MAX_CONN_TO_TICKS;
+ else
+ Ticks++;
+ ConnReq->tcr_timeout = (ushort)Ticks;
+ } else
+ ConnReq->tcr_timeout = 0;
+
+ // OK, we're just about set. We need to update the TCB
+ // state, and send the FIN.
+ if (DiscTCB->tcb_state == TCB_ESTAB) {
+ DiscTCB->tcb_state = TCB_FIN_WAIT1;
+
+ // Since we left established, we're off the fast
+ // receive path.
+ DiscTCB->tcb_slowcount++;
+ DiscTCB->tcb_fastchk |= TCP_FLAG_SLOW;
+ } else
+ if (DiscTCB->tcb_state == TCB_CLOSE_WAIT)
+ DiscTCB->tcb_state = TCB_LAST_ACK;
+ else {
+ CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle);
+ FreeConnReq(ConnReq);
+ return TDI_INVALID_STATE;
+ }
+
+ TStats.ts_currestab--; // Update SNMP info.
+ CTEAssert(*(int *)&TStats.ts_currestab >= 0);
+
+ CTEAssert(DiscTCB->tcb_connreq == NULL);
+ DiscTCB->tcb_connreq = ConnReq;
+ DiscTCB->tcb_flags |= FIN_NEEDED;
+ DiscTCB->tcb_refcnt++;
+#ifdef VXD
+ CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle);
+ TCPSend(DiscTCB);
+#else
+ TCPSend(DiscTCB, ConnTableHandle);
+#endif
+ return TDI_PENDING;
+ } else {
+ // This is a DISC_WAIT request.
+ ConnReq->tcr_timeout = 0;
+ if (DiscTCB->tcb_discwait == NULL) {
+ DiscTCB->tcb_discwait = ConnReq;
+ Status = TDI_PENDING;
+ } else
+ Status = TDI_INVALID_STATE;
+
+ CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle);
+ return Status;
+ }
+ } else {
+ // Couldn't get a ConnReq.
+ CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle);
+ return TDI_NO_RESOURCES;
+ }
+ }
+ }
+ }
+
+ // No Conn, or no TCB on conn. Return an error.
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ return TDI_INVALID_CONNECTION;
+}
+
+//* OKToNotify - See if it's OK to notify about a DISC.
+//
+// A little utility function, called to see it it's OK to notify the client
+// of an incoming FIN.
+//
+// Input: NotifyTCB - TCB to check.
+//
+// Returns: TRUE if it's OK, False otherwise.
+//
+uint
+OKToNotify(TCB *NotifyTCB)
+{
+ CTEStructAssert(NotifyTCB, tcb);
+ if (NotifyTCB->tcb_pendingcnt == 0 && NotifyTCB->tcb_urgcnt == 0 &&
+ NotifyTCB->tcb_rcvhead == NULL && NotifyTCB->tcb_exprcv == NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+//* NotifyOfDisc - Notify a client that a TCB is being disconnected.
+//
+// Called when we're disconnecting a TCB because we've received a FIN or
+// RST from the remote peer, or because we're aborting for some reason.
+// We'll complete a DISCONNECT_WAIT request if we have one, or try and
+// issue an indication otherwise. This is only done if we're in a synchronized
+// state and not in TIMED-WAIT.
+//
+// Input: DiscTCB - Pointer to TCB we're notifying.
+// Status - Status code for notification.
+//
+// Returns: Nothing.
+//
+void
+NotifyOfDisc(TCB *DiscTCB, IPOptInfo *DiscInfo, TDI_STATUS Status)
+{
+ CTELockHandle TCBHandle, AOTHandle, ConnTHandle;
+ TCPConnReq *DiscReq;
+ TCPConn *Conn;
+ AddrObj *DiscAO;
+ PVOID ConnContext;
+
+ CTEStructAssert(DiscTCB, tcb);
+ CTEAssert(DiscTCB->tcb_refcnt != 0);
+
+ CTEGetLock(&DiscTCB->tcb_lock, &TCBHandle);
+ if (SYNC_STATE(DiscTCB->tcb_state) &&
+ !(DiscTCB->tcb_flags & DISC_NOTIFIED)) {
+
+ // We can't notify him if there's still data to be taken.
+ if (Status == TDI_GRACEFUL_DISC && !OKToNotify(DiscTCB)) {
+ DiscTCB->tcb_flags |= DISC_PENDING;
+ CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle);
+ return;
+ }
+
+ DiscTCB->tcb_flags |= DISC_NOTIFIED;
+ DiscTCB->tcb_flags &= ~DISC_PENDING;
+
+ // We're in a state where a disconnect is meaningful, and we haven't
+ // already notified the client.
+
+ // See if we have a DISC-WAIT request pending.
+ if ((DiscReq = DiscTCB->tcb_discwait) != NULL) {
+ // We have a disconnect wait request. Complete it and we're done.
+ DiscTCB->tcb_discwait = NULL;
+ CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle);
+ (*DiscReq->tcr_req.tr_rtn)(DiscReq->tcr_req.tr_context, Status, 0);
+ FreeConnReq(DiscReq);
+ return;
+
+ }
+
+ // No DISC-WAIT. Find the AddrObj for the connection, and see if there
+ // is a disconnect handler registered.
+
+ ConnContext = DiscTCB->tcb_conncontext;
+ CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle);
+
+ CTEGetLock(&AddrObjTableLock, &AOTHandle);
+ CTEGetLock(&ConnTableLock, &ConnTHandle);
+ if ((Conn = DiscTCB->tcb_conn) != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ DiscAO = Conn->tc_ao;
+ if (DiscAO != NULL) {
+ CTELockHandle AOHandle;
+ PDisconnectEvent DiscEvent;
+ PVOID DiscContext;
+
+
+ CTEStructAssert(DiscAO, ao);
+ CTEGetLock(&DiscAO->ao_lock, &AOHandle);
+ CTEFreeLock(&ConnTableLock, AOHandle);
+ CTEFreeLock(&AddrObjTableLock, ConnTHandle);
+
+ DiscEvent = DiscAO->ao_disconnect;
+ DiscContext = DiscAO->ao_disconncontext;
+
+ if (DiscEvent != NULL) {
+ uint InfoLength;
+ PVOID Info;
+
+ REF_AO(DiscAO);
+ CTEFreeLock(&DiscAO->ao_lock, AOTHandle);
+
+ if (DiscInfo != NULL) {
+ InfoLength = (uint)DiscInfo->ioi_optlength;
+ Info = DiscInfo->ioi_options;
+ } else {
+ InfoLength = 0;
+ Info = NULL;
+ }
+
+ IF_TCPDBG(TCP_DEBUG_CLOSE) {
+ TCPTRACE(("TCP: indicating %s disconnect\n",
+ (Status == TDI_GRACEFUL_DISC) ? "graceful" :
+ "abortive"
+ ));
+ }
+
+ (*DiscEvent)(DiscContext,
+ ConnContext, 0,
+ NULL, InfoLength, Info, (Status == TDI_GRACEFUL_DISC) ?
+ TDI_DISCONNECT_RELEASE : TDI_DISCONNECT_ABORT);
+
+ DELAY_DEREF_AO(DiscAO);
+ return;
+ } else {
+ CTEFreeLock(&DiscAO->ao_lock, AOTHandle);
+ return;
+ }
+ }
+ }
+
+ CTEFreeLock(&ConnTableLock, ConnTHandle);
+ CTEFreeLock(&AddrObjTableLock, AOTHandle);
+ return;
+
+ }
+ CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle);
+
+}
+
+//* GracefulClose - Complete the transition to a gracefully closed state.
+//
+// Called when we need to complete the transition to a gracefully closed
+// state, either TIME_WAIT or CLOSED. This completion involves removing
+// the TCB from it's associated connection (if it has one), notifying the
+// upper layer client either via completing a request or calling a disc.
+// notification handler, and actually doing the transition.
+//
+// The tricky part here is if we need to notify him (instead of completing
+// a graceful disconnect request). We can't notify him if there is pending
+// data on the connection, so in that case we have to pend the disconnect
+// notification until we deliver the data.
+//
+// Input: CloseTCB - TCB to transition.
+// ToTimeWait - True if we're going to TIME_WAIT, False if
+// we're going to close the TCB.
+// Notify - True if we're going to transition via notification,
+// False if we're going to transition by completing
+// a disconnect request.
+// Handle - Lock handle for TCB.
+//
+// Returns: Nothing.
+//
+void
+GracefulClose(TCB *CloseTCB, uint ToTimeWait, uint Notify, CTELockHandle Handle)
+{
+
+ CTEStructAssert(CloseTCB, tcb);
+
+ CTEAssert(CloseTCB->tcb_refcnt != 0);
+
+ // First, see if we need to notify the client of a FIN.
+ if (Notify) {
+ // We do need to notify him. See if it's OK to do so.
+ if (OKToNotify(CloseTCB)) {
+ // We can notify him. Change his state, pull him from the conn.,
+ // and notify him.
+ if (ToTimeWait) {
+ // Save the time we went into time wait, in case we need to
+ // scavenge.
+ CloseTCB->tcb_alive = CTESystemUpTime();
+ CloseTCB->tcb_state = TCB_TIME_WAIT;
+ CTEFreeLock(&CloseTCB->tcb_lock, Handle);
+ } else {
+ // He's going to close. Mark him as closing with TryToCloseTCB
+ // (he won't actually close since we have a ref. on him). We
+ // do this so that anyone touching him after we free the
+ // lock will fail.
+ TryToCloseTCB(CloseTCB, TDI_SUCCESS, Handle);
+ }
+
+
+ RemoveTCBFromConn(CloseTCB);
+ NotifyOfDisc(CloseTCB, NULL, TDI_GRACEFUL_DISC);
+
+ } else {
+ // Can't notify him now. Set the appropriate flags, and return.
+ CloseTCB->tcb_flags |= (GC_PENDING | (ToTimeWait ? TW_PENDING : 0));
+ DerefTCB(CloseTCB, Handle);
+ return;
+ }
+ } else {
+ // We're not notifying this guy, we just need to complete a conn. req.
+ // We need to check and see if he's been notified, and if not
+ // we'll complete the request and notify him later.
+ if (CloseTCB->tcb_flags & DISC_NOTIFIED) {
+ // He's been notified.
+ if (ToTimeWait) {
+ // Save the time we went into time wait, in case we need to
+ // scavenge.
+ CloseTCB->tcb_alive = CTESystemUpTime();
+ CloseTCB->tcb_state = TCB_TIME_WAIT;
+ CTEFreeLock(&CloseTCB->tcb_lock, Handle);
+ } else {
+ // Mark him as closed. See comments above.
+ TryToCloseTCB(CloseTCB, TDI_SUCCESS, Handle);
+ }
+
+ RemoveTCBFromConn(CloseTCB);
+
+ CTEGetLock(&CloseTCB->tcb_lock, &Handle);
+ CompleteConnReq(CloseTCB, NULL, TDI_SUCCESS);
+ CTEFreeLock(&CloseTCB->tcb_lock, Handle);
+ } else {
+ // He hasn't been notified. He should be pending already.
+ CTEAssert(CloseTCB->tcb_flags & DISC_PENDING);
+ CloseTCB->tcb_flags |= (GC_PENDING | (ToTimeWait ? TW_PENDING : 0));
+
+ CompleteConnReq(CloseTCB, NULL, TDI_SUCCESS);
+
+ DerefTCB(CloseTCB, Handle);
+ return;
+ }
+ }
+
+ // If we're going to TIME_WAIT, start the TIME_WAIT timer now.
+ // Otherwise close the TCB.
+ CTEGetLock(&CloseTCB->tcb_lock, &Handle);
+ if (!CLOSING(CloseTCB) && ToTimeWait) {
+ START_TCB_TIMER(CloseTCB->tcb_rexmittimer, MAX_REXMIT_TO);
+ CTEFreeLock(&CloseTCB->tcb_lock, Handle);
+ RemoveConnFromTCB(CloseTCB);
+ CTEGetLock(&CloseTCB->tcb_lock, &Handle);
+
+ }
+
+ DerefTCB(CloseTCB, Handle);
+
+}
+
+//* ConnCheckPassed - Check to see if we have exceeded the connect limit
+//
+// Called when a SYN is received to determine whether we will accept
+// the incoming connection. If the is an empty slot or if the IPAddr
+// is already in the table, we accept it.
+//
+// Input: Source Address of incoming connection
+// Destination port of incoming connection
+//
+// Returns: TRUE is connect is to be accepted
+// FALSE if connection is rejected
+//
+int ConnCheckPassed(IPAddr Src, ulong Prt)
+
+{
+ UNREFERENCED_PARAMETER(Src);
+ UNREFERENCED_PARAMETER(Prt);
+
+ return TRUE;
+}
+
+void InitAddrChecks()
+{
+ return;
+}
+
+//* EnumerateConnectionList - Enumerate Connection List database.
+//
+// This routine enumerates the contents of the connection limit database
+//
+// Input:
+//
+// Buffer - A pointer to a buffer into which to put
+// the returned connection list entries.
+//
+// BufferSize - On input, the size in bytes of Buffer.
+// On output, the number of bytes written.
+//
+// EntriesAvailable - On output, the total number of connection entries
+// available in the database.
+//
+// Returns: A TDI status code:
+//
+// TDI_SUCCESS otherwise.
+//
+// NOTES:
+//
+// This routine acquires AddrObjTableLock.
+//
+// Entries written to output buffer are in host byte order.
+//
+void
+EnumerateConnectionList(uchar *Buffer, ulong BufferSize,
+ ulong *EntriesReturned, ulong *EntriesAvailable)
+{
+
+ UNREFERENCED_PARAMETER(Buffer);
+ UNREFERENCED_PARAMETER(BufferSize);
+
+ *EntriesAvailable = 0;
+ *EntriesReturned = 0;
+
+ return;
+}
+
+
+#pragma BEGIN_INIT
+
+//* InitTCPConn - Initialize TCP connection management code.
+//
+// Called during init time to initialize our TCP connection mgmt..
+//
+// Input: Nothing.
+//
+// Returns: TRUE.
+//
+int
+InitTCPConn(void)
+{
+#ifdef NT
+ ExInitializeSListHead(&ConnReqFree);
+#endif
+
+ CTEInitLock(&ConnReqFreeLock);
+ return TRUE;
+}
+
+//* UnInitTCPConn - Uninitialize our connection management code.
+//
+// Called if initialization fails to uninitialize our conn mgmet.
+//
+//
+// Input: Nothing.
+//
+// Returns: Nothing.
+//
+void
+UnInitTCPConn(void)
+{
+
+}
+
+#pragma END_INIT
+
diff --git a/private/ntos/tdi/tcpip/tcp/tcpconn.h b/private/ntos/tdi/tcpip/tcp/tcpconn.h
new file mode 100644
index 000000000..e3eed2c8b
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcpconn.h
@@ -0,0 +1,124 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCPCONN.H - TCP connection related definitions.
+//
+// This file contains the definitions for connection related structures,
+// such as the TCPConnReq structure.
+//
+
+#define INVALID_CONN_INDEX 0xffffff
+
+//* Structure used for tracking Connect/Listen/Accept/Disconnect requests.
+
+#define tcr_signature 0x20524354 // 'TCR '
+
+struct TCPConnReq {
+ struct TCPReq tcr_req; // General request structure.
+#ifdef DEBUG
+ ulong tcr_sig;
+#endif
+ struct _TDI_CONNECTION_INFORMATION *tcr_conninfo; // Where to return info.
+ ushort tcr_flags; // Flags for this request.
+ ushort tcr_timeout; // Timeout value for this request.
+};
+
+typedef struct TCPConnReq TCPConnReq;
+
+typedef void (*ConnDoneRtn)(struct TCPConn *, CTELockHandle);
+
+//* Structure of a TCB Connection. A TCP Connection points to a TCP and an
+// address object.
+
+#define tc_signature 0x20204354 // 'TC '
+
+struct TCPConn {
+#ifdef DEBUG
+ ulong tc_sig;
+#endif
+ Queue tc_q; // Linkage on AO.
+ struct TCB *tc_tcb; // Pointer to TCB for connection.
+ struct AddrObj *tc_ao; // Back pointer to AddrObj.
+ uchar tc_inst; // Instance number.
+ uchar tc_flags; // Flags for connection.
+ ushort tc_refcnt; // Count of TCBs which reference this
+ // connection.
+ void *tc_context; // User's context.
+ CTEReqCmpltRtn tc_rtn; // Completion routine.
+ PVOID tc_rtncontext; // User context for completion routine.
+ ConnDoneRtn tc_donertn; // Routine to call when refcnt goes to 0.
+ uint tc_tcbflags; // Flags for TCB when it comes in.
+ uint tc_window; // Default window for TCB.
+
+}; /* TCPConn */
+
+typedef struct TCPConn TCPConn;
+
+#define CONN_CLOSING 1 // Connection is closing.
+#define CONN_DISACC 2 // Conn. is disassociating.
+#define CONN_WINSET 4 // Window explictly set.
+
+#define CONN_INVALID (CONN_CLOSING | CONN_DISACC)
+
+//* Structure of a ConnTable.
+typedef struct TCPConn *TCPConnTable[];
+
+extern TCPConnTable *ConnTable;
+
+#define FREE_CONN_INDEX(i) (*ConnTable)[(i)] = NULL
+EXTERNAL_LOCK(ConnTableLock)
+
+struct TCPAddrCheck {
+ IPAddr SourceAddress;
+ uint TickCount;
+}; /* TCPAddrCheck */
+
+typedef struct TCPAddrCheck TCPAddrCheckElement;
+
+extern TCPAddrCheckElement *AddrCheckTable;
+
+//* External definitions for TDI entry points.
+extern TDI_STATUS TdiOpenConnection(PTDI_REQUEST Request, PVOID Context);
+extern TDI_STATUS TdiCloseConnection(PTDI_REQUEST Request);
+extern TDI_STATUS TdiAssociateAddress(PTDI_REQUEST Request, HANDLE AddrHandle);
+extern TDI_STATUS TdiDisAssociateAddress(PTDI_REQUEST Request);
+extern TDI_STATUS TdiConnect(PTDI_REQUEST Request, void *Timeout,
+ PTDI_CONNECTION_INFORMATION RequestAddr,
+ PTDI_CONNECTION_INFORMATION ReturnAddr);
+extern TDI_STATUS TdiListen(PTDI_REQUEST Request, ushort Flags,
+ PTDI_CONNECTION_INFORMATION AcceptableAddr,
+ PTDI_CONNECTION_INFORMATION ConnectedAddr);
+extern TDI_STATUS TdiAccept(PTDI_REQUEST Request,
+ PTDI_CONNECTION_INFORMATION AcceptInfo,
+ PTDI_CONNECTION_INFORMATION ConnectedInfo);
+extern TDI_STATUS TdiDisconnect(PTDI_REQUEST Request, void *TO, ushort Flags,
+ PTDI_CONNECTION_INFORMATION DiscConnInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo);
+
+extern struct TCPConnReq *GetConnReq(void);
+extern void FreeConnReq(struct TCPConnReq *FreedReq);
+extern void DerefTCB(struct TCB *DoneTCB, CTELockHandle Handle);
+extern void InitRCE(struct TCB *NewTCB);
+extern void AcceptConn(struct TCB *AcceptTCB, CTELockHandle Handle);
+extern void FreeConnID(uint ConnID);
+extern void NotifyOfDisc(struct TCB *DiscTCB, struct IPOptInfo *DiscInfo,
+ TDI_STATUS Status);
+extern TCPConn *GetConnFromConnID(uint ConnID);
+
+extern void TryToCloseTCB(struct TCB *ClosedTCB, uchar Reason,
+ CTELockHandle Handle);
+extern TDI_STATUS InitTCBFromConn(struct TCPConn *Conn, struct TCB *NewTCB,
+ PTDI_CONNECTION_INFORMATION Addr, uint AOLocked);
+
+extern void PushData(struct TCB *PushTCB);
+extern TDI_STATUS MapIPError(IP_STATUS IPError, TDI_STATUS Default);
+extern void GracefulClose(struct TCB *CloseTCB, uint ToTimeWait, uint Notify,
+ CTELockHandle Handle);
+extern void RemoveTCBFromConn(struct TCB *RemovedTCB);
+extern void InitAddrChecks();
+extern int ConnCheckPassed(IPAddr Src, ulong Prt);
+extern void EnumerateConnectionList(uchar *Buffer, ulong BufferSize,
+ ulong *EntriesReturned, ulong *EntriesAvailable);
diff --git a/private/ntos/tdi/tcpip/tcp/tcpdeb.c b/private/ntos/tdi/tcpip/tcp/tcpdeb.c
new file mode 100644
index 000000000..70478f6ee
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcpdeb.c
@@ -0,0 +1,169 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCPDEB.C - TCP debug code.
+//
+// This file contains the code for various TCP specific debug routines.
+//
+
+#include "oscfg.h"
+
+
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#ifdef VXD
+#include "tdivxd.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "tcp.h"
+#include "tcpsend.h"
+#include "tlcommon.h"
+
+
+#ifdef DEBUG
+
+#ifdef NT
+
+ULONG TCPDebug = TCP_DEBUG_CANCEL;
+
+#endif
+
+
+//* CheckRBList - Check a list of RBs for the correct size.
+//
+// A routine to walk a list of RBs, making sure the size is what we think
+// it it.
+//
+// Input: RBList - List of RBs to check.
+// Size - Size RBs should be.
+//
+// Returns: Nothing.
+//
+void
+CheckRBList(IPRcvBuf *RBList, uint Size)
+{
+ uint SoFar = 0;
+ IPRcvBuf *List = RBList;
+
+ while (List != NULL) {
+ SoFar += List->ipr_size;
+ List = List->ipr_next;
+ }
+
+ CTEAssert(Size == SoFar);
+
+}
+
+//* CheckTCBRcv - Check receives on a TCB.
+//
+// Check the receive state of a TCB.
+//
+// Input: CheckTCB - TCB to check.
+//
+// Returns: Nothing.
+//
+void
+CheckTCBRcv(TCB *CheckTCB)
+{
+ CTEStructAssert(CheckTCB, tcb);
+
+ CTEAssert(!(CheckTCB->tcb_flags & FLOW_CNTLD) ||
+ (CheckTCB->tcb_sendwin == 0));
+
+ if ((CheckTCB->tcb_fastchk & ~TCP_FLAG_IN_RCV) == TCP_FLAG_ACK) {
+ CTEAssert(CheckTCB->tcb_slowcount == 0);
+ CTEAssert(CheckTCB->tcb_state == TCB_ESTAB);
+ CTEAssert(CheckTCB->tcb_raq == NULL);
+ CTEAssert(!(CheckTCB->tcb_flags & TCP_SLOW_FLAGS));
+ CTEAssert(!CLOSING(CheckTCB));
+ } else {
+ CTEAssert(CheckTCB->tcb_slowcount != 0);
+ CTEAssert( (CheckTCB->tcb_state != TCB_ESTAB) ||
+ (CheckTCB->tcb_raq != NULL) ||
+ (CheckTCB->tcb_flags & TCP_SLOW_FLAGS) ||
+ CLOSING(CheckTCB));
+ }
+
+}
+
+//* CheckTCBSends - Check the send status of a TCB.
+//
+// A routine to check the send status of a TCB. We make sure that all
+// of the SendReqs make sense, as well as making sure that the send seq.
+// variables in the TCB are consistent.
+//
+// Input: CheckTCB - TCB to check.
+//
+// Returns: Nothing.
+//
+void
+CheckTCBSends(TCB *CheckTCB)
+{
+ Queue *End, *Current; // End and current elements.
+ TCPSendReq *CurrentTSR; // Current send req we're
+ // examining.
+ uint Unacked; // Number of unacked bytes.
+ PNDIS_BUFFER CurrentBuffer;
+ TCPSendReq *TCBTsr; // Current send on TCB.
+ uint FoundSendReq;
+
+
+ CTEStructAssert(CheckTCB, tcb);
+
+ // Don't check on unsynchronized TCBs.
+ if (!SYNC_STATE(CheckTCB->tcb_state))
+ return;
+
+ CTEAssert(SEQ_LTE(CheckTCB->tcb_senduna, CheckTCB->tcb_sendnext));
+ CTEAssert(SEQ_LTE(CheckTCB->tcb_sendnext, CheckTCB->tcb_sendmax));
+ CTEAssert(!(CheckTCB->tcb_flags & FIN_OUTSTANDING) ||
+ (CheckTCB->tcb_sendnext == CheckTCB->tcb_sendmax));
+
+ if (CheckTCB->tcb_unacked == 0) {
+ CTEAssert(CheckTCB->tcb_cursend == NULL);
+ CTEAssert(CheckTCB->tcb_sendsize == 0);
+ }
+
+ if (CheckTCB->tcb_sendbuf != NULL)
+ CTEAssert(CheckTCB->tcb_sendofs < NdisBufferLength(CheckTCB->tcb_sendbuf));
+
+ TCBTsr = CheckTCB->tcb_cursend;
+ FoundSendReq = (TCBTsr == NULL) ? TRUE : FALSE;
+
+ End = QEND(&CheckTCB->tcb_sendq);
+ Current = QHEAD(&CheckTCB->tcb_sendq);
+
+ Unacked = 0;
+ while (Current != End) {
+ CurrentTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, Current, tr_q),
+ tsr_req);
+ CTEStructAssert(CurrentTSR, tsr);
+
+ if (CurrentTSR == TCBTsr)
+ FoundSendReq = TRUE;
+
+ CTEAssert(CurrentTSR->tsr_unasize <= CurrentTSR->tsr_size);
+
+ CurrentBuffer = CurrentTSR->tsr_buffer;
+ CTEAssert(CurrentBuffer != NULL);
+
+ CTEAssert(CurrentTSR->tsr_offset < NdisBufferLength(CurrentBuffer));
+
+ Unacked += CurrentTSR->tsr_unasize;
+ Current = QNEXT(Current);
+ }
+
+ CTEAssert(FoundSendReq);
+
+ CTEAssert(Unacked == CheckTCB->tcb_unacked);
+ Unacked += ((CheckTCB->tcb_flags & FIN_SENT) ? 1 : 0);
+ CTEAssert((CheckTCB->tcb_sendmax - CheckTCB->tcb_senduna) <= (int) Unacked);
+}
+
+#endif
diff --git a/private/ntos/tdi/tcpip/tcp/tcpdeb.h b/private/ntos/tdi/tcpip/tcp/tcpdeb.h
new file mode 100644
index 000000000..b22e18a1b
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcpdeb.h
@@ -0,0 +1,78 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCPDEB.H - TCP debug definitions.
+//
+// This file contains the definitions for the debug code.
+//
+
+#ifndef NO_TCP_DEFS
+#ifdef DEBUG
+
+#ifndef UDP_ONLY
+extern void CheckRBList(IPRcvBuf *RBList, uint Size);
+extern void CheckTCBSends(TCB *SendTcb);
+extern void CheckTCBRcv(TCB *RcvTCB);
+#else
+#define CheckRBList(R, S)
+#define CheckTCBSends(T)
+#define CheckTCBRcv(T)
+#endif // UDP_ONLY
+
+#else
+
+#define CheckRBList(R, S)
+#define CheckTCBSends(T)
+#define CheckTCBRcv(T)
+#endif // DEBUG
+#endif // NO_TCP_DEFS
+
+//
+// Additional debugging support for NT
+//
+#ifdef NT
+#if DBG
+
+extern ULONG TCPDebug;
+
+#define TCP_DEBUG_OPEN 0x00000001
+#define TCP_DEBUG_CLOSE 0x00000002
+#define TCP_DEBUG_ASSOCIATE 0x00000004
+#define TCP_DEBUG_CONNECT 0x00000008
+#define TCP_DEBUG_SEND 0x00000010
+#define TCP_DEBUG_RECEIVE 0x00000020
+#define TCP_DEBUG_INFO 0x00000040
+#define TCP_DEBUG_IRP 0x00000080
+#define TCP_DEBUG_SEND_DGRAM 0x00000100
+#define TCP_DEBUG_RECEIVE_DGRAM 0x00000200
+#define TCP_DEBUG_EVENT_HANDLER 0x00000400
+#define TCP_DEBUG_CLEANUP 0x00000800
+#define TCP_DEBUG_CANCEL 0x00001000
+#define TCP_DEBUG_RAW 0x00002000
+#define TCP_DEBUG_OPTIONS 0x00004000
+
+
+#define TCPTRACE(many_args) DbgPrint many_args
+
+#define IF_TCPDBG(flag) if (TCPDebug & flag)
+
+
+#else // DBG
+
+
+#define TCPTRACE(many_args)
+#define IF_TCPDBG(flag) if (0)
+
+
+#endif // DBG
+#else // NT
+
+
+#define TCPTRACE(many_args)
+#define IF_TCPDBG(flag) if (0)
+
+
+#endif // NT
diff --git a/private/ntos/tdi/tcpip/tcp/tcpdeliv.c b/private/ntos/tdi/tcpip/tcp/tcpdeliv.c
new file mode 100644
index 000000000..36d686257
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcpdeliv.c
@@ -0,0 +1,1971 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCPDELIV.C - TCP deliver data code.
+//
+// This file contains the code for delivering data to the user, including
+// putting data into recv. buffers and calling indication handlers.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#ifdef VXD
+#include "tdivxd.h"
+#include "tdistat.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "tcp.h"
+#include "tcb.h"
+#include "tcprcv.h"
+#include "tcpsend.h"
+#include "tcpconn.h"
+#include "tcpdeliv.h"
+#include "tlcommon.h"
+
+EXTERNAL_LOCK(AddrObjTableLock)
+
+extern void
+PutOnRAQ(TCB *RcvTCB, TCPRcvInfo *RcvInfo, IPRcvBuf *RcvBuf, uint Size);
+
+#ifndef NT
+TCPRcvReq *TCPRcvReqFree = NULL; // Rcv req. free list.
+#else
+SLIST_HEADER TCPRcvReqFree; // Rcv req. free list.
+#endif
+
+DEFINE_LOCK_STRUCTURE(TCPRcvReqFreeLock) // Protects rcv req free list.
+uint NumTCPRcvReq = 0; // Current number of RcvReqs in system.
+uint MaxRcvReq = 0xffffffff; // Maximum allowed number of SendReqs.
+
+#ifdef NT
+
+NTSTATUS
+TCPPrepareIrpForCancel(
+ PTCP_CONTEXT TcpContext,
+ PIRP Irp,
+ PDRIVER_CANCEL CancelRoutine
+ );
+
+ULONG
+TCPGetMdlChainByteCount(
+ PMDL Mdl
+ );
+
+void
+TCPDataRequestComplete(
+ void *Context,
+ unsigned int Status,
+ unsigned int ByteCount
+ );
+
+VOID
+TCPCancelRequest(
+ PDEVICE_OBJECT Device,
+ PIRP Irp
+ );
+
+#endif // NT
+
+
+//* FreeRcvReq - Free a rcv request structure.
+//
+// Called to free a rcv request structure.
+//
+// Input: FreedReq - Rcv request structure to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeRcvReq(TCPRcvReq *FreedReq)
+{
+#ifdef NT
+
+ PSINGLE_LIST_ENTRY BufferLink;
+
+ CTEStructAssert(FreedReq, trr);
+
+ BufferLink = STRUCT_OF(SINGLE_LIST_ENTRY, &(FreedReq->trr_next), Next);
+
+ ExInterlockedPushEntrySList(
+ &TCPRcvReqFree,
+ BufferLink,
+ &TCPRcvReqFreeLock
+ );
+
+#else // NT
+
+ TCPRcvReq **Temp;
+
+ CTEStructAssert(FreedReq, trr);
+
+ FreedReq->trr_next = TCPRcvReqFree;
+ TCPRcvReqFree = FreedReq;
+
+#endif // NT
+}
+
+//* GetRcvReq - Get a recv. request structure.
+//
+// Called to get a rcv. request structure.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to RcvReq structure, or NULL if none.
+//
+TCPRcvReq *
+GetRcvReq(void)
+{
+ TCPRcvReq *Temp;
+
+#ifdef NT
+
+ PSINGLE_LIST_ENTRY BufferLink;
+
+
+ BufferLink = ExInterlockedPopEntrySList(
+ &TCPRcvReqFree,
+ &TCPRcvReqFreeLock
+ );
+
+ if (BufferLink != NULL) {
+ Temp = STRUCT_OF(TCPRcvReq, BufferLink, trr_next);
+ CTEStructAssert(Temp, trr);
+ }
+ else {
+ if (NumTCPRcvReq < MaxRcvReq)
+ Temp = CTEAllocMem(sizeof(TCPRcvReq));
+ else
+ Temp = NULL;
+
+ if (Temp != NULL) {
+ ExInterlockedAddUlong(&NumTCPRcvReq, 1, &TCPRcvReqFreeLock);
+#ifdef DEBUG
+ Temp->trr_sig = trr_signature;
+#endif
+ }
+ }
+
+#else // NT
+
+ Temp = TCPRcvReqFree;
+ if (Temp != NULL)
+ TCPRcvReqFree = Temp->trr_next;
+ else {
+ if (NumTCPRcvReq < MaxRcvReq)
+ Temp = CTEAllocMem(sizeof(TCPRcvReq));
+ else
+ Temp = NULL;
+
+ if (Temp != NULL) {
+ NumTCPRcvReq++;
+#ifdef DEBUG
+ Temp->trr_sig = trr_signature;
+#endif
+ }
+ }
+
+#endif // NT
+
+ return Temp;
+}
+
+
+
+//* FindLastBuffer - Find the last buffer in a chain.
+//
+// A utility routine to find the last buffer in an rb chain.
+//
+// Input: Buf - Pointer to RB chain.
+//
+// Returns: Pointer to last buf in chain.
+//
+IPRcvBuf *
+FindLastBuffer(IPRcvBuf *Buf)
+{
+ CTEAssert(Buf != NULL);
+
+ while (Buf->ipr_next != NULL)
+ Buf = Buf->ipr_next;
+
+ return Buf;
+}
+
+
+//* FreePartialRB - Free part of an RB chain.
+//
+// Called to adjust an free part of an RB chain. We walk down the chain,
+// trying to free buffers.
+//
+// Input: RB - RB chain to be adjusted.
+// Size - Size in bytes to be freed.
+//
+// Returns: Pointer to adjusted RB chain.
+//
+IPRcvBuf *
+FreePartialRB(IPRcvBuf *RB, uint Size)
+{
+ while (Size != 0) {
+ IPRcvBuf *TempBuf;
+
+ CTEAssert(RB != NULL);
+
+ if (Size >= RB->ipr_size) {
+ Size -= RB->ipr_size;
+ TempBuf = RB;
+ RB = RB->ipr_next;
+ if (TempBuf->ipr_owner == IPR_OWNER_TCP)
+ CTEFreeMem(TempBuf);
+ } else {
+ RB->ipr_size -= Size;
+ RB->ipr_buffer += Size;
+ break;
+ }
+ }
+
+ CTEAssert(RB != NULL);
+ return RB;
+
+}
+
+//* CopyRBChain - Copy a chain of IP rcv buffers.
+//
+// Called to copy a chain of IP rcv buffers. We don't copy a buffer if it's
+// already owner by TCP. We assume that all non-TCP owned buffers start
+// before any TCP owner buffers, so we quit when we copy to a TCP owner buffer.
+//
+// Input: OrigBuf - Buffer chain to copy from.
+// LastBuf - Where to return pointer to last buffer in
+// chain.
+// Size - Maximum size in bytes to copy.
+//
+// Returns: Pointer to new buffer chain.
+//
+IPRcvBuf *
+CopyRBChain(IPRcvBuf *OrigBuf, IPRcvBuf **LastBuf, uint Size)
+{
+ IPRcvBuf *FirstBuf, *EndBuf;
+ uint BytesToCopy;
+
+ CTEAssert(OrigBuf != NULL);
+ CTEAssert(Size > 0);
+
+ if (OrigBuf->ipr_owner != IPR_OWNER_TCP) {
+
+ BytesToCopy = MIN(Size, OrigBuf->ipr_size);
+ FirstBuf = CTEAllocMem(sizeof(IPRcvBuf) + BytesToCopy);
+ if (FirstBuf != NULL) {
+ EndBuf = FirstBuf;
+ FirstBuf->ipr_next = NULL;
+ FirstBuf->ipr_owner = IPR_OWNER_TCP;
+ FirstBuf->ipr_size = BytesToCopy;
+ FirstBuf->ipr_buffer = (uchar *)(FirstBuf + 1);
+ CTEMemCopy(FirstBuf->ipr_buffer, OrigBuf->ipr_buffer,
+ BytesToCopy);
+ Size -= BytesToCopy;
+ OrigBuf = OrigBuf->ipr_next;
+ while (OrigBuf != NULL && OrigBuf->ipr_owner != IPR_OWNER_TCP
+ && Size != 0) {
+ IPRcvBuf *NewBuf;
+
+ BytesToCopy = MIN(Size, OrigBuf->ipr_size);
+ NewBuf = CTEAllocMem(sizeof(IPRcvBuf) + BytesToCopy);
+ if (NewBuf != NULL) {
+ NewBuf->ipr_next = NULL;
+ NewBuf->ipr_owner = IPR_OWNER_TCP;
+ NewBuf->ipr_size = BytesToCopy;
+ NewBuf->ipr_buffer = (uchar *)(NewBuf + 1);
+ CTEMemCopy(NewBuf->ipr_buffer, OrigBuf->ipr_buffer,
+ BytesToCopy);
+ EndBuf->ipr_next = NewBuf;
+ EndBuf = NewBuf;
+ Size -= BytesToCopy;
+ OrigBuf = OrigBuf->ipr_next;
+ } else {
+ FreeRBChain(FirstBuf);
+ return NULL;
+ }
+ }
+ EndBuf->ipr_next = OrigBuf;
+ } else
+ return NULL;
+ } else {
+ FirstBuf = OrigBuf;
+ EndBuf = OrigBuf;
+ if (Size < OrigBuf->ipr_size)
+ OrigBuf->ipr_size = Size;
+ Size -= OrigBuf->ipr_size;
+ }
+
+ // Now walk down the chain, until we run out of
+ // Size. At this point, Size is the bytes left to 'copy' (it may be 0),
+ // and the sizes in buffers FirstBuf...EndBuf are correct.
+ while (Size != 0) {
+
+ EndBuf = EndBuf->ipr_next;
+ CTEAssert(EndBuf != NULL);
+
+ if (Size < EndBuf->ipr_size)
+ EndBuf->ipr_size = Size;
+
+ Size -= EndBuf->ipr_size;
+ }
+
+ // If there's anything left in the chain, free it now.
+ if (EndBuf->ipr_next != NULL) {
+ FreeRBChain(EndBuf->ipr_next);
+ EndBuf->ipr_next = NULL;
+ }
+
+ *LastBuf = EndBuf;
+ return FirstBuf;
+
+}
+
+//* PendData - Pend incoming data to a client.
+//
+// Called when we need to buffer data for a client because there's no receive
+// down and we can't indicate.
+//
+// The TCB lock is held throughout this procedure. If this is to be changed,
+// make sure consistency of tcb_pendingcnt is preserved. This routine is
+// always called at DPC level.
+//
+// Input: RcvTCB - TCB on which to receive the data.
+// RcvFlags - TCP flags for the incoming packet.
+// InBuffer - Input buffer of packet.
+// Size - Size in bytes of data in InBuffer.
+//
+// Returns: Number of bytes of data taken.
+//
+uint
+PendData(TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer, uint Size)
+{
+
+ IPRcvBuf *NewBuf, *LastBuf;
+
+ CTEStructAssert(RcvTCB, tcb);
+ CTEAssert(Size > 0);
+ CTEAssert(InBuffer != NULL);
+
+ CTEAssert(RcvTCB->tcb_refcnt != 0);
+ CTEAssert(RcvTCB->tcb_fastchk & TCP_FLAG_IN_RCV);
+ CTEAssert(RcvTCB->tcb_currcv == NULL);
+ CTEAssert(RcvTCB->tcb_rcvhndlr == PendData);
+
+ CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
+
+ NewBuf = CopyRBChain(InBuffer, &LastBuf, Size);
+ if (NewBuf != NULL) {
+ // We have a duplicate chain. Put it on the end of the
+ // pending q.
+ if (RcvTCB->tcb_pendhead == NULL) {
+ RcvTCB->tcb_pendhead = NewBuf;
+ RcvTCB->tcb_pendtail = LastBuf;
+ } else {
+ RcvTCB->tcb_pendtail->ipr_next = NewBuf;
+ RcvTCB->tcb_pendtail = LastBuf;
+ }
+ RcvTCB->tcb_pendingcnt += Size;
+ } else {
+ FreeRBChain(InBuffer);
+ Size = 0;
+ }
+
+ CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
+
+ return Size;
+
+}
+
+
+
+//* BufferData - Put incoming data into client's buffer.
+//
+// Called when we believe we have a buffer into which we can put data. We put
+// it in there, and if we've filled the buffer or the incoming data has the
+// push flag set we'll mark the TCB to return the buffer. Otherwise we'll
+// get out and return the data later.
+//
+// In NT, this routine is called with the TCB lock held, and holds it for
+// the duration of the call. This is important to ensure consistency of
+// the tcb_pendingcnt field. If we need to change this to free the lock
+// partway through, make sure to take this into account. In particular,
+// TdiReceive zeros pendingcnt before calling this routine, and this routine
+// may update it. If the lock is freed in here there would be a window where
+// we really do have pending data, but it's not on the list or reflected in
+// pendingcnt. This could screw up our windowing computations, and we'd have
+// to be careful not to end up with more data pending than our window allows.
+//
+// Input: RcvTCB - TCB on which to receive the data.
+// RcvFlags - TCP rcv flags for the incoming packet.
+// InBuffer - Input buffer of packet.
+// Size - Size in bytes of data in InBuffer.
+//
+// Returns: Number of bytes of data taken.
+//
+uint
+BufferData(TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer, uint Size)
+
+{
+ uchar *DestPtr; // Destination pointer.
+ uchar *SrcPtr; // Src pointer.
+ uint SrcSize, DestSize; // Sizes of current source and
+ // destination buffers.
+ uint Copied; // Total bytes to copy.
+ uint BytesToCopy; // Bytes of data to copy this time.
+ TCPRcvReq *DestReq; // Current receive request.
+ IPRcvBuf *SrcBuf; // Current source buffer.
+ PNDIS_BUFFER DestBuf; // Current receive buffer.
+ uint RcvCmpltd;
+ uint Flags;
+
+ CTEStructAssert(RcvTCB, tcb);
+ CTEAssert(Size > 0);
+ CTEAssert(InBuffer != NULL);
+
+ CTEAssert(RcvTCB->tcb_refcnt != 0);
+ CTEAssert(RcvTCB->tcb_rcvhndlr == BufferData);
+
+ Copied = 0;
+ RcvCmpltd = 0;
+
+ DestReq = RcvTCB->tcb_currcv;
+
+ CTEAssert(DestReq != NULL);
+ CTEStructAssert(DestReq, trr);
+
+ DestBuf = DestReq->trr_buffer;
+
+ DestSize = MIN(NdisBufferLength(DestBuf) - DestReq->trr_offset,
+ DestReq->trr_size - DestReq->trr_amt);
+ DestPtr = (uchar *)NdisBufferVirtualAddress(DestBuf) + DestReq->trr_offset;
+
+ SrcBuf = InBuffer;
+ SrcSize = SrcBuf->ipr_size;
+ SrcPtr = SrcBuf->ipr_buffer;
+
+ Flags = (RcvFlags & TCP_FLAG_PUSH) ? TRR_PUSHED : 0;
+ RcvCmpltd = Flags;
+ DestReq->trr_flags |= Flags;
+
+ do {
+
+ BytesToCopy = MIN(Size - Copied, MIN(SrcSize, DestSize));
+
+ CTEMemCopy(DestPtr, SrcPtr, BytesToCopy);
+ Copied += BytesToCopy;
+ DestReq->trr_amt += BytesToCopy;
+
+ // Update our source pointers.
+ if ((SrcSize -= BytesToCopy) == 0) {
+ IPRcvBuf *TempBuf;
+
+ // We've copied everything in this buffer.
+ TempBuf = SrcBuf;
+ SrcBuf = SrcBuf->ipr_next;
+ if (Size != Copied) {
+ CTEAssert(SrcBuf != NULL);
+ SrcSize = SrcBuf->ipr_size;
+ SrcPtr = SrcBuf->ipr_buffer;
+ }
+ if (TempBuf->ipr_owner == IPR_OWNER_TCP)
+ CTEFreeMem(TempBuf);
+ } else
+ SrcPtr += BytesToCopy;
+
+ // Now check the destination pointer, and update it if we need to.
+ if ((DestSize -= BytesToCopy) == 0) {
+ uint DestAvail;
+
+ // Exhausted this buffer. See if there's another one.
+ DestAvail = DestReq->trr_size - DestReq->trr_amt;
+ DestBuf = NDIS_BUFFER_LINKAGE(DestBuf);
+
+ if (DestBuf != NULL && (DestAvail != 0)) {
+ // Have another buffer in the chain. Update things.
+ DestSize = MIN(NdisBufferLength(DestBuf), DestAvail);
+ DestPtr = (uchar *)NdisBufferVirtualAddress(DestBuf);
+ } else {
+ // No more buffers in the chain. See if we have another buffer
+ // on the list.
+ DestReq->trr_flags |= TRR_PUSHED;
+
+ // If we've been told there's to be no back traffic, get an ACK
+ // going right away.
+ if (DestReq->trr_flags & TDI_RECEIVE_NO_RESPONSE_EXP)
+ DelayAction(RcvTCB, NEED_ACK);
+
+ RcvCmpltd = TRUE;
+ DestReq = DestReq->trr_next;
+ if (DestReq != NULL) {
+ DestBuf = DestReq->trr_buffer;
+ DestSize = MIN(NdisBufferLength(DestBuf), DestReq->trr_size);
+ DestPtr = (uchar *)NdisBufferVirtualAddress(DestBuf);
+
+ // If we have more to put into here, set the flags.
+ if (Copied != Size)
+ DestReq->trr_flags |= Flags;
+
+ } else {
+ // All out of buffer space. Reset the data handler pointer.
+ break;
+ }
+ }
+ } else
+ // Current buffer not empty yet.
+ DestPtr += BytesToCopy;
+
+
+ // If we've copied all that we need to, we're done.
+ } while (Copied != Size);
+
+ // We've finished copying, and have a few more things to do. We need to
+ // update the current rcv. pointer and possibly the offset in the
+ // recv. request. If we need to complete any receives we have to schedule
+ // that. If there's any data we couldn't copy we'll need to dispose of
+ // it.
+ RcvTCB->tcb_currcv = DestReq;
+ if (DestReq != NULL) {
+ DestReq->trr_buffer = DestBuf;
+ DestReq->trr_offset = DestPtr - (uchar *) NdisBufferVirtualAddress(DestBuf);
+ RcvTCB->tcb_rcvhndlr = BufferData;
+ } else
+ RcvTCB->tcb_rcvhndlr = PendData;
+
+ RcvTCB->tcb_indicated -= MIN(Copied, RcvTCB->tcb_indicated);
+
+ if (Size != Copied) {
+ IPRcvBuf *NewBuf, *LastBuf;
+
+ CTEAssert(DestReq == NULL);
+
+ // We have data to dispose of. Update the first buffer of the chain
+ // with the current src pointer and size, and copy it.
+ CTEAssert(SrcSize <= SrcBuf->ipr_size);
+ CTEAssert(
+ ((uint) (SrcPtr - SrcBuf->ipr_buffer)) ==
+ (SrcBuf->ipr_size - SrcSize)
+ );
+
+ SrcBuf->ipr_buffer = SrcPtr;
+ SrcBuf->ipr_size = SrcSize;
+
+ NewBuf = CopyRBChain(SrcBuf, &LastBuf, Size - Copied);
+ if (NewBuf != NULL) {
+ // We managed to copy the buffer. Push it on the pending queue.
+ if (RcvTCB->tcb_pendhead == NULL) {
+ RcvTCB->tcb_pendhead = NewBuf;
+ RcvTCB->tcb_pendtail = LastBuf;
+ } else {
+ LastBuf->ipr_next = RcvTCB->tcb_pendhead;
+ RcvTCB->tcb_pendhead = NewBuf;
+ }
+ RcvTCB->tcb_pendingcnt += Size - Copied;
+ Copied = Size;
+
+ CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
+
+ } else
+ FreeRBChain(SrcBuf);
+ } else {
+ // We copied Size bytes, but the chain could be longer than that. Free
+ // it if we need to.
+ if (SrcBuf != NULL)
+ FreeRBChain(SrcBuf);
+ }
+
+
+ if (RcvCmpltd != 0) {
+ DelayAction(RcvTCB, NEED_RCV_CMPLT);
+ } else {
+ START_TCB_TIMER(RcvTCB->tcb_pushtimer, PUSH_TO);
+ }
+
+ return Copied;
+
+}
+
+
+//* IndicateData - Indicate incoming data to a client.
+//
+// Called when we need to indicate data to an upper layer client. We'll pass
+// up a pointer to whatever we have available, and the client may take some
+// or all of it.
+//
+// Input: RcvTCB - TCB on which to receive the data.
+// RcvFlags - TCP rcv flags for the incoming packet.
+// InBuffer - Input buffer of packet.
+// Size - Size in bytes of data in InBuffer.
+//
+// Returns: Number of bytes of data taken.
+//
+uint
+IndicateData(TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer, uint Size)
+{
+
+ TDI_STATUS Status;
+ PRcvEvent Event;
+ PVOID EventContext, ConnContext;
+ uint BytesTaken = 0;
+#ifdef NT
+ EventRcvBuffer *ERB = NULL;
+ PTDI_REQUEST_KERNEL_RECEIVE RequestInformation;
+ PIO_STACK_LOCATION IrpSp;
+#else
+ EventRcvBuffer ERB;
+#endif
+ TCPRcvReq *RcvReq;
+ IPRcvBuf *NewBuf;
+ ulong IndFlags;
+
+
+ CTEStructAssert(RcvTCB, tcb);
+ CTEAssert(Size > 0);
+ CTEAssert(InBuffer != NULL);
+
+ CTEAssert(RcvTCB->tcb_refcnt != 0);
+ CTEAssert(RcvTCB->tcb_fastchk & TCP_FLAG_IN_RCV);
+ CTEAssert(RcvTCB->tcb_rcvind != NULL);
+ CTEAssert(RcvTCB->tcb_rcvhead == NULL);
+ CTEAssert(RcvTCB->tcb_rcvhndlr == IndicateData);
+
+ RcvReq = GetRcvReq();
+ if (RcvReq != NULL) {
+ // The indicate handler is saved in the TCB. Just call up into it.
+ Event = RcvTCB->tcb_rcvind;
+ EventContext = RcvTCB->tcb_ricontext;
+ ConnContext = RcvTCB->tcb_conncontext;
+ RcvTCB->tcb_indicated = Size;
+ RcvTCB->tcb_flags |= IN_RCV_IND;
+
+#ifndef VXD
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, NULL);
+#endif
+
+ IF_TCPDBG(TCP_DEBUG_RECEIVE) {
+ TCPTRACE((
+ "Indicating %lu bytes, %lu available\n",
+ InBuffer->ipr_size, Size
+ ));
+ }
+
+#if TCP_FLAG_PUSH >= TDI_RECEIVE_ENTIRE_MESSAGE
+ IndFlags = TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL |
+ TDI_RECEIVE_AT_DISPATCH_LEVEL |
+ ((RcvFlags & TCP_FLAG_PUSH) >>
+ ((TCP_FLAG_PUSH / TDI_RECEIVE_ENTIRE_MESSAGE) - 1));
+#else
+ IndFlags = TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL |
+ TDI_RECEIVE_AT_DISPATCH_LEVEL |
+ ((RcvFlags & TCP_FLAG_PUSH) <<
+ ((TDI_RECEIVE_ENTIRE_MESSAGE / TCP_FLAG_PUSH) - 1));
+#endif
+
+ Status = (*Event)(EventContext, ConnContext,
+ IndFlags, InBuffer->ipr_size, Size, &BytesTaken,
+ InBuffer->ipr_buffer, &ERB);
+
+ IF_TCPDBG(TCP_DEBUG_RECEIVE) {
+ TCPTRACE(("%lu bytes taken, status %lx\n", BytesTaken, Status));
+ }
+
+ // See what the client did. If the return status is MORE_PROCESSING,
+ // we've been given a buffer. In that case put it on the front of the
+ // buffer queue, and if all the data wasn't taken go ahead and copy
+ // it into the new buffer chain.
+ //
+ // Note that the size and buffer chain we're concerned with here is
+ // the one that we passed to the client. Since we're in a rcv. handler,
+ // any data that has come in would have been put on the reassembly
+ // queue.
+ if (Status == TDI_MORE_PROCESSING) {
+
+#ifdef NT
+
+ CTEAssert(ERB != NULL);
+
+ IrpSp = IoGetCurrentIrpStackLocation(ERB);
+
+ Status = TCPPrepareIrpForCancel(
+ (PTCP_CONTEXT) IrpSp->FileObject->FsContext,
+ ERB,
+ TCPCancelRequest
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ RequestInformation = (PTDI_REQUEST_KERNEL_RECEIVE)
+ &(IrpSp->Parameters);
+
+ RcvReq->trr_rtn = TCPDataRequestComplete;
+ RcvReq->trr_context = ERB;
+ RcvReq->trr_buffer = ERB->MdlAddress;
+ RcvReq->trr_size = RequestInformation->ReceiveLength;
+ RcvReq->trr_uflags = (ushort *)
+ &(RequestInformation->ReceiveFlags);
+ RcvReq->trr_flags = (uint)(RequestInformation->ReceiveFlags);
+ RcvReq->trr_offset = 0;
+ RcvReq->trr_amt = 0;
+
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, NULL);
+
+#else // NT
+
+ RcvReq->trr_rtn = ERB.erb_rtn;
+ RcvReq->trr_context = ERB.erb_context;
+ RcvReq->trr_buffer = ERB.erb_buffer;
+ RcvReq->trr_size = ERB.erb_size;
+ RcvReq->trr_uflags = ERB.erb_flags;
+ CTEAssert(ERB.erb_flags != NULL);
+ RcvReq->trr_flags = (uint)(*ERB.erb_flags);
+ RcvReq->trr_offset = 0;
+ RcvReq->trr_amt = 0;
+
+#endif // NT
+
+ RcvTCB->tcb_flags &= ~IN_RCV_IND;
+
+ CTEAssert(RcvTCB->tcb_rcvhndlr == IndicateData);
+
+ // Push him on the front of the rcv. queue.
+ CTEAssert((RcvTCB->tcb_currcv == NULL) ||
+ (RcvTCB->tcb_currcv->trr_amt == 0));
+
+ if (RcvTCB->tcb_rcvhead == NULL) {
+ RcvTCB->tcb_rcvhead = RcvReq;
+ RcvTCB->tcb_rcvtail = RcvReq;
+ RcvReq->trr_next = NULL;
+ } else {
+ RcvReq->trr_next = RcvTCB->tcb_rcvhead;
+ RcvTCB->tcb_rcvhead = RcvReq;
+ }
+
+ RcvTCB->tcb_currcv = RcvReq;
+ RcvTCB->tcb_rcvhndlr = BufferData;
+
+ CTEAssert(BytesTaken <= Size);
+
+ RcvTCB->tcb_indicated -= BytesTaken;
+ if ((Size -= BytesTaken) != 0) {
+
+ // Not everything was taken. Adjust the buffer chain to point
+ // beyond what was taken.
+ InBuffer = FreePartialRB(InBuffer, BytesTaken);
+
+ CTEAssert(InBuffer != NULL);
+
+ // We've adjusted the buffer chain. Call the BufferData
+ // handler.
+ BytesTaken += BufferData(RcvTCB, RcvFlags, InBuffer, Size);
+
+ } else {
+ // All of the data was taken. Free the buffer chain.
+ FreeRBChain(InBuffer);
+ }
+
+ return BytesTaken;
+#ifdef NT
+ }
+ else {
+ //
+ // The IRP was cancelled before it was handed back to us.
+ // We'll pretend we never saw it. TCPPrepareIrpForCancel
+ // already completed it. The client may have taken data,
+ // so we will act as if success was returned.
+ //
+ ERB = NULL;
+ Status = TDI_SUCCESS;
+ }
+
+#endif // NT
+
+ }
+
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, NULL);
+
+ RcvTCB->tcb_flags &= ~IN_RCV_IND;
+
+ // Status is not more processing. If it's not SUCCESS, the client
+ // didn't take any of the data. In either case we now need to
+ // see if all of the data was taken. If it wasn't, we'll try and
+ // push it onto the front of the pending queue.
+
+ FreeRcvReq(RcvReq); // This won't be needed.
+ if (Status == TDI_NOT_ACCEPTED)
+ BytesTaken = 0;
+
+ CTEAssert(BytesTaken <= Size);
+
+ RcvTCB->tcb_indicated -= BytesTaken;
+
+ CTEAssert(RcvTCB->tcb_rcvhndlr == IndicateData);
+
+ // Check to see if a rcv. buffer got posted during the indication.
+ // If it did, reset the recv. handler now.
+ if (RcvTCB->tcb_rcvhead != NULL)
+ RcvTCB->tcb_rcvhndlr = BufferData;
+
+
+ // See if all of the data was taken.
+ if (BytesTaken == Size) {
+ CTEAssert(RcvTCB->tcb_indicated == 0);
+
+ FreeRBChain(InBuffer);
+ return BytesTaken; // It was all taken.
+ }
+
+ // It wasn't all taken. Adjust for what was taken, and push
+ // on the front of the pending queue. We also need to check to
+ // see if a receive buffer got posted during the indication. This
+ // would be weird, but not impossible.
+ InBuffer = FreePartialRB(InBuffer, BytesTaken);
+ if (RcvTCB->tcb_rcvhead == NULL) {
+ IPRcvBuf *LastBuf;
+
+ RcvTCB->tcb_rcvhndlr = PendData;
+ NewBuf = CopyRBChain(InBuffer, &LastBuf, Size - BytesTaken);
+ if (NewBuf != NULL) {
+ // We have a duplicate chain. Push it on the front of the
+ // pending q.
+ if (RcvTCB->tcb_pendhead == NULL) {
+ RcvTCB->tcb_pendhead = NewBuf;
+ RcvTCB->tcb_pendtail = LastBuf;
+ } else {
+ LastBuf->ipr_next = RcvTCB->tcb_pendhead;
+ RcvTCB->tcb_pendhead = NewBuf;
+ }
+ RcvTCB->tcb_pendingcnt += Size - BytesTaken;
+ BytesTaken = Size;
+ } else {
+ FreeRBChain(InBuffer);
+ }
+
+ return BytesTaken;
+ } else {
+ // Just great. There's now a rcv. buffer on the TCB. Call the
+ // BufferData handler now.
+ CTEAssert(RcvTCB->tcb_rcvhndlr = BufferData);
+
+ BytesTaken += BufferData(RcvTCB, RcvFlags, InBuffer,
+ Size - BytesTaken);
+
+ return BytesTaken;
+ }
+
+ } else {
+ // Couldn't get a recv. request. We must be really low on resources,
+ // so just bail out now.
+ FreeRBChain(InBuffer);
+ return 0;
+ }
+
+}
+
+
+//* IndicatePendingData - Indicate pending data to a client.
+//
+// Called when we need to indicate pending data to an upper layer client,
+// usually because data arrived when we were in a state that it couldn't
+// be indicated.
+//
+// Many of the comments in the BufferData header about the consistency of
+// tcb_pendingcnt apply here also.
+//
+// Input: RcvTCB - TCB on which to indicate the data.
+// RcvReq - Rcv. req. to use to indicate it.
+//
+// Returns: Nothing.
+//
+void
+#ifdef VXD
+IndicatePendingData(TCB *RcvTCB, TCPRcvReq *RcvReq)
+#else
+IndicatePendingData(TCB *RcvTCB, TCPRcvReq *RcvReq, CTELockHandle TCBHandle)
+#endif
+{
+
+ TDI_STATUS Status;
+ PRcvEvent Event;
+ PVOID EventContext, ConnContext;
+ uint BytesTaken = 0;
+#ifdef NT
+ EventRcvBuffer *ERB = NULL;
+ PTDI_REQUEST_KERNEL_RECEIVE RequestInformation;
+ PIO_STACK_LOCATION IrpSp;
+#else
+ EventRcvBuffer ERB;
+ CTELockHandle TCBHandle; // For debug builds.
+#endif
+ IPRcvBuf *NewBuf;
+ uint Size;
+
+
+ CTEStructAssert(RcvTCB, tcb);
+
+ CTEAssert(RcvTCB->tcb_refcnt != 0);
+ CTEAssert(RcvTCB->tcb_rcvind != NULL);
+ CTEAssert(RcvTCB->tcb_rcvhead == NULL);
+ CTEAssert(RcvTCB->tcb_pendingcnt != 0);
+ CTEAssert(RcvReq != NULL);
+
+#ifdef VXD
+ CTEGetLock(&RcvTCB->tcb_lock, &TCBHandle);
+#endif
+
+ for (;;) {
+ CTEAssert(RcvTCB->tcb_rcvhndlr == PendData);
+
+ // The indicate handler is saved in the TCB. Just call up into it.
+ Event = RcvTCB->tcb_rcvind;
+ EventContext = RcvTCB->tcb_ricontext;
+ ConnContext = RcvTCB->tcb_conncontext;
+ RcvTCB->tcb_indicated = RcvTCB->tcb_pendingcnt;
+ RcvTCB->tcb_flags |= IN_RCV_IND;
+
+ CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
+
+ IF_TCPDBG(TCPDebug & TCP_DEBUG_RECEIVE) {
+ TCPTRACE((
+ "Indicating pending %d bytes, %d available\n",
+ RcvTCB->tcb_pendhead->ipr_size, RcvTCB->tcb_pendingcnt
+ ));
+ }
+
+ Status = (*Event)(EventContext, ConnContext,
+ TDI_RECEIVE_COPY_LOOKAHEAD | TDI_RECEIVE_NORMAL |
+ TDI_RECEIVE_ENTIRE_MESSAGE,
+ RcvTCB->tcb_pendhead->ipr_size, RcvTCB->tcb_pendingcnt,
+ &BytesTaken, RcvTCB->tcb_pendhead->ipr_buffer, &ERB);
+
+ IF_TCPDBG(TCPDebug & TCP_DEBUG_RECEIVE) {
+ TCPTRACE(("%d bytes taken\n", BytesTaken));
+ }
+
+ // See what the client did. If the return status is MORE_PROCESSING,
+ // we've been given a buffer. In that case put it on the front of the
+ // buffer queue, and if all the data wasn't taken go ahead and copy
+ // it into the new buffer chain.
+ if (Status == TDI_MORE_PROCESSING) {
+
+#ifdef NT
+
+ IF_TCPDBG(TCP_DEBUG_RECEIVE) {
+ TCPTRACE(("more processing on receive\n"));
+ }
+
+ CTEAssert(ERB != NULL);
+
+ IrpSp = IoGetCurrentIrpStackLocation(ERB);
+
+ Status = TCPPrepareIrpForCancel(
+ (PTCP_CONTEXT) IrpSp->FileObject->FsContext,
+ ERB,
+ TCPCancelRequest
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ RequestInformation = (PTDI_REQUEST_KERNEL_RECEIVE)
+ &(IrpSp->Parameters);
+
+ RcvReq->trr_rtn = TCPDataRequestComplete;
+ RcvReq->trr_context = ERB;
+ RcvReq->trr_buffer = ERB->MdlAddress;
+ RcvReq->trr_size = RequestInformation->ReceiveLength;
+ RcvReq->trr_uflags = (ushort *)
+ &(RequestInformation->ReceiveFlags);
+ RcvReq->trr_flags = (uint)(RequestInformation->ReceiveFlags);
+ RcvReq->trr_offset = 0;
+ RcvReq->trr_amt = 0;
+
+#else // NT
+
+ RcvReq->trr_rtn = ERB.erb_rtn;
+ RcvReq->trr_context = ERB.erb_context;
+ RcvReq->trr_buffer = ERB.erb_buffer;
+ RcvReq->trr_size = ERB.erb_size;
+ RcvReq->trr_uflags = ERB.erb_flags;
+ RcvReq->trr_flags = (uint)(*ERB.erb_flags);
+ RcvReq->trr_offset = 0;
+ RcvReq->trr_amt = 0;
+
+#endif // NT
+
+ CTEGetLock(&RcvTCB->tcb_lock, &TCBHandle);
+ RcvTCB->tcb_flags &= ~IN_RCV_IND;
+
+ // Push him on the front of the rcv. queue.
+ CTEAssert((RcvTCB->tcb_currcv == NULL) ||
+ (RcvTCB->tcb_currcv->trr_amt == 0));
+
+ if (RcvTCB->tcb_rcvhead == NULL) {
+ RcvTCB->tcb_rcvhead = RcvReq;
+ RcvTCB->tcb_rcvtail = RcvReq;
+ RcvReq->trr_next = NULL;
+ } else {
+ RcvReq->trr_next = RcvTCB->tcb_rcvhead;
+ RcvTCB->tcb_rcvhead = RcvReq;
+ }
+
+ RcvTCB->tcb_currcv = RcvReq;
+ RcvTCB->tcb_rcvhndlr = BufferData;
+
+ // Have to pick up the new size and pointers now, since things could
+ // have changed during the upcall.
+ Size = RcvTCB->tcb_pendingcnt;
+ NewBuf = RcvTCB->tcb_pendhead;
+
+ RcvTCB->tcb_pendingcnt = 0;
+ RcvTCB->tcb_pendhead = NULL;
+
+ CTEAssert(BytesTaken <= Size);
+
+ RcvTCB->tcb_indicated -= BytesTaken;
+ if ((Size -= BytesTaken) != 0) {
+
+ // Not everything was taken. Adjust the buffer chain to point
+ // beyond what was taken.
+ NewBuf = FreePartialRB(NewBuf, BytesTaken);
+
+ CTEAssert(NewBuf != NULL);
+
+ // We've adjusted the buffer chain. Call the BufferData
+ // handler.
+#ifdef VXD
+ CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
+ (void)BufferData(RcvTCB, TCP_FLAG_PUSH, NewBuf, Size);
+#else
+ (void)BufferData(RcvTCB, TCP_FLAG_PUSH, NewBuf, Size);
+ CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
+#endif
+
+ } else {
+ // All of the data was taken. Free the buffer chain. Since
+ // we were passed a buffer chain which we put on the head of
+ // the list, leave the rcvhandler pointing at BufferData.
+ CTEAssert(RcvTCB->tcb_rcvhndlr == BufferData);
+ CTEAssert(RcvTCB->tcb_indicated == 0);
+ CTEAssert(RcvTCB->tcb_rcvhead != NULL);
+
+ CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
+ FreeRBChain(NewBuf);
+ }
+
+ return;
+#ifdef NT
+ }
+ else {
+ //
+ // The IRP was cancelled before it was handed back to us.
+ // We'll pretend we never saw it. TCPPrepareIrpForCancel
+ // already completed it. The client may have taken data,
+ // so we will act as if success was returned.
+ //
+ ERB = NULL;
+ Status = TDI_SUCCESS;
+ }
+
+#endif // NT
+
+ }
+
+ CTEGetLock(&RcvTCB->tcb_lock, &TCBHandle);
+
+ RcvTCB->tcb_flags &= ~IN_RCV_IND;
+
+ // Status is not more processing. If it's not SUCCESS, the client
+ // didn't take any of the data. In either case we now need to
+ // see if all of the data was taken. If it wasn't, we're done.
+
+ if (Status == TDI_NOT_ACCEPTED)
+ BytesTaken = 0;
+
+ CTEAssert(RcvTCB->tcb_rcvhndlr == PendData);
+
+ RcvTCB->tcb_indicated -= BytesTaken;
+ Size = RcvTCB->tcb_pendingcnt;
+ NewBuf = RcvTCB->tcb_pendhead;
+
+ CTEAssert(BytesTaken <= Size);
+
+ // See if all of the data was taken.
+ if (BytesTaken == Size) {
+ // It was all taken. Zap the pending data information.
+ RcvTCB->tcb_pendingcnt = 0;
+ RcvTCB->tcb_pendhead = NULL;
+
+ CTEAssert(RcvTCB->tcb_indicated == 0);
+ if (RcvTCB->tcb_rcvhead == NULL) {
+ if (RcvTCB->tcb_rcvind != NULL)
+ RcvTCB->tcb_rcvhndlr = IndicateData;
+ } else
+ RcvTCB->tcb_rcvhndlr = BufferData;
+
+ CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
+ FreeRBChain(NewBuf);
+ break;
+ }
+
+ // It wasn't all taken. Adjust for what was taken, We also need to check
+ // to see if a receive buffer got posted during the indication. This
+ // would be weird, but not impossible.
+ NewBuf = FreePartialRB(NewBuf, BytesTaken);
+
+ CTEAssert(RcvTCB->tcb_rcvhndlr == PendData);
+
+ if (RcvTCB->tcb_rcvhead == NULL) {
+ RcvTCB->tcb_pendhead = NewBuf;
+ RcvTCB->tcb_pendingcnt -= BytesTaken;
+ if (RcvTCB->tcb_indicated != 0 || RcvTCB->tcb_rcvind == NULL) {
+ CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
+ break;
+ }
+
+ // From here, we'll loop around and indicate the new data that
+ // presumably came in during the previous indication.
+ } else {
+ // Just great. There's now a rcv. buffer on the TCB. Call the
+ // BufferData handler now.
+ RcvTCB->tcb_rcvhndlr = BufferData;
+ RcvTCB->tcb_pendingcnt = 0;
+ RcvTCB->tcb_pendhead = NULL;
+#ifdef VXD
+ CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
+ BytesTaken += BufferData(RcvTCB, TCP_FLAG_PUSH, NewBuf,
+ Size - BytesTaken);
+#else
+ BytesTaken += BufferData(RcvTCB, TCP_FLAG_PUSH, NewBuf,
+ Size - BytesTaken);
+ CTEFreeLock(&RcvTCB->tcb_lock, TCBHandle);
+#endif
+ break;
+ }
+
+ } // for (;;)
+
+ FreeRcvReq(RcvReq); // This isn't needed anymore.
+
+}
+
+//* DeliverUrgent - Deliver urgent data to a client.
+//
+// Called to deliver urgent data to a client. We assume the input
+// urgent data is in a buffer we can keep. The buffer can be NULL, in
+// which case we'll just look on the urgent pending queue for data.
+//
+// Input: RcvTCB - TCB to deliver on.
+// RcvBuf - RcvBuffer for urgent data.
+// Size - Number of bytes of urgent data to deliver.
+//
+// Returns: Nothing.
+//
+void
+DeliverUrgent(TCB *RcvTCB, IPRcvBuf *RcvBuf, uint Size,
+ CTELockHandle *TCBHandle)
+{
+ CTELockHandle AOHandle, AOTblHandle, ConnHandle;
+ TCPRcvReq *RcvReq, *PrevReq;
+ uint BytesTaken = 0;
+ IPRcvBuf *LastBuf;
+#ifdef NT
+ EventRcvBuffer *ERB;
+#else
+ EventRcvBuffer ERB;
+#endif
+ PRcvEvent ExpRcv;
+ PVOID ExpRcvContext;
+ PVOID ConnContext;
+ TDI_STATUS Status;
+
+ CTEStructAssert(RcvTCB, tcb);
+ CTEAssert(RcvTCB->tcb_refcnt != 0);
+
+ CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
+
+ // See if we have new data, or are processing old data.
+ if (RcvBuf != NULL) {
+ // We have new data. If the pending queue is not NULL, or we're already
+ // in this routine, just put the buffer on the end of the queue.
+ if (RcvTCB->tcb_urgpending != NULL || (RcvTCB->tcb_flags & IN_DELIV_URG)) {
+ IPRcvBuf *PrevRcvBuf;
+
+ // Put him on the end of the queue.
+ PrevRcvBuf = STRUCT_OF(IPRcvBuf, &RcvTCB->tcb_urgpending, ipr_next);
+ while (PrevRcvBuf->ipr_next != NULL)
+ PrevRcvBuf = PrevRcvBuf->ipr_next;
+
+ PrevRcvBuf->ipr_next = RcvBuf;
+ return;
+ }
+ } else {
+ // The input buffer is NULL. See if we have existing data, or are in
+ // this routine. If we have no existing data or are in this routine
+ // just return.
+ if (RcvTCB->tcb_urgpending == NULL ||
+ (RcvTCB->tcb_flags & IN_DELIV_URG)) {
+ return;
+ } else {
+ RcvBuf = RcvTCB->tcb_urgpending;
+ Size = RcvTCB->tcb_urgcnt;
+ RcvTCB->tcb_urgpending = NULL;
+ RcvTCB->tcb_urgcnt = 0;
+ }
+ }
+
+
+ CTEAssert(RcvBuf != NULL);
+ CTEAssert(!(RcvTCB->tcb_flags & IN_DELIV_URG));
+
+ // We know we have data to deliver, and we have a pointer and a size.
+ // Go into a loop, trying to deliver the data. On each iteration, we'll
+ // try to find a buffer for the data. If we find one, we'll copy and
+ // complete it right away. Otherwise we'll try and indicate it. If we
+ // can't indicate it, we'll put it on the pending queue and leave.
+ RcvTCB->tcb_flags |= IN_DELIV_URG;
+ RcvTCB->tcb_slowcount++;
+ RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+
+ do {
+ CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
+
+ BytesTaken = 0;
+
+ // First check the expedited queue.
+ if ((RcvReq = RcvTCB->tcb_exprcv) != NULL)
+ RcvTCB->tcb_exprcv = RcvReq->trr_next;
+ else {
+ // Nothing in the expedited rcv. queue. Walk down the ordinary
+ // receive queue, looking for a buffer that we can steal.
+ PrevReq = STRUCT_OF(TCPRcvReq, &RcvTCB->tcb_rcvhead, trr_next);
+ RcvReq = PrevReq->trr_next;
+ while (RcvReq != NULL) {
+ CTEStructAssert(RcvReq, trr);
+ if (RcvReq->trr_flags & TDI_RECEIVE_EXPEDITED) {
+ // This is a candidate.
+ if (RcvReq->trr_amt == 0) {
+
+ CTEAssert(RcvTCB->tcb_rcvhndlr == BufferData);
+
+ // And he has nothing currently in him. Pull him
+ // out of the queue.
+ if (RcvTCB->tcb_rcvtail == RcvReq) {
+ if (RcvTCB->tcb_rcvhead == RcvReq)
+ RcvTCB->tcb_rcvtail = NULL;
+ else
+ RcvTCB->tcb_rcvtail = PrevReq;
+ }
+
+ PrevReq->trr_next = RcvReq->trr_next;
+ if (RcvTCB->tcb_currcv == RcvReq) {
+ RcvTCB->tcb_currcv = RcvReq->trr_next;
+ if (RcvTCB->tcb_currcv == NULL) {
+ // We've taken the last receive from the list.
+ // Reset the rcvhndlr.
+ if (RcvTCB->tcb_rcvind != NULL &&
+ RcvTCB->tcb_indicated == 0)
+ RcvTCB->tcb_rcvhndlr = IndicateData;
+ else
+ RcvTCB->tcb_rcvhndlr = PendData;
+ }
+ }
+
+ break;
+ }
+ }
+ PrevReq = RcvReq;
+ RcvReq = PrevReq->trr_next;
+ }
+ }
+
+ // We've done our best to get a buffer. If we got one, copy into it
+ // now, and complete the request.
+
+ if (RcvReq != NULL) {
+ // Got a buffer.
+ CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle);
+ BytesTaken = CopyRcvToNdis(RcvBuf, RcvReq->trr_buffer, Size, 0, 0);
+ (*RcvReq->trr_rtn)(RcvReq->trr_context, TDI_SUCCESS, BytesTaken);
+ FreeRcvReq(RcvReq);
+ CTEGetLock(&RcvTCB->tcb_lock, TCBHandle);
+ RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind, BytesTaken);
+
+ } else {
+ // No posted buffer. If we can indicate, do so.
+ if (RcvTCB->tcb_urgind == 0) {
+ TCPConn *Conn;
+
+ // See if he has an expedited rcv handler.
+ ConnContext = RcvTCB->tcb_conncontext;
+ CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle);
+ CTEGetLock(&AddrObjTableLock, &AOTblHandle);
+ CTEGetLock(&ConnTableLock, &ConnHandle);
+ CTEGetLock(&RcvTCB->tcb_lock, TCBHandle);
+ if ((Conn = RcvTCB->tcb_conn) != NULL) {
+ CTEStructAssert(Conn, tc);
+ CTEAssert(Conn->tc_tcb == RcvTCB);
+ CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle);
+ if (Conn->tc_ao != NULL) {
+ AddrObj *AO;
+
+ AO = Conn->tc_ao;
+ CTEGetLock(&AO->ao_lock, &AOHandle);
+ if (AO_VALID(AO) && (ExpRcv = AO->ao_exprcv) != NULL) {
+ ExpRcvContext = AO->ao_exprcvcontext;
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+
+ // We're going to indicate.
+ RcvTCB->tcb_urgind = Size;
+ CTEFreeLock(&ConnTableLock, ConnHandle);
+ CTEFreeLock(&AddrObjTableLock, AOTblHandle);
+
+ Status = (*ExpRcv)(ExpRcvContext, ConnContext,
+ TDI_RECEIVE_COPY_LOOKAHEAD |
+ TDI_RECEIVE_ENTIRE_MESSAGE |
+ TDI_RECEIVE_EXPEDITED,
+ RcvBuf->ipr_size, Size, &BytesTaken,
+ RcvBuf->ipr_buffer, &ERB);
+
+ CTEGetLock(&RcvTCB->tcb_lock, TCBHandle);
+
+ // See what he did with it.
+ if (Status == TDI_MORE_PROCESSING) {
+ uint CopySize;
+
+ // He gave us a buffer.
+ if (BytesTaken == Size) {
+ // He gave us a buffer, but took all of
+ // it. We'll just return it to him.
+ CopySize = 0;
+ } else {
+ // We have some data to copy in.
+ RcvBuf = FreePartialRB(RcvBuf, BytesTaken);
+
+#ifdef NT
+ CopySize = CopyRcvToNdis(RcvBuf,
+ ERB->MdlAddress,
+ TCPGetMdlChainByteCount(ERB->MdlAddress),
+ 0, 0);
+#else // NT
+ CopySize = CopyRcvToNdis(RcvBuf,
+ ERB.erb_buffer, ERB.erb_size, 0, 0);
+#endif // NT
+
+ }
+ BytesTaken += CopySize;
+ RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind,
+ BytesTaken);
+ CTEFreeLock(&RcvTCB->tcb_lock, *TCBHandle);
+
+#ifdef NT
+
+ ERB->IoStatus.Status = TDI_SUCCESS;
+ ERB->IoStatus.Information = CopySize;
+ IoCompleteRequest(ERB, 2);
+
+#else // NT
+ (*ERB.erb_rtn)(ERB.erb_context, TDI_SUCCESS,
+ CopySize);
+#endif // NT
+
+ CTEGetLock(&RcvTCB->tcb_lock, TCBHandle);
+
+ } else {
+
+ // No buffer to deal with.
+ if (Status == TDI_NOT_ACCEPTED)
+ BytesTaken = 0;
+
+ RcvTCB->tcb_urgind -= MIN(RcvTCB->tcb_urgind,
+ BytesTaken);
+
+ }
+ goto checksize;
+ } else // No rcv. handler.
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+ }
+ // Conn->tc_ao == NULL.
+ CTEFreeLock(&ConnTableLock, ConnHandle);
+ CTEFreeLock(&AddrObjTableLock, AOTblHandle);
+ CTEGetLock(&RcvTCB->tcb_lock, TCBHandle);
+ } else {
+ // RcvTCB has invalid index.
+ CTEFreeLock(&ConnTableLock, *TCBHandle);
+ CTEFreeLock(&AddrObjTableLock, ConnHandle);
+ *TCBHandle = AOTblHandle;
+ }
+
+ }
+
+ // For whatever reason we couldn't indicate the data. At this point
+ // we hold the lock on the TCB. Push the buffer onto the pending
+ // queue and return.
+ CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
+
+ LastBuf = FindLastBuffer(RcvBuf);
+ LastBuf->ipr_next = RcvTCB->tcb_urgpending;
+ RcvTCB->tcb_urgpending = RcvBuf;
+ RcvTCB->tcb_urgcnt += Size;
+ break;
+ }
+
+checksize:
+ // See how much we took. If we took it all, check the pending queue.
+ // At this point, we should hold the lock on the TCB.
+ if (Size == BytesTaken) {
+ // Took it all.
+ FreeRBChain(RcvBuf);
+ RcvBuf = RcvTCB->tcb_urgpending;
+ Size = RcvTCB->tcb_urgcnt;
+ } else {
+ // We didn't manage to take it all. Free what we did take,
+ // and then merge with the pending queue.
+ RcvBuf = FreePartialRB(RcvBuf, BytesTaken);
+ Size = Size - BytesTaken + RcvTCB->tcb_urgcnt;
+ if (RcvTCB->tcb_urgpending != NULL) {
+
+ // Find the end of the current RcvBuf chain, so we can
+ // merge.
+
+ LastBuf = FindLastBuffer(RcvBuf);
+ LastBuf->ipr_next = RcvTCB->tcb_urgpending;
+ }
+ }
+
+ RcvTCB->tcb_urgpending = NULL;
+ RcvTCB->tcb_urgcnt = 0;
+
+ } while (RcvBuf != NULL);
+
+ CheckRBList(RcvTCB->tcb_urgpending, RcvTCB->tcb_urgcnt);
+
+ RcvTCB->tcb_flags &= ~IN_DELIV_URG;
+ if (--(RcvTCB->tcb_slowcount) == 0) {
+ RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+ }
+
+}
+
+//* PushData - Push all data back to the client.
+//
+// Called when we've received a FIN and need to push data to the client.
+//
+// Input: PushTCB - TCB to be pushed.
+//
+// Returns: Nothing.
+//
+void
+PushData(TCB *PushTCB)
+{
+ TCPRcvReq *RcvReq;
+
+ CTEStructAssert(PushTCB, tcb);
+
+ RcvReq = PushTCB->tcb_rcvhead;
+ while (RcvReq != NULL) {
+ CTEStructAssert(RcvReq, trr);
+ RcvReq->trr_flags |= TRR_PUSHED;
+ RcvReq = RcvReq->trr_next;
+ }
+
+ RcvReq = PushTCB->tcb_exprcv;
+ while (RcvReq != NULL) {
+ CTEStructAssert(RcvReq, trr);
+ RcvReq->trr_flags |= TRR_PUSHED;
+ RcvReq = RcvReq->trr_next;
+ }
+
+ if (PushTCB->tcb_rcvhead != NULL || PushTCB->tcb_exprcv != NULL)
+ DelayAction(PushTCB, NEED_RCV_CMPLT);
+
+}
+
+
+
+//* SplitRcvBuf - Split an IPRcvBuf into three pieces.
+//
+// This function takes an input IPRcvBuf and splits it into three pieces.
+// The first piece is the input buffer, which we just skip over. The second
+// and third pieces are actually copied, even if we already own them, so
+// that the may go to different places.
+//
+// Input: RcvBuf - RcvBuf chain to be split.
+// Size - Total size in bytes of rcvbuf chain.
+// Offset - Offset to skip over.
+// SecondSize - Size in bytes of second piece.
+// SecondBuf - Where to return second buffer pointer.
+// ThirdBuf - Where to return third buffer pointer.
+//
+// Returns: Nothing. *SecondBuf and *ThirdBuf are set to NULL if we can't
+// get memory for them.
+//
+void
+SplitRcvBuf(IPRcvBuf *RcvBuf, uint Size, uint Offset, uint SecondSize,
+ IPRcvBuf **SecondBuf, IPRcvBuf **ThirdBuf)
+{
+ IPRcvBuf *TempBuf;
+ uint ThirdSize;
+
+ CTEAssert(Offset < Size);
+ CTEAssert(((Offset + SecondSize) < Size) || (((Offset + SecondSize) == Size)
+ && ThirdBuf == NULL));
+
+ CTEAssert(RcvBuf != NULL);
+
+ // RcvBuf points at the buffer to copy from, and Offset is the offset into
+ // this buffer to copy from.
+ if (SecondBuf != NULL) {
+ // We need to allocate memory for a second buffer.
+ TempBuf = CTEAllocMem(sizeof(IPRcvBuf) + SecondSize);
+ if (TempBuf != NULL) {
+ TempBuf->ipr_size = SecondSize;
+ TempBuf->ipr_owner = IPR_OWNER_TCP;
+ TempBuf->ipr_buffer = (uchar *)(TempBuf + 1);
+ TempBuf->ipr_next = NULL;
+ CopyRcvToBuffer(TempBuf->ipr_buffer, RcvBuf, SecondSize, Offset);
+ *SecondBuf = TempBuf;
+ } else {
+ *SecondBuf = NULL;
+ if (ThirdBuf != NULL)
+ *ThirdBuf = NULL;
+ return;
+ }
+ }
+
+ if (ThirdBuf != NULL) {
+ // We need to allocate memory for a third buffer.
+ ThirdSize = Size - (Offset + SecondSize);
+ TempBuf = CTEAllocMem(sizeof(IPRcvBuf) + ThirdSize);
+
+ if (TempBuf != NULL) {
+ TempBuf->ipr_size = ThirdSize;
+ TempBuf->ipr_owner = IPR_OWNER_TCP;
+ TempBuf->ipr_buffer = (uchar *)(TempBuf + 1);
+ TempBuf->ipr_next = NULL;
+ CopyRcvToBuffer(TempBuf->ipr_buffer, RcvBuf, ThirdSize,
+ Offset + SecondSize);
+ *ThirdBuf = TempBuf;
+ } else
+ *ThirdBuf = NULL;
+ }
+
+
+}
+
+//* HandleUrgent - Handle urgent data.
+//
+// Called when an incoming segment has urgent data in it. We make sure there
+// really is urgent data in the segment, and if there is we try to dispose
+// of it either by putting it into a posted buffer or calling an exp. rcv.
+// indication handler.
+//
+// This routine is called at DPC level, and with the TCP locked.
+//
+// Urgent data handling is a little complicated. Each TCB has the starting
+// and ending sequence numbers of the 'current' (last received) bit of urgent
+// data. It is possible that the start of the current urgent data might be
+// greater than tcb_rcvnext, if urgent data came in, we handled it, and then
+// couldn't take the preceding normal data. The urgent valid flag is cleared
+// when the next byte of data the user would read (rcvnext - pendingcnt) is
+// greater than the end of urgent data - we do this so that we can correctly
+// support SIOCATMARK. We always seperate urgent data out of the data stream.
+// If the urgent valid field is set when we get into this routing we have
+// to play a couple of games. If the incoming segment starts in front of the
+// current urgent data, we truncate it before the urgent data, and put any
+// data after the urgent data on the reassemble queue. These gyrations are
+// done to avoid delivering the same urgent data twice. If the urgent valid
+// field in the TCB is set and the segment starts after the current urgent
+// data the new urgent information will replace the current urgent information.
+//
+// Input: RcvTCB - TCB to recv the data on.
+// RcvInfo - RcvInfo structure for the incoming segment.
+// RcvBuf - Pointer to IPRcvBuf train containing the
+// incoming segment.
+// Size - Pointer to size in bytes of data in the segment.
+//
+// Returns: Nothing.
+//
+void
+HandleUrgent(TCB *RcvTCB, TCPRcvInfo *RcvInfo, IPRcvBuf *RcvBuf, uint *Size)
+{
+ uint BytesInFront, BytesInBack; // Bytes in front of and in
+ // back of the urgent data.
+ uint UrgSize; // Size in bytes of urgent data.
+ SeqNum UrgStart, UrgEnd;
+ IPRcvBuf *EndBuf, *UrgBuf;
+ TCPRcvInfo NewRcvInfo;
+ CTELockHandle TCBHandle;
+
+ CTEStructAssert(RcvTCB, tcb);
+ CTEAssert(RcvTCB->tcb_refcnt != 0);
+ CTEAssert(RcvInfo->tri_flags & TCP_FLAG_URG);
+ CTEAssert(SEQ_EQ(RcvInfo->tri_seq, RcvTCB->tcb_rcvnext));
+
+ // First, validate the urgent pointer.
+ if (RcvTCB->tcb_flags & BSD_URGENT) {
+ // We're using BSD style urgent data. We assume that the urgent
+ // data is one byte long, and that the urgent pointer points one
+ // after the urgent data instead of at the last byte of urgent data.
+ // See if the urgent data is in this segment.
+
+ if (RcvInfo->tri_urgent == 0 || RcvInfo->tri_urgent > *Size) {
+ // Not in this segment. Clear the urgent flag and return.
+ RcvInfo->tri_flags &= ~TCP_FLAG_URG;
+ return;
+ }
+
+ UrgSize = 1;
+ BytesInFront = RcvInfo->tri_urgent - 1;
+
+ } else {
+
+ // This is not BSD style urgent. We assume that the urgent data
+ // starts at the front of the segment and the last byte is pointed
+ // to by the urgent data pointer.
+
+ BytesInFront = 0;
+ UrgSize = MIN(RcvInfo->tri_urgent + 1, *Size);
+
+ }
+
+ BytesInBack = *Size - BytesInFront - UrgSize;
+
+ // UrgStart and UrgEnd are the first and last sequence numbers of the
+ // urgent data in this segment.
+
+ UrgStart = RcvInfo->tri_seq + BytesInFront;
+ UrgEnd = UrgStart + UrgSize - 1;
+
+ if (!(RcvTCB->tcb_flags & URG_INLINE)) {
+
+ EndBuf = NULL;
+
+ // Now see if this overlaps with any urgent data we've already seen.
+ if (RcvTCB->tcb_flags & URG_VALID) {
+ // We have some urgent data still around. See if we've advanced
+ // rcvnext beyond the urgent data. If we have, this is new urgent
+ // data, and we can go ahead and process it (although anyone doing
+ // an SIOCATMARK socket command might get confused). If we haven't
+ // consumed the data in front of the existing urgent data yet, we'll
+ // truncate this seg. to that amount and push the rest onto the
+ // reassembly queue. Note that rcvnext should never fall between
+ // tcb_urgstart and tcb_urgend.
+
+ CTEAssert(SEQ_LT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgstart) ||
+ SEQ_GT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgend));
+
+ if (SEQ_LT(RcvTCB->tcb_rcvnext, RcvTCB->tcb_urgstart)) {
+
+ // There appears to be some overlap in the data stream. Split
+ // the buffer up into pieces that come before the current urgent
+ // data and after the current urgent data, putting the latter
+ // on the reassembly queue.
+
+ UrgSize = RcvTCB->tcb_urgend - RcvTCB->tcb_urgstart + 1;
+
+ BytesInFront = MIN(RcvTCB->tcb_urgstart - RcvTCB->tcb_rcvnext,
+ (int) *Size);
+
+ if (SEQ_GT(RcvTCB->tcb_rcvnext + *Size, RcvTCB->tcb_urgend)) {
+ // We have data after this piece of urgent data.
+ BytesInBack = RcvTCB->tcb_rcvnext + *Size -
+ RcvTCB->tcb_urgend;
+ } else
+ BytesInBack = 0;
+
+ SplitRcvBuf(RcvBuf, *Size, BytesInFront, UrgSize, NULL,
+ (BytesInBack ? &EndBuf : NULL));
+
+ if (EndBuf != NULL) {
+ NewRcvInfo.tri_seq = RcvTCB->tcb_urgend + 1;
+ NewRcvInfo.tri_flags = RcvInfo->tri_flags;
+ NewRcvInfo.tri_urgent = UrgEnd - NewRcvInfo.tri_seq;
+ if (RcvTCB->tcb_flags & BSD_URGENT)
+ NewRcvInfo.tri_urgent++;
+ NewRcvInfo.tri_ack = RcvInfo->tri_ack;
+ NewRcvInfo.tri_window = RcvInfo->tri_window;
+ PutOnRAQ(RcvTCB, &NewRcvInfo, EndBuf, BytesInBack);
+ }
+
+ *Size = BytesInFront;
+ RcvInfo->tri_flags &= ~TCP_FLAG_URG;
+ return;
+ }
+ }
+
+ // We have urgent data we can process now. Split it into its component
+ // parts, the first part, the urgent data, and the stuff after the
+ // urgent data.
+ SplitRcvBuf(RcvBuf, *Size, BytesInFront, UrgSize, &UrgBuf,
+ (BytesInBack ? &EndBuf : NULL));
+
+ // If we managed to split out the end stuff, put it on the queue now.
+ if (EndBuf != NULL) {
+ NewRcvInfo.tri_seq = RcvInfo->tri_seq + BytesInFront + UrgSize;
+ NewRcvInfo.tri_flags = RcvInfo->tri_flags & ~TCP_FLAG_URG;
+ NewRcvInfo.tri_ack = RcvInfo->tri_ack;
+ NewRcvInfo.tri_window = RcvInfo->tri_window;
+ PutOnRAQ(RcvTCB, &NewRcvInfo, EndBuf, BytesInBack);
+ }
+
+
+ if (UrgBuf != NULL) {
+ // We succesfully split the urgent data out.
+ if (!(RcvTCB->tcb_flags & URG_VALID)) {
+ RcvTCB->tcb_flags |= URG_VALID;
+ RcvTCB->tcb_slowcount++;
+ RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+ }
+ RcvTCB->tcb_urgstart = UrgStart;
+ RcvTCB->tcb_urgend = UrgEnd;
+#ifdef VXD
+#ifdef DEBUG
+ TCBHandle = DEFAULT_SIMIRQL;
+#endif
+#else
+ TCBHandle = DISPATCH_LEVEL;
+#endif
+ DeliverUrgent(RcvTCB, UrgBuf, UrgSize, &TCBHandle);
+ }
+
+ *Size = BytesInFront;
+
+ } else {
+ // Urgent data is to be processed inline. We just need to remember
+ // where it is and treat it as normal data. If there's already urgent
+ // data, we remember the latest urgent data.
+
+ RcvInfo->tri_flags &= ~TCP_FLAG_URG;
+
+ if (RcvTCB->tcb_flags & URG_VALID) {
+ // There is urgent data. See if this stuff comes after the existing
+ // urgent data.
+
+ if (SEQ_LTE(UrgEnd, RcvTCB->tcb_urgend)) {
+ // The existing urgent data completely overlaps this stuff,
+ // so ignore this.
+ return;
+ }
+ } else {
+ RcvTCB->tcb_flags |= URG_VALID;
+ RcvTCB->tcb_slowcount++;
+ RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+ }
+
+ RcvTCB->tcb_urgstart = UrgStart;
+ RcvTCB->tcb_urgend = UrgEnd;
+ }
+
+ return;
+}
+
+//* TdiReceive - Process a receive request.
+//
+// This is the main TDI receive request handler. We validate the connection
+// and make sure that we have a TCB in the proper state, then we try to
+// allocate a receive request structure. If that succeeds, we'll look and
+// see what's happening on the TCB - if there's pending data, we'll put it
+// in the buffer. Otherwise we'll just queue the receive for later.
+//
+// Input: Request - TDI_REQUEST structure for this request.
+// Flags - Pointer to flags word.
+// RcvLength - Pointer to length in bytes of receive buffer.
+// Buffer - Pointer to buffer to take data.
+//
+// Returns: TDI_STATUS of request.
+//
+TDI_STATUS
+TdiReceive(PTDI_REQUEST Request, ushort *Flags, uint *RcvLength,
+ PNDIS_BUFFER Buffer)
+{
+ TCPConn *Conn;
+ TCB *RcvTCB;
+ TCPRcvReq *RcvReq;
+ CTELockHandle ConnTableHandle, TCBHandle;
+ TDI_STATUS Error;
+ ushort UFlags;
+
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+
+ Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ RcvTCB = Conn->tc_tcb;
+ if (RcvTCB != NULL) {
+ CTEStructAssert(RcvTCB, tcb);
+ CTEGetLock(&RcvTCB->tcb_lock, &TCBHandle);
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+ UFlags = *Flags;
+
+ if ((DATA_RCV_STATE(RcvTCB->tcb_state) ||
+ (RcvTCB->tcb_pendingcnt != 0 && (UFlags & TDI_RECEIVE_NORMAL)) ||
+ (RcvTCB->tcb_urgcnt != 0 && (UFlags & TDI_RECEIVE_EXPEDITED)))
+ && !CLOSING(RcvTCB)) {
+ // We have a TCB, and it's valid. Get a receive request now.
+
+ CheckRBList(RcvTCB->tcb_pendhead, RcvTCB->tcb_pendingcnt);
+
+ RcvReq = GetRcvReq();
+ if (RcvReq != NULL) {
+
+ RcvReq->trr_rtn = Request->RequestNotifyObject;
+ RcvReq->trr_context = Request->RequestContext;
+ RcvReq->trr_buffer = Buffer;
+ RcvReq->trr_size = *RcvLength;
+ RcvReq->trr_uflags = Flags;
+ RcvReq->trr_offset = 0;
+ RcvReq->trr_amt = 0;
+ RcvReq->trr_flags = (uint)UFlags;
+ if ((UFlags & (TDI_RECEIVE_NORMAL | TDI_RECEIVE_EXPEDITED))
+ != TDI_RECEIVE_EXPEDITED) {
+ // This is not an expedited only receive. Put him
+ // on the normal receive queue.
+ RcvReq->trr_next = NULL;
+ if (RcvTCB->tcb_rcvhead == NULL) {
+ // The receive queue is empty. Put him on the front.
+ RcvTCB->tcb_rcvhead = RcvReq;
+ RcvTCB->tcb_rcvtail = RcvReq;
+ } else {
+ RcvTCB->tcb_rcvtail->trr_next = RcvReq;
+ RcvTCB->tcb_rcvtail = RcvReq;
+ }
+
+ // If this recv. can't hold urgent data or there isn't
+ // any pending urgent data continue processing.
+ if (!(UFlags & TDI_RECEIVE_EXPEDITED) ||
+ RcvTCB->tcb_urgcnt == 0) {
+ // If tcb_currcv is NULL, there is no currently
+ // active receive. In this case, check to see if
+ // there is pending data and that we are not
+ // currently in a receive indication handler. If
+ // both of these are true then deal with the
+ // pending data.
+ if (RcvTCB->tcb_currcv == NULL) {
+ RcvTCB->tcb_currcv = RcvReq;
+ // No currently active receive.
+ if (!(RcvTCB->tcb_flags & IN_RCV_IND)) {
+ // Not in a rcv. indication.
+ RcvTCB->tcb_rcvhndlr = BufferData;
+ if (RcvTCB->tcb_pendhead == NULL) {
+ CTEFreeLock(&RcvTCB->tcb_lock,
+ ConnTableHandle);
+ return TDI_PENDING;
+ } else {
+ IPRcvBuf *PendBuffer;
+ uint PendSize;
+ uint OldRcvWin;
+
+ // We have pending data to deal with.
+ PendBuffer = RcvTCB->tcb_pendhead;
+ PendSize = RcvTCB->tcb_pendingcnt;
+ RcvTCB->tcb_pendhead = NULL;
+ RcvTCB->tcb_pendingcnt = 0;
+ RcvTCB->tcb_refcnt++;
+
+ // We assume that BufferData holds
+ // the lock (does not yield) during
+ // this call. If this changes for some
+ // reason, we'll have to fix the code
+ // below that does the window update
+ // check. See the comments in the
+ // BufferData() routine for more info.
+#ifdef VXD
+ CTEFreeLock(&RcvTCB->tcb_lock,
+ ConnTableHandle);
+ (void)BufferData(RcvTCB, TCP_FLAG_PUSH,
+ PendBuffer, PendSize);
+ CTEGetLock(&RcvTCB->tcb_lock,
+ &ConnTableHandle);
+#else
+ (void)BufferData(RcvTCB, TCP_FLAG_PUSH,
+ PendBuffer, PendSize);
+#endif
+ CheckTCBRcv(RcvTCB);
+ // Now we need to see if the window
+ // has changed. If it has, send an
+ // ACK.
+ OldRcvWin = RcvTCB->tcb_rcvwin;
+ if (OldRcvWin != RcvWin(RcvTCB)) {
+ // The window has changed, so send
+ // an ACK.
+
+ DelayAction(RcvTCB, NEED_ACK);
+ }
+
+ DerefTCB(RcvTCB, ConnTableHandle);
+ ProcessTCBDelayQ();
+ return TDI_PENDING;
+ }
+ }
+ // In a receive indication. The recv. request
+ // is now on the queue, so just fall through
+ // to the return.
+
+ }
+ // A rcv. is currently active. No need to do
+ // anything else.
+ CTEFreeLock(&RcvTCB->tcb_lock, ConnTableHandle);
+ return TDI_PENDING;
+ } else {
+ // This buffer can hold urgent data and we have
+ // some pending. Deliver it now.
+ RcvTCB->tcb_refcnt++;
+ DeliverUrgent(RcvTCB, NULL, 0, &ConnTableHandle);
+ DerefTCB(RcvTCB, ConnTableHandle);
+ return TDI_PENDING;
+ }
+ } else {
+ TCPRcvReq *Temp;
+
+ // This is an expedited only receive. Just put him
+ // on the end of the expedited receive queue.
+ Temp = STRUCT_OF(TCPRcvReq, &RcvTCB->tcb_exprcv,
+ trr_next);
+ while (Temp->trr_next != NULL)
+ Temp = Temp->trr_next;
+
+ RcvReq->trr_next = NULL;
+ Temp->trr_next = RcvReq;
+ if (RcvTCB->tcb_urgpending != NULL) {
+ RcvTCB->tcb_refcnt++;
+ DeliverUrgent(RcvTCB, NULL, 0, &ConnTableHandle);
+ DerefTCB(RcvTCB, ConnTableHandle);
+ return TDI_PENDING;
+ } else
+ Error = TDI_PENDING;
+ }
+ } else {
+ // Couldn't get a rcv. req.
+ Error = TDI_NO_RESOURCES;
+ }
+ } else {
+ // The TCB is in an invalid state.
+ Error = TDI_INVALID_STATE;
+ }
+ CTEFreeLock(&RcvTCB->tcb_lock, ConnTableHandle);
+ return Error;
+ } else // No TCB for connection.
+ Error = TDI_INVALID_STATE;
+ } else // No connection.
+ Error = TDI_INVALID_CONNECTION;
+
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ return Error;
+
+}
diff --git a/private/ntos/tdi/tcpip/tcp/tcpdeliv.h b/private/ntos/tdi/tcpip/tcp/tcpdeliv.h
new file mode 100644
index 000000000..94fe73a7d
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcpdeliv.h
@@ -0,0 +1,44 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCPDELIV.H - TCP data delivery definitions.
+//
+// This file contains the definitions for structures used by the data
+// delivery code.
+//
+
+extern void FreeRcvReq(struct TCPRcvReq *FreedReq);
+
+extern uint IndicateData(struct TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer,
+ uint Size);
+extern uint BufferData(struct TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer,
+ uint Size);
+extern uint PendData(struct TCB *RcvTCB, uint RcvFlags, IPRcvBuf *InBuffer,
+ uint Size);
+
+#ifdef VXD
+extern void IndicatePendingData(struct TCB *RcvTCB, struct TCPRcvReq *RcvReq);
+#else
+extern void IndicatePendingData(struct TCB *RcvTCB, struct TCPRcvReq *RcvReq,
+ CTELockHandle TCBHandle);
+#endif
+
+extern void HandleUrgent(struct TCB *RcvTCB, struct TCPRcvInfo *RcvInfo,
+ IPRcvBuf *RcvBuf, uint *Size);
+
+extern TDI_STATUS TdiReceive(PTDI_REQUEST Request, ushort *Flags,
+ uint *RcvLength, PNDIS_BUFFER Buffer);
+extern IPRcvBuf *FreePartialRB(IPRcvBuf *RB, uint Size);
+extern void PushData(struct TCB *PushTCB);
+
+EXTERNAL_LOCK(TCPRcvReqFreeLock) // Protects rcv req free list.
+
+#ifdef NT
+extern SLIST_HEADER TCPRcvReqFree;
+#endif
+
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/tcpip.def b/private/ntos/tdi/tcpip/tcp/tcpip.def
new file mode 100644
index 000000000..c02c158c1
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcpip.def
@@ -0,0 +1,8 @@
+
+NAME TCPIP.SYS
+
+DESCRIPTION 'TCPIP.SYS'
+
+EXPORTS
+ IPAddInterface
+ IPDelInterface
diff --git a/private/ntos/tdi/tcpip/tcp/tcpip.rc b/private/ntos/tdi/tcpip/tcp/tcpip.rc
new file mode 100644
index 000000000..bd0b7a4ad
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcpip.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "TCP/IP driver"
+
+#define VER_INTERNALNAME_STR "tcpip.sys"
+#define VER_ORIGINALFILENAME_STR "tcpip.sys"
+
+#include <common.ver>
diff --git a/private/ntos/tdi/tcpip/tcp/tcprcv.c b/private/ntos/tdi/tcpip/tcp/tcprcv.c
new file mode 100644
index 000000000..46698a2b0
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcprcv.c
@@ -0,0 +1,3397 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCPRCV.C - TCP receive protocol code.
+//
+// This file contains the code for handling incoming TCP packets.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#ifdef VXD
+#include "tdivxd.h"
+#include "tdistat.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "tcp.h"
+#include "tcb.h"
+#include "tcpconn.h"
+#include "tcpsend.h"
+#include "tcprcv.h"
+#include "tcpdeliv.h"
+#include "tlcommon.h"
+#include "info.h"
+#include "tcpcfg.h"
+#include "secfltr.h"
+
+uint RequestCompleteFlags;
+
+Queue ConnRequestCompleteQ;
+Queue SendCompleteQ;
+
+Queue TCBDelayQ;
+
+#ifdef SYN_ATTACK
+DEFINE_LOCK_STRUCTURE(SynAttLock)
+#endif
+DEFINE_LOCK_STRUCTURE(RequestCompleteLock)
+DEFINE_LOCK_STRUCTURE(TCBDelayLock)
+
+ulong TCBDelayRtnCount;
+ulong TCBDelayRtnLimit;
+#define TCB_DELAY_RTN_LIMIT 4
+
+EXTERNAL_LOCK(TCBTableLock)
+EXTERNAL_LOCK(AddrObjTableLock)
+EXTERNAL_LOCK(ConnTableLock)
+
+extern IPInfo LocalNetInfo;
+
+#define PERSIST_TIMEOUT MS_TO_TICKS(500)
+
+
+void ResetSendNext(TCB *SeqTCB, SeqNum NewSeq);
+
+#if FAST_RETRANSMIT
+extern uint MaxDupAcks;
+void ResetAndFastSend(TCB *SeqTCB, SeqNum NewSeq);
+#endif
+
+
+#ifdef NT
+
+NTSTATUS
+TCPPrepareIrpForCancel(
+ PTCP_CONTEXT TcpContext,
+ PIRP Irp,
+ PDRIVER_CANCEL CancelRoutine
+ );
+
+extern void
+TCPRequestComplete(
+ void *Context,
+ unsigned int Status,
+ unsigned int UnUsed
+ );
+
+VOID
+TCPCancelRequest(
+ PDEVICE_OBJECT Device,
+ PIRP Irp
+ );
+
+//
+// All of the init code can be discarded.
+//
+#ifdef ALLOC_PRAGMA
+
+int InitTCPRcv(void);
+void UnInitTCPRcv(void);
+
+#pragma alloc_text(INIT, InitTCPRcv)
+#pragma alloc_text(INIT, UnInitTCPRcv)
+
+#endif // ALLOC_PRAGMA
+
+#ifdef RASAUTODIAL
+extern BOOLEAN fAcdLoadedG;
+#endif
+
+#endif // NT
+
+//* AdjustRcvWin - Adjust the receive window on a TCB.
+//
+// A utility routine that adjusts the receive window to an even multiple of
+// the local segment size. We round it up to the next closest multiple, or
+// leave it alone if it's already an event multiple. We assume we have
+// exclusive access to the input TCB.
+//
+// Input: WinTCB - TCB to be adjusted.
+//
+// Returns: Nothing.
+//
+void
+AdjustRcvWin(TCB *WinTCB)
+{
+ ushort LocalMSS;
+ uchar FoundMSS;
+ ulong SegmentsInWindow;
+
+ CTEAssert(WinTCB->tcb_defaultwin != 0);
+ CTEAssert(WinTCB->tcb_rcvwin != 0);
+ CTEAssert(WinTCB->tcb_remmss != 0);
+
+ if (WinTCB->tcb_flags & WINDOW_SET)
+ return;
+
+ // First, get the local MSS by calling IP.
+
+ FoundMSS = (*LocalNetInfo.ipi_getlocalmtu)(WinTCB->tcb_saddr, &LocalMSS);
+
+ // If we didn't find it, error out.
+ if (!FoundMSS) {
+ CTEAssert(FALSE);
+ return;
+ }
+
+ LocalMSS -= sizeof(TCPHeader);
+ LocalMSS = MIN(LocalMSS, WinTCB->tcb_remmss);
+
+ SegmentsInWindow = WinTCB->tcb_defaultwin / (ulong)LocalMSS;
+
+ // Make sure we have at least 4 segments in window, if that wouldn't make
+ // the window too big.
+ if (SegmentsInWindow < 4) {
+
+ // We have fewer than four segments in the window. Round up to 4
+ // if we can do so without exceeding the maximum window size; otherwise
+ // use the maximum multiple that we can fit in 64K. The exception is if
+ // we can only fit one integral multiple in the window - in that case
+ // we'll use a window of 0xffff.
+ if (LocalMSS <= (0xffff/4)) {
+ WinTCB->tcb_defaultwin = (uint)(4 * LocalMSS);
+ } else {
+ ulong SegmentsInMaxWindow;
+
+ // Figure out the maximum number of segments we could possibly
+ // fit in a window. If this is > 1, use that as the basis for
+ // our window size. Otherwise use a maximum size window.
+
+ SegmentsInMaxWindow = 0xffff/(ulong)LocalMSS;
+ if (SegmentsInMaxWindow != 1)
+ WinTCB->tcb_defaultwin = SegmentsInMaxWindow * (ulong)LocalMSS;
+ else
+ WinTCB->tcb_defaultwin = 0xffff;
+ }
+
+ WinTCB->tcb_rcvwin = WinTCB->tcb_defaultwin;
+
+ } else
+ // If it's not already an even multiple, bump the default and current
+ // windows to the nearest multiple.
+ if ((SegmentsInWindow * (ulong)LocalMSS) != WinTCB->tcb_defaultwin) {
+ ulong NewWindow;
+
+ NewWindow = (SegmentsInWindow + 1) * (ulong)LocalMSS;
+
+ // Don't let the new window be > 64K.
+ if (NewWindow <= 0xffff) {
+ WinTCB->tcb_defaultwin = (uint)NewWindow;
+ WinTCB->tcb_rcvwin = (uint)NewWindow;
+ }
+ }
+
+}
+
+//* CompleteRcvs - Complete rcvs on a TCB.
+//
+// Called when we need to complete rcvs on a TCB. We'll pull things from
+// the TCB's rcv queue, as long as there are rcvs that have the PUSH bit
+// set.
+//
+// Input: CmpltTCB - TCB to complete on.
+//
+// Returns: Nothing.
+//
+void
+CompleteRcvs(TCB *CmpltTCB)
+{
+ CTELockHandle TCBHandle;
+ TCPRcvReq *CurrReq, *NextReq, *IndReq;
+
+ CTEStructAssert(CmpltTCB, tcb);
+ CTEAssert(CmpltTCB->tcb_refcnt != 0);
+
+ CTEGetLock(&CmpltTCB->tcb_lock, &TCBHandle);
+
+ if (!CLOSING(CmpltTCB) && !(CmpltTCB->tcb_flags & RCV_CMPLTING)
+ && (CmpltTCB->tcb_rcvhead != NULL)) {
+
+ CmpltTCB->tcb_flags |= RCV_CMPLTING;
+
+ for (;;) {
+
+ CurrReq = CmpltTCB->tcb_rcvhead;
+ IndReq = NULL;
+ do {
+ CTEStructAssert(CurrReq, trr);
+
+ if (CurrReq->trr_flags & TRR_PUSHED) {
+ // Need to complete this one. If this is the current rcv
+ // advance the current rcv to the next one in the list.
+ // Then set the list head to the next one in the list.
+
+ CTEAssert(CurrReq->trr_amt != 0 ||
+ !DATA_RCV_STATE(CmpltTCB->tcb_state));
+
+ NextReq = CurrReq->trr_next;
+ if (CmpltTCB->tcb_currcv == CurrReq)
+ CmpltTCB->tcb_currcv = NextReq;
+
+ CmpltTCB->tcb_rcvhead = NextReq;
+
+ if (NextReq == NULL) {
+ // We've just removed the last buffer. Set the
+ // rcvhandler to PendData, in case something
+ // comes in during the callback.
+ CTEAssert(CmpltTCB->tcb_rcvhndlr != IndicateData);
+ CmpltTCB->tcb_rcvhndlr = PendData;
+ }
+
+ CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle);
+ if (CurrReq->trr_uflags != NULL)
+ *(CurrReq->trr_uflags) =
+ TDI_RECEIVE_NORMAL | TDI_RECEIVE_ENTIRE_MESSAGE;
+
+ (*CurrReq->trr_rtn)(CurrReq->trr_context, TDI_SUCCESS,
+ CurrReq->trr_amt);
+ if (IndReq != NULL)
+ FreeRcvReq(CurrReq);
+ else
+ IndReq = CurrReq;
+ CTEGetLock(&CmpltTCB->tcb_lock, &TCBHandle);
+ CurrReq = CmpltTCB->tcb_rcvhead;
+
+ } else
+ // This one isn't to be completed, so bail out.
+ break;
+ } while (CurrReq != NULL);
+
+ // Now see if we've completed all of the requests. If we have, we
+ // may need to deal with pending data and/or reset the rcv. handler.
+ if (CurrReq == NULL) {
+ // We've completed everything that can be, so stop the push
+ // timer. We don't stop it if CurrReq isn't NULL because we
+ // want to make sure later data is eventually pushed.
+ STOP_TCB_TIMER(CmpltTCB->tcb_pushtimer);
+
+ CTEAssert(IndReq != NULL);
+ // No more recv. requests.
+ if (CmpltTCB->tcb_pendhead == NULL) {
+ FreeRcvReq(IndReq);
+ // No pending data. Set the rcv. handler to either PendData
+ // or IndicateData.
+ if (!(CmpltTCB->tcb_flags & (DISC_PENDING | GC_PENDING))) {
+ if (CmpltTCB->tcb_rcvind != NULL &&
+ CmpltTCB->tcb_indicated == 0)
+ CmpltTCB->tcb_rcvhndlr = IndicateData;
+ else
+ CmpltTCB->tcb_rcvhndlr = PendData;
+ } else {
+ goto Complete_Notify;
+ }
+
+ } else {
+ // We have pending data to deal with.
+ if (CmpltTCB->tcb_rcvind != NULL &&
+ CmpltTCB->tcb_indicated == 0) {
+ // There's a rcv. indicate handler on this TCB. Call
+ // the indicate handler with the pending data.
+#ifdef VXD
+ CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle);
+ IndicatePendingData(CmpltTCB, IndReq);
+ SendACK(CmpltTCB);
+ CTEGetLock(&CmpltTCB->tcb_lock, &TCBHandle);
+#else
+ IndicatePendingData(CmpltTCB, IndReq, TCBHandle);
+ SendACK(CmpltTCB);
+ CTEGetLock(&CmpltTCB->tcb_lock, &TCBHandle);
+#endif
+ // See if a buffer has been posted. If so, we'll need
+ // to check and see if it needs to be completed.
+ if (CmpltTCB->tcb_rcvhead != NULL)
+ continue;
+ else {
+ // If the pending head is now NULL, we've used up
+ // all the data.
+ if (CmpltTCB->tcb_pendhead == NULL &&
+ (CmpltTCB->tcb_flags &
+ (DISC_PENDING | GC_PENDING)))
+ goto Complete_Notify;
+ }
+
+ } else {
+ // No indicate handler, so nothing to do. The rcv.
+ // handler should already be set to PendData.
+ FreeRcvReq(IndReq);
+ CTEAssert(CmpltTCB->tcb_rcvhndlr == PendData);
+ }
+ }
+ } else {
+ if (IndReq != NULL)
+ FreeRcvReq(IndReq);
+ CTEAssert(CmpltTCB->tcb_rcvhndlr == BufferData);
+ }
+
+ break;
+ }
+ CmpltTCB->tcb_flags &= ~RCV_CMPLTING;
+ }
+ CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle);
+ return;
+
+Complete_Notify:
+ // Something is pending. Figure out what it is, and do
+ // it.
+ if (CmpltTCB->tcb_flags & GC_PENDING) {
+ CmpltTCB->tcb_flags &= ~RCV_CMPLTING;
+ // Bump the refcnt, because GracefulClose will
+ // deref the TCB and we're not really done with
+ // it yet.
+ CmpltTCB->tcb_refcnt++;
+ GracefulClose(CmpltTCB,
+ CmpltTCB->tcb_flags & TW_PENDING, TRUE,
+ TCBHandle);
+
+ } else
+ if (CmpltTCB->tcb_flags & DISC_PENDING) {
+ CmpltTCB->tcb_flags &= ~DISC_PENDING;
+ CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle);
+ NotifyOfDisc(CmpltTCB, NULL, TDI_GRACEFUL_DISC);
+
+ CTEGetLock(&CmpltTCB->tcb_lock, &TCBHandle);
+ CmpltTCB->tcb_flags &= ~RCV_CMPLTING;
+ CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle);
+ } else {
+ CTEAssert(FALSE);
+ CTEFreeLock(&CmpltTCB->tcb_lock, TCBHandle);
+ }
+
+ return;
+
+}
+
+//* ProcessTCBDelayQ - Process TCBs on the delayed Q.
+//
+// Called at various times to process TCBs on the delayed Q.
+//
+// Entry: Nothing.
+//
+// Returns: Nothing.
+//
+void
+ProcessTCBDelayQ(void)
+{
+ CTELockHandle QHandle;
+ TCB *DelayTCB;
+ CTELockHandle TCBHandle;
+
+ CTEGetLock(&TCBDelayLock, &QHandle);
+
+ // Check for recursion. We do not stop recursion completely, only
+ // limit it. This is done to allow multiple threads to process the
+ // TCBDelayQ simultaneously.
+
+ TCBDelayRtnCount++;
+ if (TCBDelayRtnCount > TCBDelayRtnLimit) {
+ TCBDelayRtnCount--;
+ CTEFreeLock(&TCBDelayLock, QHandle);
+ return;
+ }
+
+ while (!EMPTYQ(&TCBDelayQ)) {
+
+ DEQUEUE(&TCBDelayQ, DelayTCB, TCB, tcb_delayq);
+ CTEStructAssert(DelayTCB, tcb);
+ CTEAssert(DelayTCB->tcb_refcnt != 0);
+ CTEAssert(DelayTCB->tcb_flags & IN_DELAY_Q);
+ CTEFreeLock(&TCBDelayLock, QHandle);
+
+ CTEGetLock(&DelayTCB->tcb_lock, &TCBHandle);
+
+ while (!CLOSING(DelayTCB) && (DelayTCB->tcb_flags & DELAYED_FLAGS)) {
+
+ if (DelayTCB->tcb_flags & NEED_RCV_CMPLT) {
+ DelayTCB->tcb_flags &= ~NEED_RCV_CMPLT;
+ CTEFreeLock(&DelayTCB->tcb_lock, TCBHandle);
+ CompleteRcvs(DelayTCB);
+ CTEGetLock(&DelayTCB->tcb_lock, &TCBHandle);
+ }
+
+ if (DelayTCB->tcb_flags & NEED_OUTPUT) {
+ DelayTCB->tcb_flags &= ~NEED_OUTPUT;
+ DelayTCB->tcb_refcnt++;
+#ifdef VXD
+ CTEFreeLock(&DelayTCB->tcb_lock, TCBHandle);
+ TCPSend(DelayTCB);
+#else
+ TCPSend(DelayTCB, TCBHandle);
+#endif
+ CTEGetLock(&DelayTCB->tcb_lock, &TCBHandle);
+ }
+
+ if (DelayTCB->tcb_flags & NEED_ACK) {
+ DelayTCB->tcb_flags &= ~NEED_ACK;
+ CTEFreeLock(&DelayTCB->tcb_lock, TCBHandle);
+ SendACK(DelayTCB);
+ CTEGetLock(&DelayTCB->tcb_lock, &TCBHandle);
+ }
+
+ }
+
+ DelayTCB->tcb_flags &= ~IN_DELAY_Q;
+ DerefTCB(DelayTCB, TCBHandle);
+ CTEGetLock(&TCBDelayLock, &QHandle);
+
+ }
+
+ TCBDelayRtnCount--;
+ CTEFreeLock(&TCBDelayLock, QHandle);
+
+}
+
+//* DelayAction - Put a TCB on the queue for a delayed action.
+//
+// Called when we want to put a TCB on the DelayQ for a delayed action at
+// rcv. complete or some other time. The lock on the TCB must be held when
+// this is called.
+//
+// Input: DelayTCB - TCB which we're going to sched.
+// Action - Action we're scheduling.
+//
+// Returns: Nothing.
+//
+void
+DelayAction(TCB *DelayTCB, uint Action)
+{
+ CTELockHandle DQHandle;
+
+ // Schedule the completion.
+ CTEGetLockAtDPC(&TCBDelayLock, &DQHandle);
+ DelayTCB->tcb_flags |= Action;
+ if (!(DelayTCB->tcb_flags & IN_DELAY_Q)) {
+ DelayTCB->tcb_flags |= IN_DELAY_Q;
+ DelayTCB->tcb_refcnt++; // Reference this for later.
+ ENQUEUE(&TCBDelayQ, &DelayTCB->tcb_delayq);
+ }
+ CTEFreeLockFromDPC(&TCBDelayLock, DQHandle);
+
+}
+
+//* TCPRcvComplete - Handle a receive complete.
+//
+// Called by the lower layers when we're done receiving. We look to see if
+// we have and pending requests to complete. If we do, we complete them. Then
+// we look to see if we have any TCBs pending for output. If we do, we
+// get them going.
+//
+// Input: Nothing.
+//
+// Returns: Nothing.
+//
+void
+TCPRcvComplete(void)
+{
+ CTELockHandle CompleteHandle;
+ TCPReq *Req;
+
+ if (RequestCompleteFlags & ANY_REQUEST_COMPLETE) {
+ CTEGetLock(&RequestCompleteLock, &CompleteHandle);
+ if (!(RequestCompleteFlags & IN_RCV_COMPLETE)) {
+ RequestCompleteFlags |= IN_RCV_COMPLETE;
+ do {
+ if (RequestCompleteFlags & CONN_REQUEST_COMPLETE) {
+ if (!EMPTYQ(&ConnRequestCompleteQ)) {
+ DEQUEUE(&ConnRequestCompleteQ, Req, TCPReq, tr_q);
+ CTEStructAssert(Req, tr);
+ CTEStructAssert(*(TCPConnReq **)&Req, tcr);
+
+ CTEFreeLock(&RequestCompleteLock, CompleteHandle);
+ (*Req->tr_rtn)(Req->tr_context, Req->tr_status, 0);
+ FreeConnReq((TCPConnReq *)Req);
+ CTEGetLock(&RequestCompleteLock, &CompleteHandle);
+
+ } else
+ RequestCompleteFlags &= ~CONN_REQUEST_COMPLETE;
+ }
+
+ if (RequestCompleteFlags & SEND_REQUEST_COMPLETE) {
+ if (!EMPTYQ(&SendCompleteQ)) {
+ TCPSendReq *SendReq;
+
+ DEQUEUE(&SendCompleteQ, Req, TCPReq, tr_q);
+ CTEStructAssert(Req, tr);
+ SendReq = (TCPSendReq *)Req;
+ CTEStructAssert(SendReq, tsr);
+
+ CTEFreeLock(&RequestCompleteLock, CompleteHandle);
+ (*Req->tr_rtn)(Req->tr_context, Req->tr_status,
+ Req->tr_status == TDI_SUCCESS ? SendReq->tsr_size
+ : 0);
+ FreeSendReq((TCPSendReq *)Req);
+ CTEGetLock(&RequestCompleteLock, &CompleteHandle);
+
+ } else
+ RequestCompleteFlags &= ~SEND_REQUEST_COMPLETE;
+ }
+
+ } while (RequestCompleteFlags & ANY_REQUEST_COMPLETE);
+
+ RequestCompleteFlags &= ~IN_RCV_COMPLETE;
+ }
+ CTEFreeLock(&RequestCompleteLock, CompleteHandle);
+ }
+
+ ProcessTCBDelayQ();
+
+}
+
+//* CompleteConnReq - Complete a connection request on a TCB.
+//
+// A utility function to complete a connection request on a TCB. We remove
+// the connreq, and put it on the ConnReqCmpltQ where it will be picked
+// off later during RcvCmplt processing. We assume the TCB lock is held when
+// we're called.
+//
+// Input: CmpltTCB - TCB from which to complete.
+// OptInfo - IP OptInfo for completeion.
+// Status - Status to complete with.
+//
+// Returns: Nothing.
+//
+void
+CompleteConnReq(TCB *CmpltTCB, IPOptInfo *OptInfo, TDI_STATUS Status)
+{
+ TCPConnReq *ConnReq;
+ CTELockHandle QueueHandle;
+
+ CTEStructAssert(CmpltTCB, tcb);
+
+ ConnReq = CmpltTCB->tcb_connreq;
+ if (ConnReq != NULL) {
+
+ // There's a connreq on this TCB. Fill in the connection information
+ // before returning it.
+
+ CmpltTCB->tcb_connreq = NULL;
+ UpdateConnInfo(ConnReq->tcr_conninfo, OptInfo, CmpltTCB->tcb_daddr,
+ CmpltTCB->tcb_dport);
+
+ ConnReq->tcr_req.tr_status = Status;
+ CTEGetLockAtDPC(&RequestCompleteLock, &QueueHandle);
+ RequestCompleteFlags |= CONN_REQUEST_COMPLETE;
+ ENQUEUE(&ConnRequestCompleteQ, &ConnReq->tcr_req.tr_q);
+ CTEFreeLockFromDPC(&RequestCompleteLock, QueueHandle);
+ } else
+ DEBUGCHK;
+
+}
+
+
+#ifdef SYN_ATTACK
+void
+SynAttChk ( AddrObj *ListenAO )
+//
+// function to check whether certain thresholds relevant to containing a
+// SYN attack are being crossed.
+//
+// This function is called from FindListenConn when a connection has been
+// found to handle the SYN request
+//
+{
+ BOOLEAN RexmitCntChanged = FALSE;
+ CTELockHandle Handle;
+
+ CTEGetLockAtDPC(&SynAttLock, &Handle);
+
+ //
+ // We are putting a connection in the syn_rcvd state. Check
+ // if we have reached the threshold. If we have reduce the
+ // number of retries to a lower value.
+ //
+ if ((++TCPHalfOpen >= TCPMaxHalfOpen) && (MaxConnectResponseRexmitCountTmp == MAX_CONNECT_RESPONSE_REXMIT_CNT)) {
+ if (TCPHalfOpenRetried >= TCPMaxHalfOpenRetried) {
+ MaxConnectResponseRexmitCountTmp = ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT;
+ RexmitCntChanged = TRUE;
+ }
+ }
+
+ //
+ // if this connection limit for a port was reached earlier.
+ // Check if the lower watermark is getting hit now.
+ //
+
+ if (ListenAO->ConnLimitReached)
+ {
+ ListenAO->ConnLimitReached = FALSE;
+ if (!RexmitCntChanged && (MaxConnectResponseRexmitCountTmp == ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT)) {
+
+ CTEAssert(TCPPortsExhausted > 0);
+ //
+ // The fact that FindListenConn found a connection on the port
+ // indicates that we had a connection available. This port
+ // was therefore not exhausted of connections. Set state
+ // appropriately. If the port has no more connections now,
+ // it will get added to the Exhausted count next time a syn for
+ // the port comes along.
+ //
+ if (--TCPPortsExhausted <= TCPMaxPortsExhaustedLW) {
+ MaxConnectResponseRexmitCountTmp =
+ MAX_CONNECT_RESPONSE_REXMIT_CNT;
+ }
+ }
+ }
+
+ CTEFreeLockFromDPC(&SynAttLock, Handle);
+ return;
+}
+#endif
+
+
+//* FindListenConn - Find (or fabricate) a listening connection.
+//
+// Called by our Receive handler to decide what to do about an incoming
+// SYN. We walk down the list of connections associated with the destination
+// address, and if we find any in the listening state that can be used for
+// the incoming request we'll take them, possibly returning a listen in the
+// process. If we don't find any appropriate listening connections, we'll
+// call the Connect Event handler if one is registerd. If all else fails,
+// we'll return NULL and the SYN will be RST.
+//
+// The caller must hold the AddrObjTableLock before calling this routine,
+// and that lock must have been taken at DPC level. This routine will free
+// that lock back to DPC level.
+//
+// Input: ListenAO - Pointer to AddrObj for local address.
+// Src - Source IP address of SYN.
+// SrcPort - Source port of SYN.
+// OptInfo - IP options info from SYN.
+//
+// Returns: Pointer to found TCB, or NULL if we can't find one.
+//
+TCB *
+FindListenConn(AddrObj *ListenAO, IPAddr Src, ushort SrcPort, IPOptInfo *OptInfo)
+{
+ CTELockHandle Handle; // Lock handle on AO, TCB.
+ TCB *CurrentTCB = NULL;
+ TCPConn *CurrentConn = NULL;
+ TCPConnReq *ConnReq = NULL;
+ CTELockHandle ConnHandle;
+ Queue *Temp;
+ uint FoundConn = FALSE;
+
+ CTEStructAssert(ListenAO, ao);
+
+ CTEGetLockAtDPC(&ConnTableLock, &ConnHandle);
+ CTEGetLockAtDPC(&ListenAO->ao_lock, &Handle);
+
+#ifdef NT
+ CTEFreeLockFromDPC(&AddrObjTableLock, DISPATCH_LEVEL);
+#endif
+
+
+ // We have the lock on the AddrObj. Walk down it's list, looking
+ // for connections in the listening state.
+
+ if (AO_VALID(ListenAO)) {
+ if (ListenAO->ao_listencnt != 0) {
+ CTELockHandle TCBHandle;
+
+ Temp = QHEAD(&ListenAO->ao_listenq);
+ while (Temp != QEND(&ListenAO->ao_listenq)) {
+
+ CurrentConn = QSTRUCT(TCPConn, Temp, tc_q);
+ CTEStructAssert(CurrentConn, tc);
+
+ // If this TCB is in the listening state, with no delete
+ // pending, it's a candidate. Look at the pending listen
+ // info. to see if we should take it.
+ if ((CurrentTCB = CurrentConn->tc_tcb) != NULL) {
+
+ CTEStructAssert(CurrentTCB, tcb);
+ CTEAssert(CurrentTCB->tcb_state == TCB_LISTEN);
+
+ CTEGetLockAtDPC(&CurrentTCB->tcb_lock, &TCBHandle);
+
+ if (CurrentTCB->tcb_state == TCB_LISTEN &&
+ !PENDING_ACTION(CurrentTCB)) {
+
+ // Need to see if we can take it.
+ // See if the addresses specifed in the ConnReq
+ // match.
+ if ((IP_ADDR_EQUAL(CurrentTCB->tcb_daddr,
+ NULL_IP_ADDR) ||
+ IP_ADDR_EQUAL(CurrentTCB->tcb_daddr,
+ Src)) &&
+ (CurrentTCB->tcb_dport == 0 ||
+ CurrentTCB->tcb_dport == SrcPort)) {
+ FoundConn = TRUE;
+ break;
+ }
+
+ // Otherwise, this didn't match, so we'll check the
+ // next one.
+ }
+ CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle);
+ }
+
+ Temp = QNEXT(Temp);;
+ }
+
+ // See why we've exited the loop.
+ if (FoundConn) {
+ CTEStructAssert(CurrentTCB, tcb);
+
+ // We exited because we found a TCB. If it's pre-accepted,
+ // we're done.
+ CurrentTCB->tcb_refcnt++;
+
+ CTEAssert(CurrentTCB->tcb_connreq != NULL);
+
+ ConnReq = CurrentTCB->tcb_connreq;
+ // If QUERY_ACCEPT isn't set, turn on the CONN_ACCEPTED bit.
+ if (!(ConnReq->tcr_flags & TDI_QUERY_ACCEPT))
+ CurrentTCB->tcb_flags |= CONN_ACCEPTED;
+
+ CurrentTCB->tcb_state = TCB_SYN_RCVD;
+
+ ListenAO->ao_listencnt--;
+
+ // Since he's no longer listening, remove him from the listen
+ // queue and put him on the active queue.
+ REMOVEQ(&CurrentConn->tc_q);
+ ENQUEUE(&ListenAO->ao_activeq, &CurrentConn->tc_q);
+#ifdef SYN_ATTACK
+ if (SynAttackProtect) {
+ SynAttChk(ListenAO);
+ }
+#endif
+
+ CTEFreeLockFromDPC(&CurrentTCB->tcb_lock, TCBHandle);
+ CTEFreeLockFromDPC(&ListenAO->ao_lock, Handle);
+ CTEFreeLockFromDPC(&ConnTableLock, ConnHandle);
+ return CurrentTCB;
+ } else {
+ // Since we have a listening count, this should never happen
+ // if that count was non-zero initially.
+ CTEAssert(FALSE);
+ }
+ }
+
+ // We didn't find a matching TCB. If there's a connect indication
+ // handler, call it now to find a connection to accept on.
+
+ CTEAssert(FoundConn == FALSE);
+
+ if (ListenAO->ao_connect != NULL) {
+ uchar TAddress[TCP_TA_SIZE];
+ PVOID ConnContext;
+ PConnectEvent Event;
+ PVOID EventContext;
+ TDI_STATUS Status;
+ TCB *AcceptTCB;
+ TCPConnReq *ConnReq;
+#ifdef NT
+ ConnectEventInfo *EventInfo;
+#else
+ ConnectEventInfo EventInfo;
+#endif
+
+
+ // He has a connect handler. Put the transport address together,
+ // and call him. We also need to get the necessary resources
+ // first.
+ AcceptTCB = AllocTCB();
+ ConnReq = GetConnReq();
+
+ if (AcceptTCB != NULL && ConnReq != NULL) {
+ Event = ListenAO->ao_connect;
+ EventContext = ListenAO->ao_conncontext;
+
+ BuildTDIAddress(TAddress, Src, SrcPort);
+ REF_AO(ListenAO);
+
+ AcceptTCB->tcb_state = TCB_LISTEN;
+ AcceptTCB->tcb_connreq = ConnReq;
+ AcceptTCB->tcb_flags |= CONN_ACCEPTED;
+
+ CTEFreeLockFromDPC(&ListenAO->ao_lock, Handle);
+ CTEFreeLockFromDPC(&ConnTableLock, ConnHandle);
+
+ IF_TCPDBG(TCP_DEBUG_CONNECT) {
+ TCPTRACE(("indicating connect request\n"));
+ }
+
+ Status = (*Event)(EventContext, TCP_TA_SIZE,
+ (PTRANSPORT_ADDRESS)TAddress, 0, NULL,
+ OptInfo->ioi_optlength, OptInfo->ioi_options,
+ &ConnContext, &EventInfo);
+
+ if (Status == TDI_MORE_PROCESSING) {
+#ifdef NT
+ PIO_STACK_LOCATION IrpSp;
+ PTDI_REQUEST_KERNEL_ACCEPT AcceptRequest;
+
+ IrpSp = IoGetCurrentIrpStackLocation(EventInfo);
+
+ Status = TCPPrepareIrpForCancel(
+ (PTCP_CONTEXT) IrpSp->FileObject->FsContext,
+ EventInfo,
+ TCPCancelRequest
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ Status = TDI_NOT_ACCEPTED;
+ EventInfo = NULL;
+ goto AcceptIrpCancelled;
+ }
+
+#endif // NT
+
+ // He accepted it. Find the connection on the AddrObj.
+ CTEGetLockAtDPC(&ConnTableLock, &ConnHandle);
+ CTEGetLockAtDPC(&ListenAO->ao_lock, &Handle);
+#ifdef NT
+ {
+
+ IF_TCPDBG(TCP_DEBUG_CONNECT) {
+ TCPTRACE((
+ "connect indication accepted, queueing request\n"
+ ));
+ }
+
+ AcceptRequest = (PTDI_REQUEST_KERNEL_ACCEPT)
+ &(IrpSp->Parameters);
+ ConnReq->tcr_conninfo =
+ AcceptRequest->ReturnConnectionInformation;
+ ConnReq->tcr_req.tr_rtn = TCPRequestComplete;
+ ConnReq->tcr_req.tr_context = EventInfo;
+
+ }
+#else // NT
+ ConnReq->tcr_req.tr_rtn = EventInfo.cei_rtn;
+ ConnReq->tcr_req.tr_context = EventInfo.cei_context;
+ ConnReq->tcr_conninfo = EventInfo.cei_conninfo;
+#endif // NT
+ Temp = QHEAD(&ListenAO->ao_idleq);;
+ CurrentTCB = NULL;
+ Status = TDI_INVALID_CONNECTION;
+
+ while (Temp != QEND(&ListenAO->ao_idleq)) {
+
+ CurrentConn = QSTRUCT(TCPConn, Temp, tc_q);
+
+ CTEStructAssert(CurrentConn, tc);
+ if ((CurrentConn->tc_context == ConnContext) &&
+ !(CurrentConn->tc_flags & CONN_INVALID)) {
+
+ // We think we have a match. The connection
+ // shouldn't have a TCB associated with it. If it
+ // does, it's an error. InitTCBFromConn will
+ // handle all this.
+
+ AcceptTCB->tcb_refcnt = 1;
+#ifdef NT
+ Status = InitTCBFromConn(CurrentConn, AcceptTCB,
+ AcceptRequest->RequestConnectionInformation,
+ TRUE);
+#else // NT
+ Status = InitTCBFromConn(CurrentConn, AcceptTCB,
+ EventInfo.cei_acceptinfo,
+ TRUE);
+#endif // NT
+
+ if (Status == TDI_SUCCESS) {
+ FoundConn = TRUE;
+ AcceptTCB->tcb_state = TCB_SYN_RCVD;
+ AcceptTCB->tcb_conn = CurrentConn;
+ CurrentConn->tc_tcb = AcceptTCB;
+ CurrentConn->tc_refcnt++;
+
+ // Move him from the idle q to the active
+ // queue.
+ REMOVEQ(&CurrentConn->tc_q);
+ ENQUEUE(&ListenAO->ao_activeq, &CurrentConn->tc_q);
+ }
+
+ // In any case, we're done now.
+ break;
+
+ }
+ Temp = QNEXT(Temp);
+ }
+
+ if (!FoundConn) {
+ // Didn't find a match, or had an error. Status
+ // code is set.
+ // Complete the ConnReq and free the resources.
+ CompleteConnReq(AcceptTCB, OptInfo, Status);
+ FreeTCB(AcceptTCB);
+ AcceptTCB = NULL;
+ }
+#ifdef SYN_ATTACK
+ else {
+ if (SynAttackProtect) {
+ SynAttChk(ListenAO);
+ }
+ }
+#endif
+
+ LOCKED_DELAY_DEREF_AO(ListenAO);
+ CTEFreeLockFromDPC(&ListenAO->ao_lock, Handle);
+ CTEFreeLockFromDPC(&ConnTableLock, ConnHandle);
+
+ return AcceptTCB;
+ }
+#ifdef SYN_ATTACK
+
+ if (SynAttackProtect) {
+ CTELockHandle Handle;
+
+ //
+ // If we need to Trigger to a lower retry count
+ //
+
+ if (!ListenAO->ConnLimitReached) {
+ ListenAO->ConnLimitReached = TRUE;
+ CTEGetLockAtDPC(&SynAttLock, &Handle);
+ if ((++TCPPortsExhausted >= TCPMaxPortsExhausted) &&
+ (MaxConnectResponseRexmitCountTmp == MAX_CONNECT_RESPONSE_REXMIT_CNT)) {
+
+ MaxConnectResponseRexmitCountTmp = ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT;
+ }
+ CTEFreeLockFromDPC(&SynAttLock, Handle);
+ }
+ }
+#endif
+
+#ifdef NT
+
+AcceptIrpCancelled:
+
+#endif // NT
+ // The event handler didn't take it. Dereference it, free
+ // the resources, and return NULL.
+ FreeConnReq(ConnReq);
+ FreeTCB(AcceptTCB);
+ DELAY_DEREF_AO(ListenAO);
+ return NULL;
+
+ } else {
+ // We couldn't get a needed resource. Free any that we
+ // did get, and fall through to the 'return NULL' code.
+ if (ConnReq != NULL)
+ FreeConnReq(ConnReq);
+ if (AcceptTCB != NULL)
+ FreeTCB(AcceptTCB);
+ }
+
+ }
+#ifdef SYN_ATTACK
+ else {
+ if (SynAttackProtect) {
+ CTELockHandle Handle;
+
+ //
+ // If we need to Trigger to a lower retry count
+ //
+
+ if (!ListenAO->ConnLimitReached) {
+ ListenAO->ConnLimitReached = TRUE;
+ CTEGetLockAtDPC(&SynAttLock, &Handle);
+ if ((++TCPPortsExhausted >= TCPMaxPortsExhausted) &&
+ (MaxConnectResponseRexmitCountTmp == MAX_CONNECT_RESPONSE_REXMIT_CNT)) {
+
+ MaxConnectResponseRexmitCountTmp = ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT;
+ }
+ CTEFreeLockFromDPC(&SynAttLock, Handle);
+ }
+ }
+ }
+#endif
+
+ // No event handler, or no resource. Free the locks, and return NULL.
+ CTEFreeLockFromDPC(&ListenAO->ao_lock, Handle);
+ CTEFreeLockFromDPC(&ConnTableLock, ConnHandle);
+ return NULL;
+ }
+
+ // If we get here, the address object wasn't valid.
+ CTEFreeLockFromDPC(&ListenAO->ao_lock, Handle);
+ CTEFreeLockFromDPC(&ConnTableLock, ConnHandle);
+ return NULL;
+}
+
+
+//* FindMSS - Find the MSS option in a segment.
+//
+// Called when a SYN is received to find the MSS option in a segment. If we
+// don't find one, we assume the worst and return 536.
+//
+// Input: TCPH - TCP header to be searched.
+//
+// Returns: MSS to be used.
+//
+ushort
+FindMSS(TCPHeader UNALIGNED *TCPH)
+{
+ uint OptSize;
+ uchar *OptPtr;
+
+ OptSize = TCP_HDR_SIZE(TCPH) - sizeof(TCPHeader);
+
+ OptPtr = (uchar *)(TCPH + 1);
+
+ while (OptSize) {
+
+ if (*OptPtr == TCP_OPT_EOL)
+ break;
+
+ if (*OptPtr == TCP_OPT_NOP) {
+ OptPtr++;
+ OptSize--;
+ continue;
+ }
+
+ if (*OptPtr == TCP_OPT_MSS) {
+ if (OptPtr[1] == MSS_OPT_SIZE) {
+ ushort TempMss = *(ushort UNALIGNED *)(OptPtr + 2);
+ if (TempMss != 0)
+ return net_short(TempMss);
+ else
+ break; // MSS size of 0, use default.
+ } else
+ break; // Bad option size, use default.
+ } else {
+ // Unknown option.
+ if (OptPtr[1] == 0 || OptPtr[1] > OptSize)
+ break; // Bad option length, bail out.
+
+ OptSize -= OptPtr[1];
+ OptPtr += OptPtr[1];
+ }
+ }
+
+ return MAX_REMOTE_MSS;
+
+}
+
+//* ACKAndDrop - Acknowledge a segment, and drop it.
+//
+// Called from within the receive code when we need to drop a segment that's
+// outside the receive window.
+//
+// Input: RI - Receive info for incoming segment.
+// RcvTCB - TCB for incoming segment.
+//
+// Returns: Nothing.
+//
+void
+ACKAndDrop(TCPRcvInfo *RI, TCB *RcvTCB)
+{
+ CTELockHandle Handle;
+
+#ifdef VXD
+#ifdef DEBUG
+ Handle = DEFAULT_SIMIRQL;
+#endif
+#else
+ Handle = DISPATCH_LEVEL;
+#endif
+
+ if (!(RI->tri_flags & TCP_FLAG_RST)) {
+
+ if (RcvTCB->tcb_state == TCB_TIME_WAIT)
+ START_TCB_TIMER(RcvTCB->tcb_rexmittimer, MAX_REXMIT_TO);
+
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, Handle);
+
+ SendACK(RcvTCB);
+
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, &Handle);
+ }
+ DerefTCB(RcvTCB, Handle);
+
+}
+
+//* ACKData - Acknowledge data.
+//
+// Called from the receive handler to acknowledge data. We're given the
+// TCB and the new value of senduna. We walk down the send q. pulling
+// off sends and putting them on the complete q until we hit the end
+// or we acknowledge the specified number of bytes of data.
+//
+// NOTE: We manipulate the send refcnt and acked flag without taking a lock.
+// This is OK in the VxD version where locks don't mean anything anyway, but
+// in the port to NT we'll need to add locking. The lock will have to be
+// taken in the transmit complete routine. We can't use a lock in the TCB,
+// since the TCB could go away before the transmit complete happens, and a lock
+// in the TSR would be overkill, so it's probably best to use a global lock
+// for this. If that causes too much contention, we could use a set of locks
+// and pass a pointer to the appropriate lock back as part of the transmit
+// confirm context. This lock pointer would also need to be stored in the
+// TCB.
+//
+// Input: ACKTcb - TCB from which to pull data.
+// SendUNA - New value of send una.
+//
+// Returns: Nothing.
+//
+void
+ACKData(TCB *ACKTcb, SeqNum SendUNA)
+{
+ Queue *End, *Current; // End and current elements.
+ Queue *TempQ, *EndQ;
+ Queue *LastCmplt; // Last one we completed.
+ TCPSendReq *CurrentTSR; // Current send req we're
+ // looking at.
+ PNDIS_BUFFER CurrentBuffer; // Current NDIS_BUFFER.
+ uint Updated = FALSE;
+ uint BufLength;
+ int Amount, OrigAmount;
+ long Result;
+ CTELockHandle Handle;
+ uint Temp;
+
+ CTEStructAssert(ACKTcb, tcb);
+
+ CheckTCBSends(ACKTcb);
+
+ Amount = SendUNA - ACKTcb->tcb_senduna;
+ CTEAssert(Amount > 0);
+
+ // Do a quick check to see if this acks everything that we have. If it does,
+ // handle it right away. We can only do this in the ESTABLISHED state,
+ // because we blindly update sendnext, and that can only work if we
+ // haven't sent a FIN.
+ if ((Amount == (int) ACKTcb->tcb_unacked) && ACKTcb->tcb_state == TCB_ESTAB) {
+
+ // Everything is acked.
+ CTEAssert(!EMPTYQ(&ACKTcb->tcb_sendq));
+
+ TempQ = ACKTcb->tcb_sendq.q_next;
+
+ INITQ(&ACKTcb->tcb_sendq);
+
+ ACKTcb->tcb_sendnext = SendUNA;
+ ACKTcb->tcb_senduna = SendUNA;
+
+ CTEAssert(ACKTcb->tcb_sendnext == ACKTcb->tcb_sendmax);
+ ACKTcb->tcb_cursend = NULL;
+ ACKTcb->tcb_sendbuf = NULL;
+ ACKTcb->tcb_sendofs = 0;
+ ACKTcb->tcb_sendsize = 0;
+ ACKTcb->tcb_unacked = 0;
+
+ // Now walk down the list of send requests. If the reference count
+ // has gone to 0, put it on the send complete queue.
+ CTEGetLock(&RequestCompleteLock, &Handle);
+ EndQ = &ACKTcb->tcb_sendq;
+ do {
+ CurrentTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, TempQ, tr_q),
+ tsr_req);
+
+ CTEStructAssert(CurrentTSR, tsr);
+
+ TempQ = CurrentTSR->tsr_req.tr_q.q_next;
+
+ CurrentTSR->tsr_req.tr_status = TDI_SUCCESS;
+ Result = CTEInterlockedDecrementLong(&CurrentTSR->tsr_refcnt);
+
+ CTEAssert(Result >= 0);
+
+
+ if (Result <= 0) {
+ // No more references are outstanding, the send can be
+ // completed.
+
+ // If we've sent directly from this send, NULL out the next
+ // pointer for the last buffer in the chain.
+ if (CurrentTSR->tsr_lastbuf != NULL) {
+ NDIS_BUFFER_LINKAGE(CurrentTSR->tsr_lastbuf) = NULL;
+ CurrentTSR->tsr_lastbuf = NULL;
+ }
+ ACKTcb->tcb_totaltime += (TCPTime - CurrentTSR->tsr_time);
+ Temp = ACKTcb->tcb_bcountlow;
+ ACKTcb->tcb_bcountlow += CurrentTSR->tsr_size;
+ ACKTcb->tcb_bcounthi += (Temp > ACKTcb->tcb_bcountlow ? 1 : 0);
+
+ ENQUEUE(&SendCompleteQ, &CurrentTSR->tsr_req.tr_q);
+ }
+
+ } while (TempQ != EndQ);
+
+ RequestCompleteFlags |= SEND_REQUEST_COMPLETE;
+ CTEFreeLock(&RequestCompleteLock, Handle);
+
+ CheckTCBSends(ACKTcb);
+ return;
+ }
+
+ OrigAmount = Amount;
+ End = QEND(&ACKTcb->tcb_sendq);
+ Current = QHEAD(&ACKTcb->tcb_sendq);
+
+ LastCmplt = NULL;
+
+ while (Amount > 0 && Current != End) {
+ CurrentTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, Current, tr_q),
+ tsr_req);
+ CTEStructAssert(CurrentTSR, tsr);
+
+
+ if (Amount >= (int) CurrentTSR->tsr_unasize) {
+ // This is completely acked. Just advance to the next one.
+ Amount -= CurrentTSR->tsr_unasize;
+
+ LastCmplt = Current;
+
+ Current = QNEXT(Current);
+ continue;
+ }
+
+ // This one is only partially acked. Update his offset and NDIS buffer
+ // pointer, and break out. We know that Amount is < the unacked size
+ // in this buffer, we we can walk the NDIS buffer chain without fear
+ // of falling off the end.
+ CurrentBuffer = CurrentTSR->tsr_buffer;
+ CTEAssert(CurrentBuffer != NULL);
+ CTEAssert(Amount < (int) CurrentTSR->tsr_unasize);
+ CurrentTSR->tsr_unasize -= Amount;
+
+ BufLength = NdisBufferLength(CurrentBuffer) - CurrentTSR->tsr_offset;
+
+ if (Amount >= (int) BufLength) {
+ do {
+ Amount -= BufLength;
+ CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
+ CTEAssert(CurrentBuffer != NULL);
+ BufLength = NdisBufferLength(CurrentBuffer);
+ } while (Amount >= (int) BufLength);
+
+ CurrentTSR->tsr_offset = Amount;
+ CurrentTSR->tsr_buffer = CurrentBuffer;
+
+ } else
+ CurrentTSR->tsr_offset += Amount;
+
+ Amount = 0;
+
+ break;
+ }
+
+#ifdef DEBUG
+ // We should always be able to remove at least Amount bytes, except in
+ // the case where a FIN has been sent. In that case we should be off
+ // by exactly one. In the debug builds we'll check this.
+ if (Amount != 0 && (!(ACKTcb->tcb_flags & FIN_SENT) || Amount != 1))
+ DEBUGCHK;
+#endif
+
+ if (SEQ_GT(SendUNA, ACKTcb->tcb_sendnext)) {
+
+ if (Current != End) {
+ // Need to reevaluate CurrentTSR, in case we bailed out of the
+ // above loop after updating Current but before updating
+ // CurrentTSR.
+ CurrentTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, Current, tr_q),
+ tsr_req);
+ CTEStructAssert(CurrentTSR, tsr);
+ ACKTcb->tcb_cursend = CurrentTSR;
+ ACKTcb->tcb_sendbuf = CurrentTSR->tsr_buffer;
+ ACKTcb->tcb_sendofs = CurrentTSR->tsr_offset;
+ ACKTcb->tcb_sendsize = CurrentTSR->tsr_unasize;
+ } else {
+ ACKTcb->tcb_cursend = NULL;
+ ACKTcb->tcb_sendbuf = NULL;
+ ACKTcb->tcb_sendofs = 0;
+ ACKTcb->tcb_sendsize = 0;
+ }
+
+ ACKTcb->tcb_sendnext = SendUNA;
+ }
+
+ // Now update tcb_unacked with the amount we tried to ack minus the
+ // amount we didn't ack (Amount should be 0 or 1 here).
+ CTEAssert(Amount == 0 || Amount == 1);
+
+ ACKTcb->tcb_unacked -= OrigAmount - Amount;
+ CTEAssert(*(int *)&ACKTcb->tcb_unacked >= 0);
+
+ ACKTcb->tcb_senduna = SendUNA;
+
+ // If we've acked any here, LastCmplt will be non-null, and Current will
+ // point to the send that should be at the start of the queue. Splice
+ // out the completed ones and put them on the end of the send completed
+ // queue, and update the TCB send q.
+ if (LastCmplt != NULL) {
+ Queue *FirstCmplt;
+ TCPSendReq *FirstTSR, *EndTSR;
+
+ CTEAssert(!EMPTYQ(&ACKTcb->tcb_sendq));
+
+ FirstCmplt = QHEAD(&ACKTcb->tcb_sendq);
+
+ // If we've acked everything, just reinit the queue.
+ if (Current == End) {
+ INITQ(&ACKTcb->tcb_sendq);
+ } else {
+ // There's still something on the queue. Just update it.
+ ACKTcb->tcb_sendq.q_next = Current;
+ Current->q_prev = &ACKTcb->tcb_sendq;
+ }
+
+ CheckTCBSends(ACKTcb);
+
+ // Now walk down the lists of things acked. If the refcnt on the send
+ // is 0, go ahead and put him on the send complete Q. Otherwise set
+ // the ACKed bit in the send, and he'll be completed when the count
+ // goes to 0 in the transmit confirm.
+ //
+ // Note that we haven't done any locking here. This will probably
+ // need to change in the port to NT.
+
+ // Set FirstTSR to the first TSR we'll complete, and EndTSR to be
+ // the first TSR that isn't completed.
+
+ FirstTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, FirstCmplt, tr_q),
+ tsr_req);
+ EndTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, Current, tr_q),
+ tsr_req);
+
+ CTEStructAssert(FirstTSR, tsr);
+ CTEAssert(FirstTSR != EndTSR);
+
+ // Now walk the list of ACKed TSRs. If we can complete one, put him
+ // on the complete queue.
+ CTEGetLockAtDPC(&RequestCompleteLock, &Handle);
+ while (FirstTSR != EndTSR) {
+
+
+ TempQ = QNEXT(&FirstTSR->tsr_req.tr_q);
+
+ CTEStructAssert(FirstTSR, tsr);
+ FirstTSR->tsr_req.tr_status = TDI_SUCCESS;
+
+ // The tsr_lastbuf->Next field is zapped to 0 when the tsr_refcnt
+ // goes to 0, so we don't need to do it here.
+
+ // Decrement the reference put on the send buffer when it was
+ // initialized indicating the send has been acknowledged.
+ Result = CTEInterlockedDecrementLong(&(FirstTSR->tsr_refcnt));
+
+ CTEAssert(Result >= 0);
+ if (Result <= 0) {
+ // No more references are outstanding, the send can be
+ // completed.
+
+ // If we've sent directly from this send, NULL out the next
+ // pointer for the last buffer in the chain.
+ if (FirstTSR->tsr_lastbuf != NULL) {
+ NDIS_BUFFER_LINKAGE(FirstTSR->tsr_lastbuf) = NULL;
+ FirstTSR->tsr_lastbuf = NULL;
+ }
+
+ ACKTcb->tcb_totaltime += (TCPTime - CurrentTSR->tsr_time);
+ Temp = ACKTcb->tcb_bcountlow;
+ ACKTcb->tcb_bcountlow += CurrentTSR->tsr_size;
+ ACKTcb->tcb_bcounthi += (Temp > ACKTcb->tcb_bcountlow ? 1 : 0);
+ ENQUEUE(&SendCompleteQ, &FirstTSR->tsr_req.tr_q);
+ }
+
+ FirstTSR = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, TempQ, tr_q),
+ tsr_req);
+ }
+ RequestCompleteFlags |= SEND_REQUEST_COMPLETE;
+ CTEFreeLockFromDPC(&RequestCompleteLock, Handle);
+ }
+
+}
+
+//* TrimRcvBuf - Trim the front edge of a receive buffer.
+//
+// A utility routine to trim the front of a receive buffer. We take in a
+// a count (which may be 0) and adjust the pointer in the first buffer in
+// the chain by that much. If there isn't that much in the first buffer,
+// we move onto the next one. If we run out of buffers we'll return a pointer
+// to the last buffer in the chain, with a size of 0. It's the caller's
+// responsibility to catch this.
+//
+// Input: RcvBuf - Buffer to be trimmed.
+// Count - Amount to be trimmed.
+//
+// Returns: A pointer to the new start, or NULL.
+//
+IPRcvBuf *
+TrimRcvBuf(IPRcvBuf *RcvBuf, uint Count)
+{
+ uint TrimThisTime;
+
+ CTEAssert(RcvBuf != NULL);
+
+ while (Count) {
+ CTEAssert(RcvBuf != NULL);
+
+ TrimThisTime = MIN(Count, RcvBuf->ipr_size);
+ Count -= TrimThisTime;
+ RcvBuf->ipr_buffer += TrimThisTime;
+ if ((RcvBuf->ipr_size -= TrimThisTime) == 0) {
+ if (RcvBuf->ipr_next != NULL)
+ RcvBuf = RcvBuf->ipr_next;
+ else {
+ // Ran out of buffers. Just return this one.
+ break;
+ }
+ }
+ }
+
+ return RcvBuf;
+
+}
+
+//* FreeRBChain - Free an RB chain.
+//
+// Called to free a chain of RBs. If we're the owner of each RB, we'll
+// free it.
+//
+// Input: RBChain - RBChain to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeRBChain(IPRcvBuf *RBChain)
+{
+ while (RBChain != NULL) {
+
+ if (RBChain->ipr_owner == IPR_OWNER_TCP) {
+ IPRcvBuf *Temp;
+
+ Temp = RBChain->ipr_next;
+ CTEFreeMem(RBChain);
+ RBChain = Temp;
+ } else
+ RBChain = RBChain->ipr_next;
+ }
+
+}
+
+IPRcvBuf DummyBuf;
+
+//* PullFromRAQ - Pull segments from the reassembly queue.
+//
+// Called when we've received frames out of order, and have some segments
+// on the reassembly queue. We'll walk down the reassembly list, segments that
+// are overlapped by the current rcv. next variable. When we get
+// to one that doesn't completely overlap we'll trim it to fit the next
+// rcv. seq. number, and pull it from the queue.
+//
+// Input: RcvTCB - TCB to pull from.
+// RcvInfo - Pointer to TCPRcvInfo structure for current seg.
+// Size - Pointer to size for current segment. We'll update
+// this when we're done.
+//
+// Returns: Nothing.
+//
+IPRcvBuf *
+PullFromRAQ(TCB *RcvTCB, TCPRcvInfo *RcvInfo, uint *Size)
+{
+ TCPRAHdr *CurrentTRH; // Current TCP RA Header being examined.
+ TCPRAHdr *TempTRH; // Temporary variable.
+ SeqNum NextSeq; // Next sequence number we want.
+ IPRcvBuf *NewBuf;
+ SeqNum NextTRHSeq; // Seq. number immediately after
+ // current TRH.
+ int Overlap; // Overlap between current TRH and
+ // NextSeq.
+
+ CTEStructAssert(RcvTCB, tcb);
+
+ CurrentTRH = RcvTCB->tcb_raq;
+ NextSeq = RcvTCB->tcb_rcvnext;
+
+ while (CurrentTRH != NULL) {
+ CTEStructAssert(CurrentTRH, trh);
+ CTEAssert(!(CurrentTRH->trh_flags & TCP_FLAG_SYN));
+
+ // If the flags for the current reassembly segment contains a FIN,
+ // it should be the last segment on the queue. This assert checks
+ // that.
+ CTEAssert(!(CurrentTRH->trh_flags & TCP_FLAG_FIN) ||
+ CurrentTRH->trh_next == NULL);
+
+ if (SEQ_LT(NextSeq, CurrentTRH->trh_start)) {
+#ifdef DEBUG
+ *Size = 0;
+#endif
+ return NULL; // The next TRH starts too far down.
+ }
+
+
+ NextTRHSeq = CurrentTRH->trh_start + CurrentTRH->trh_size +
+ ((CurrentTRH->trh_flags & TCP_FLAG_FIN) ? 1 : 0);
+
+ if (SEQ_GTE(NextSeq, NextTRHSeq)) {
+ // The current TRH is overlapped completely. Free it and continue.
+ FreeRBChain(CurrentTRH->trh_buffer);
+ TempTRH = CurrentTRH->trh_next;
+ CTEFreeMem(CurrentTRH);
+ CurrentTRH = TempTRH;
+ RcvTCB->tcb_raq = TempTRH;
+ if (TempTRH == NULL) {
+ // We've just cleaned off the RAQ. We can go back on the
+ // fast path now.
+ if (--(RcvTCB->tcb_slowcount) == 0) {
+ RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+ }
+ break;
+ }
+ } else {
+ Overlap = NextSeq - CurrentTRH->trh_start;
+ RcvInfo->tri_seq = NextSeq;
+ RcvInfo->tri_flags = CurrentTRH->trh_flags;
+ RcvInfo->tri_urgent = CurrentTRH->trh_urg;
+
+ if (Overlap != (int) CurrentTRH->trh_size) {
+ NewBuf = FreePartialRB(CurrentTRH->trh_buffer, Overlap);
+ *Size = CurrentTRH->trh_size - Overlap;
+ } else {
+ // This completely overlaps the data in this segment, but the
+ // sequence number doesn't overlap completely. There must
+ // be a FIN in the TRH. If we called FreePartialRB with this
+ // we'd end up returning NULL, which is the signal for failure.
+ // Instead we'll just return some bogus value that nobody
+ // will look at with a size of 0.
+ FreeRBChain(CurrentTRH->trh_buffer);
+ CTEAssert(CurrentTRH->trh_flags & TCP_FLAG_FIN);
+ NewBuf = &DummyBuf;
+ *Size = 0;
+ }
+
+ RcvTCB->tcb_raq = CurrentTRH->trh_next;
+ if (RcvTCB->tcb_raq == NULL) {
+ // We've just cleaned off the RAQ. We can go back on the
+ // fast path now.
+ if (--(RcvTCB->tcb_slowcount) == 0) {
+ RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+ }
+
+ }
+ CTEFreeMem(CurrentTRH);
+ return NewBuf;
+ }
+
+
+ }
+
+#ifdef DEBUG
+ *Size = 0;
+#endif
+ return NULL;
+
+}
+
+//* CreateTRH - Create a TCP reassembly header.
+//
+// This function tries to create a TCP reassembly header. We take as input
+// a pointer to the previous TRH in the chain, the RcvBuffer to put on,
+// etc. and try to create and link in a TRH. The caller must hold the lock
+// on the TCB when this is called.
+//
+// Input: PrevTRH - Pointer to TRH to insert after.
+// RcvBuf - Pointer to IP RcvBuf chain.
+// RcvInfo - Pointer to RcvInfo for this TRH.
+// Size - Size in bytes of data.
+//
+// Returns: TRUE if we created it, FALSE otherwise.
+//
+uint
+CreateTRH(TCPRAHdr *PrevTRH, IPRcvBuf *RcvBuf, TCPRcvInfo *RcvInfo, int Size)
+{
+ TCPRAHdr *NewTRH;
+ IPRcvBuf *NewRcvBuf;
+
+ CTEAssert((Size > 0) || (RcvInfo->tri_flags & TCP_FLAG_FIN));
+
+ NewTRH = CTEAllocMem(sizeof(TCPRAHdr));
+ if (NewTRH == NULL)
+ return FALSE;
+
+ NewRcvBuf = CTEAllocMem(sizeof(IPRcvBuf) + Size);
+ if (NewRcvBuf == NULL) {
+ CTEFreeMem(NewTRH);
+ return FALSE;
+ }
+
+#ifdef DEBUG
+ NewTRH->trh_sig = trh_signature;
+#endif
+ NewRcvBuf->ipr_owner = IPR_OWNER_TCP;
+ NewRcvBuf->ipr_size = (uint)Size;
+ NewRcvBuf->ipr_next = NULL;
+ NewRcvBuf->ipr_buffer = (uchar *)(NewRcvBuf + 1);
+ if (Size != 0)
+ CopyRcvToBuffer(NewRcvBuf->ipr_buffer, RcvBuf, Size, 0);
+
+ NewTRH->trh_start = RcvInfo->tri_seq;
+ NewTRH->trh_flags = RcvInfo->tri_flags;
+ NewTRH->trh_size = Size;
+ NewTRH->trh_urg = RcvInfo->tri_urgent;
+ NewTRH->trh_buffer = NewRcvBuf;
+ NewTRH->trh_end = NewRcvBuf;
+
+ NewTRH->trh_next = PrevTRH->trh_next;
+ PrevTRH->trh_next = NewTRH;
+ return TRUE;
+
+}
+
+//* PutOnRAQ - Put a segment on the reassembly queue.
+//
+// Called during segment reception to put a segment on the reassembly
+// queue. We try to use as few reassembly headers as possible, so if this
+// segment has some overlap with an existing entry in the queue we'll just
+// update the existing entry. If there is no overlap we'll create a new
+// reassembly header. Combining URGENT data with non-URGENT data is tricky.
+// If we get a segment that has urgent data that overlaps the front of a
+// reassembly header we'll always mark the whole chunk as urgent - the value
+// of the urgent pointer will mark the end of urgent data, so this is OK. If it
+// only overlaps at the end, however, we won't combine, since we would have to
+// mark previously non-urgent data as urgent. We'll trim the
+// front of the incoming segment and create a new reassembly header. Also,
+// if we have non-urgent data that overlaps at the front of a reassembly
+// header containing urgent data we can't combine these two, since again we
+// would mark non-urgent data as urgent.
+// Our search will stop if we find an entry with a FIN.
+// We assume that the TCB lock is held by the caller.
+//
+// Entry: RcvTCB - TCB on which to reassemble.
+// RcvInfo - Pointer to RcvInfo for new segment.
+// RcvBuf - IP RcvBuf chain for this segment.
+// Size - Size in bytes of data in this segment.
+//
+// Returns: Nothing.
+//
+void
+PutOnRAQ(TCB *RcvTCB, TCPRcvInfo *RcvInfo, IPRcvBuf *RcvBuf, uint Size)
+{
+ TCPRAHdr *PrevTRH, *CurrentTRH; // Prev. and current TRH
+ // pointers.
+ SeqNum NextSeq; // Seq. number of first byte
+ // after segment being
+ // reassembled.
+ SeqNum NextTRHSeq; // Seq. number of first byte
+ // after current TRH.
+ uint Created;
+
+ CTEStructAssert(RcvTCB, tcb);
+ CTEAssert(RcvTCB->tcb_rcvnext != RcvInfo->tri_seq);
+ CTEAssert(!(RcvInfo->tri_flags & TCP_FLAG_SYN));
+ NextSeq = RcvInfo->tri_seq + Size +
+ ((RcvInfo->tri_flags & TCP_FLAG_FIN) ? 1 : 0);
+
+ PrevTRH = STRUCT_OF(TCPRAHdr, &RcvTCB->tcb_raq, trh_next);
+ CurrentTRH = PrevTRH->trh_next;
+
+ // Walk down the reassembly queue, looking for the correct place to
+ // insert this, until we hit the end.
+ while (CurrentTRH != NULL) {
+ CTEStructAssert(CurrentTRH, trh);
+
+ CTEAssert(!(CurrentTRH->trh_flags & TCP_FLAG_SYN));
+ NextTRHSeq = CurrentTRH->trh_start + CurrentTRH->trh_size +
+ ((CurrentTRH->trh_flags & TCP_FLAG_FIN) ? 1 : 0);
+
+ // First, see if it starts beyond the end of the current TRH.
+ if (SEQ_LTE(RcvInfo->tri_seq, NextTRHSeq)) {
+ // We know the incoming segment doesn't start beyond the end
+ // of this TRH, so we'll either create a new TRH in front of
+ // this one or we'll merge the new segment onto this TRH.
+ // If the end of the current segment is in front of the start
+ // of the current TRH, we'll need to create a new TRH. Otherwise
+ // we'll merge these two.
+ if (SEQ_LT(NextSeq, CurrentTRH->trh_start))
+ break;
+ else {
+ // There's some overlap. If there's actually data in the
+ // incoming segment we'll merge it.
+ if (Size != 0) {
+ int FrontOverlap, BackOverlap;
+ IPRcvBuf *NewRB;
+
+ // We need to merge. If there's a FIN on the incoming
+ // segment that would fall inside this current TRH, we
+ // have a protocol violation from the remote peer. In this
+ // case just return, discarding the incoming segment.
+ if ((RcvInfo->tri_flags & TCP_FLAG_FIN) &&
+ SEQ_LTE(NextSeq, NextTRHSeq))
+ return;
+
+ // We have some overlap. Figure out how much.
+ FrontOverlap = CurrentTRH->trh_start - RcvInfo->tri_seq;
+ if (FrontOverlap > 0) {
+ // Have overlap in front. Allocate an IPRcvBuf to
+ // to hold it, and copy it, unless we would have to
+ // combine non-urgent with urgent.
+ if (!(RcvInfo->tri_flags & TCP_FLAG_URG) &&
+ (CurrentTRH->trh_flags & TCP_FLAG_URG)) {
+ if (CreateTRH(PrevTRH, RcvBuf, RcvInfo,
+ CurrentTRH->trh_start - RcvInfo->tri_seq)) {
+ PrevTRH = PrevTRH->trh_next;
+ CurrentTRH = PrevTRH->trh_next;
+ }
+ FrontOverlap = 0;
+
+ } else {
+ NewRB = CTEAllocMem(sizeof(IPRcvBuf) + FrontOverlap);
+ if (NewRB == NULL)
+ return; // Couldn't get the buffer.
+
+ NewRB->ipr_owner = IPR_OWNER_TCP;
+ NewRB->ipr_size = FrontOverlap;
+ NewRB->ipr_buffer = (uchar *)(NewRB + 1);
+ CopyRcvToBuffer(NewRB->ipr_buffer, RcvBuf,
+ FrontOverlap, 0);
+ CurrentTRH->trh_size += FrontOverlap;
+ NewRB->ipr_next = CurrentTRH->trh_buffer;
+ CurrentTRH->trh_buffer = NewRB;
+ CurrentTRH->trh_start = RcvInfo->tri_seq;
+ }
+ }
+
+ // We've updated the starting sequence number of this TRH
+ // if we needed to. Now look for back overlap. There can't
+ // be any back overlap if the current TRH has a FIN. Also
+ // we'll need to check for urgent data if there is back
+ // overlap.
+ if (!(CurrentTRH->trh_flags & TCP_FLAG_FIN)) {
+ BackOverlap = RcvInfo->tri_seq + Size - NextTRHSeq;
+ if ((BackOverlap > 0) &&
+ (RcvInfo->tri_flags & TCP_FLAG_URG) &&
+ !(CurrentTRH->trh_flags & TCP_FLAG_URG) &&
+ (FrontOverlap <= 0)) {
+ int AmountToTrim;
+ // The incoming segment has urgent data and overlaps
+ // on the back but not the front, and the current
+ // TRH has no urgent data. We can't combine into
+ // this TRH, so trim the front of the incoming
+ // segment to NextTRHSeq and move to the next
+ // TRH.
+ AmountToTrim = NextTRHSeq - RcvInfo->tri_seq;
+ CTEAssert(AmountToTrim >= 0);
+ CTEAssert(AmountToTrim < (int) Size);
+ RcvBuf = FreePartialRB(RcvBuf, (uint)AmountToTrim);
+ RcvInfo->tri_seq += AmountToTrim;
+ RcvInfo->tri_urgent -= AmountToTrim;
+ PrevTRH = CurrentTRH;
+ CurrentTRH = PrevTRH->trh_next;
+ continue;
+ }
+
+ } else
+ BackOverlap = 0;
+
+ // Now if we have back overlap, copy it.
+ if (BackOverlap > 0) {
+ // We have back overlap. Get a buffer to copy it into.
+ // If we can't get one, we won't just return, because
+ // we may have updated the front and may need to
+ // update the urgent info.
+ NewRB = CTEAllocMem(sizeof(IPRcvBuf) + BackOverlap);
+ if (NewRB != NULL) {
+ // Got the buffer.
+ NewRB->ipr_owner = IPR_OWNER_TCP;
+ NewRB->ipr_size = BackOverlap;
+ NewRB->ipr_buffer = (uchar *)(NewRB + 1);
+ CopyRcvToBuffer(NewRB->ipr_buffer, RcvBuf,
+ BackOverlap, NextTRHSeq - RcvInfo->tri_seq);
+ CurrentTRH->trh_size += BackOverlap;
+ NewRB->ipr_next = CurrentTRH->trh_end->ipr_next;
+ CurrentTRH->trh_end->ipr_next = NewRB;
+ CurrentTRH->trh_end = NewRB;
+ }
+ }
+
+ // Everything should be consistent now. If there's an
+ // urgent data pointer in the incoming segment, update the
+ // one in the TRH now.
+ if (RcvInfo->tri_flags & TCP_FLAG_URG) {
+ SeqNum UrgSeq;
+ // Have an urgent pointer. If the current TRH already
+ // has an urgent pointer, see which is bigger. Otherwise
+ // just use this one.
+ UrgSeq = RcvInfo->tri_seq + RcvInfo->tri_urgent;
+ if (CurrentTRH->trh_flags & TCP_FLAG_URG) {
+ SeqNum TRHUrgSeq;
+
+ TRHUrgSeq = CurrentTRH->trh_start +
+ CurrentTRH->trh_urg;
+ if (SEQ_LT(UrgSeq, TRHUrgSeq))
+ UrgSeq = TRHUrgSeq;
+ } else
+ CurrentTRH->trh_flags |= TCP_FLAG_URG;
+
+ CurrentTRH->trh_urg = UrgSeq - CurrentTRH->trh_start;
+ }
+
+ } else {
+ // We have a 0 length segment. The only interesting thing
+ // here is if there's a FIN on the segment. If there is,
+ // and the seq. # of the incoming segment is exactly after
+ // the current TRH, OR matches the FIN in the current TRH,
+ // we note it.
+ if (RcvInfo->tri_flags & TCP_FLAG_FIN) {
+ if (!(CurrentTRH->trh_flags & TCP_FLAG_FIN)) {
+ if (SEQ_EQ(NextTRHSeq, RcvInfo->tri_seq))
+ CurrentTRH->trh_flags |= TCP_FLAG_FIN;
+ else
+ DEBUGCHK;
+ }
+ else {
+ if ( !(SEQ_EQ((NextTRHSeq-1), RcvInfo->tri_seq)) ) {
+ DEBUGCHK;
+ }
+ }
+ }
+ }
+ return;
+ }
+ } else {
+ // Look at the next TRH, unless the current TRH has a FIN. If he
+ // has a FIN, we won't save any data beyond that anyway.
+ if (CurrentTRH->trh_flags & TCP_FLAG_FIN)
+ return;
+
+ PrevTRH = CurrentTRH;
+ CurrentTRH = PrevTRH->trh_next;
+ }
+ }
+
+ // When we get here, we need to create a new TRH. If we create one and
+ // there was previously nothing on the reassembly queue, we'll have to
+ // move off the fast receive path.
+
+ CurrentTRH = RcvTCB->tcb_raq;
+ Created = CreateTRH(PrevTRH, RcvBuf, RcvInfo, (int)Size);
+
+ if (Created && CurrentTRH == NULL) {
+ RcvTCB->tcb_slowcount++;
+ RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+ }
+
+
+}
+
+
+//* TCPRcv - Receive a TCP segment.
+//
+// This is the routine called by IP when we need to receive a TCP segment.
+// In general, we follow the RFC 793 event processing section pretty closely,
+// but there is a 'fast path' where we make some quick checks on the incoming
+// segment, and if it matches we deliver it immediately.
+//
+// Entry: IPContext - IPContext identifying physical i/f that
+// received the data.
+// Dest - IPAddr of destionation.
+// Src - IPAddr of source.
+// LocalAddr - Local address of network which caused this to be
+// received.
+// SrcAddr - Address of local interface which received the packet
+// IPH - IP Header.
+// IPHLength - Bytes in IPH.
+// RcvBuf - Pointer to receive buffer chain containing data.
+// Size - Size in bytes of data received.
+// IsBCast - Boolean indicator of whether or not this came in as
+// a bcast.
+// Protocol - Protocol this came in on - should be TCP.
+// OptInfo - Pointer to info structure for received options.
+//
+// Returns: Status of reception. Anything other than IP_SUCCESS will cause
+// IP to send a 'port unreachable' message.
+//
+IP_STATUS
+TCPRcv(void *IPContext, IPAddr Dest, IPAddr Src, IPAddr LocalAddr,
+ IPAddr SrcAddr, IPHeader UNALIGNED *IPH, uint IPHLength, IPRcvBuf *RcvBuf,
+ uint Size, uchar IsBCast, uchar Protocol, IPOptInfo *OptInfo)
+{
+ TCPHeader UNALIGNED *TCPH; // The TCP header.
+ TCB *RcvTCB; // TCB on which to receive the packet.
+ CTELockHandle TableHandle, TCBHandle;
+ TCPRcvInfo RcvInfo; // Local swapped copy of rcv info.
+ uint DataOffset; // Offset from start of header to data.
+ uint Actions;
+ uint BytesTaken;
+ uint NewSize;
+
+ CheckRBList(RcvBuf, Size);
+
+ TStats.ts_insegs++;
+
+ // Checksum it, to make sure it's valid.
+ TCPH = (TCPHeader *)RcvBuf->ipr_buffer;
+
+ if (!IsBCast) {
+
+ if (Size >= sizeof(TCPHeader) && XsumRcvBuf(PHXSUM(Src, Dest, PROTOCOL_TCP,
+ Size), RcvBuf) == 0xffff) {
+
+ // The packet is valid. Get the info we need and byte swap it,
+ // and then try to find a matching TCB.
+
+ RcvInfo.tri_seq = net_long(TCPH->tcp_seq);
+ RcvInfo.tri_ack = net_long(TCPH->tcp_ack);
+ RcvInfo.tri_window = (uint)net_short(TCPH->tcp_window);
+ RcvInfo.tri_urgent = (uint)net_short(TCPH->tcp_urgent);
+ RcvInfo.tri_flags = (uint)TCPH->tcp_flags;
+ DataOffset = TCP_HDR_SIZE(TCPH);
+
+ if (DataOffset <= Size) {
+
+ Size -= DataOffset;
+ CTEAssert(DataOffset <= RcvBuf->ipr_size);
+ RcvBuf->ipr_size -= DataOffset;
+ RcvBuf->ipr_buffer += DataOffset;
+
+ CTEGetLockAtDPC(&TCBTableLock, &TableHandle);
+ RcvTCB = FindTCB(Dest, Src, TCPH->tcp_src, TCPH->tcp_dest);
+ if (RcvTCB != NULL) {
+ // Found one. Get the lock on it, and continue.
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TCBHandle);
+ CTEFreeLockFromDPC(&TCBTableLock, TCBHandle);
+ } else {
+ uchar DType;
+
+ // Didn't find a matching TCB. If this segment carries a SYN,
+ // find a matching address object and see it it has a listen
+ // indication. If it does, call it. Otherwise send a RST
+ // back to the sender.
+ CTEFreeLockFromDPC(&TCBTableLock, TableHandle);
+
+
+ // Make sure that the source address isn't a broadcast
+ // before proceeding.
+ if ((*LocalNetInfo.ipi_invalidsrc)(Src))
+ return IP_SUCCESS;
+
+ // If it doesn't have a SYN (and only a SYN), we'll send a
+ // reset.
+ if ((RcvInfo.tri_flags & (TCP_FLAG_SYN | TCP_FLAG_ACK | TCP_FLAG_RST)) ==
+ TCP_FLAG_SYN) {
+ AddrObj *AO;
+
+ //
+ // This segment had a SYN.
+ //
+ //
+#ifdef NT
+ CTEGetLockAtDPC(&AddrObjTableLock, &TableHandle);
+#endif
+
+#ifdef SECFLTR
+ // See if we are filtering the
+ // destination interface/port.
+ //
+ if ( (!SecurityFilteringEnabled ||
+ IsPermittedSecurityFilter(
+ LocalAddr,
+ IPContext,
+ PROTOCOL_TCP,
+ (ulong) net_short(TCPH->tcp_dest)
+ ))
+ )
+ {
+#else // SECFLTR
+ if ( 1 ) {
+#endif // SECFLTR
+
+ //
+ // Find a matching address object, and then try and find a
+ // listening connection on that AO.
+ //
+ AO = GetBestAddrObj(Dest, TCPH->tcp_dest, PROTOCOL_TCP);
+ if (AO != NULL) {
+
+ // Found an AO. Try and find a listening connection.
+ // FindListenConn will free the lock on the AddrObjTable.
+ RcvTCB = FindListenConn(AO, Src, TCPH->tcp_src, OptInfo);
+
+ if (RcvTCB != NULL) {
+ uint Inserted;
+
+ CTEStructAssert(RcvTCB, tcb);
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle);
+
+ // We found a listening connection. Initialize it
+ // now, and if it is actually to be accepted we'll
+ // send a SYN-ACK also.
+
+ CTEAssert(RcvTCB->tcb_state == TCB_SYN_RCVD);
+
+ RcvTCB->tcb_daddr = Src;
+ RcvTCB->tcb_saddr = Dest;
+ RcvTCB->tcb_dport = TCPH->tcp_src;
+ RcvTCB->tcb_sport = TCPH->tcp_dest;
+ RcvTCB->tcb_rcvnext = ++RcvInfo.tri_seq;
+ RcvTCB->tcb_sendwin = RcvInfo.tri_window;
+ RcvTCB->tcb_remmss = FindMSS(TCPH);
+ TStats.ts_passiveopens++;
+ RcvTCB->tcb_fastchk |= TCP_FLAG_IN_RCV;
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle);
+
+ Inserted = InsertTCB(RcvTCB);
+
+ // Get the lock on it, and see if it's been
+ // accepted.
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle);
+ if (!Inserted) {
+ // Couldn't insert it!.
+ CompleteConnReq(RcvTCB, OptInfo,
+ TDI_CONNECTION_ABORTED);
+ RcvTCB->tcb_refcnt--;
+#ifdef NT
+ TryToCloseTCB(RcvTCB, TCB_CLOSE_ABORTED, DISPATCH_LEVEL);
+#else
+ TryToCloseTCB(RcvTCB, TCB_CLOSE_ABORTED, TableHandle);
+#endif
+ return IP_SUCCESS;
+ }
+
+
+ RcvTCB->tcb_fastchk &= ~TCP_FLAG_IN_RCV;
+ if (RcvTCB->tcb_flags & SEND_AFTER_RCV) {
+ RcvTCB->tcb_flags &= ~SEND_AFTER_RCV;
+ DelayAction(RcvTCB, NEED_OUTPUT);
+ }
+
+ // We'll need to update the options, in any case.
+ if (OptInfo->ioi_options != NULL) {
+ if (!(RcvTCB->tcb_flags & CLIENT_OPTIONS)) {
+ (*LocalNetInfo.ipi_updateopts)(OptInfo,
+ &RcvTCB->tcb_opt, Src, NULL_IP_ADDR);
+ }
+ }
+
+ if (RcvTCB->tcb_flags & CONN_ACCEPTED) {
+
+ // The connection was accepted. Finish the
+ // initialization, and send the SYN ack.
+
+#ifdef NT
+ AcceptConn(RcvTCB, DISPATCH_LEVEL);
+#else
+ AcceptConn(RcvTCB, TableHandle);
+#endif
+
+ return IP_SUCCESS;
+ } else {
+
+ // We don't know what to do about the
+ // connection yet. Return the pending listen,
+ // dereference the connection, and return.
+
+ CompleteConnReq(RcvTCB, OptInfo, TDI_SUCCESS);
+
+#ifdef NT
+ DerefTCB(RcvTCB, DISPATCH_LEVEL);
+#else
+ DerefTCB(RcvTCB, TableHandle);
+#endif
+
+ return IP_SUCCESS;
+ }
+
+ }
+ // No listening connection. AddrObjTableLock was
+ // released by FindListenConn. Fall through to send
+ // RST code.
+
+ } else {
+ // No address object. Free the lock, and fall through
+ // to the send RST code.
+ CTEFreeLockFromDPC(&AddrObjTableLock, TableHandle);
+ }
+ }
+ else {
+ // Operation not permitted. Free the lock, and fall through
+ // to the send RST code.
+ CTEFreeLockFromDPC(&AddrObjTableLock, TableHandle);
+ }
+
+ }
+
+ // Toss out any segments containing RST.
+ if (RcvInfo.tri_flags & TCP_FLAG_RST)
+ return IP_SUCCESS;
+
+ // Not a SYN, no AddrObj available, or port filtered.
+ // Send a RST back.
+ SendRSTFromHeader(TCPH, Size, Src, Dest, OptInfo);
+
+ return IP_SUCCESS;
+ }
+
+ // Do the fast path check. We can hit the fast path if the incoming
+ // sequence number matches our receive next and the masked flags
+ // match our 'predicted' flags.
+ CheckTCBRcv(RcvTCB);
+ RcvTCB->tcb_alive = TCPTime;
+
+ if (RcvTCB->tcb_rcvnext == RcvInfo.tri_seq &&
+ (RcvInfo.tri_flags & TCP_FLAGS_ALL) == RcvTCB->tcb_fastchk){
+
+ Actions = 0;
+ RcvTCB->tcb_refcnt++;
+
+ // The fast path. We know all we have to do here is ack sends and
+ // deliver data. First try and ack data.
+
+
+ if (SEQ_LT(RcvTCB->tcb_senduna, RcvInfo.tri_ack) &&
+ SEQ_LTE(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) {
+
+ uint CWin;
+ uint MSS;
+
+ // The ack acknowledes something. Pull the
+ // appropriate amount off the send q.
+ ACKData(RcvTCB, RcvInfo.tri_ack);
+
+ // If this acknowledges something we were running a RTT on,
+ // update that stuff now.
+ if (RcvTCB->tcb_rtt != 0 && SEQ_GT(RcvInfo.tri_ack,
+ RcvTCB->tcb_rttseq)) {
+ short RTT;
+
+ RTT = (short)(TCPTime - RcvTCB->tcb_rtt);
+ RcvTCB->tcb_rtt = 0;
+ RTT -= (RcvTCB->tcb_smrtt >> 3);
+ RcvTCB->tcb_smrtt += RTT;
+ RTT = (RTT >= 0 ? RTT : -RTT);
+ RTT -= (RcvTCB->tcb_delta >> 3);
+ RcvTCB->tcb_delta += RTT;
+ RcvTCB->tcb_rexmit = MIN(MAX(REXMIT_TO(RcvTCB),
+ MIN_RETRAN_TICKS), MAX_REXMIT_TO);
+ }
+
+ // Update the congestion window now.
+ CWin = RcvTCB->tcb_cwin;
+ MSS = RcvTCB->tcb_mss;
+ if (CWin < RcvTCB->tcb_maxwin) {
+ if (CWin < RcvTCB->tcb_ssthresh)
+ CWin += MSS;
+ else
+ CWin += (MSS * MSS)/CWin;
+
+ RcvTCB->tcb_cwin = CWin;
+ }
+
+ CTEAssert(*(int *)&RcvTCB->tcb_cwin > 0);
+
+ // We've acknowledged something, so reset the rexmit count.
+ // If there's still stuff outstanding, restart the rexmit
+ // timer.
+ RcvTCB->tcb_rexmitcnt = 0;
+ if (SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendmax))
+ STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer);
+ else
+ START_TCB_TIMER(RcvTCB->tcb_rexmittimer, RcvTCB->tcb_rexmit);
+
+ // Since we've acknowledged data, we need to update the window.
+ RcvTCB->tcb_sendwin = RcvInfo.tri_window;
+ RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin, RcvInfo.tri_window);
+ RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq;
+ RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack;
+ // We've updated the window, remember to send some more.
+ Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0);
+
+#if FAST_RETRANSMIT
+ {
+ // If the receiver has already sent dup acks, but we are not
+ // sending because the SendWin is less than a segment, then
+ // to avoid time outs on the previous send (receiver is waiting for
+ // retransmitted data but we are not sending the segment..) prematurely
+ // timeout (set rexmittimer to 1 tick)
+ //
+
+ int SendWin;
+ uint AmtOutstanding,AmtUnsent;
+
+ AmtOutstanding = (uint)(RcvTCB->tcb_sendnext -
+ RcvTCB->tcb_senduna);
+ AmtUnsent = RcvTCB->tcb_unacked - AmtOutstanding;
+
+ SendWin = (int)(MIN(RcvTCB->tcb_sendwin, RcvTCB->tcb_cwin) -
+ AmtOutstanding);
+
+
+ if ((Size == 0) &&
+ (SendWin < RcvTCB->tcb_mss) && (RcvTCB->tcb_dup > 0)) {
+ STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer);
+ START_TCB_TIMER(RcvTCB->tcb_rexmittimer, 1);
+ }
+ }
+
+ RcvTCB->tcb_dup = 0;
+#endif
+
+ } else {
+ // It doesn't ack anything. If it's an ack for something
+ // larger than we've sent then ACKAndDrop it, otherwise
+ // ignore it.
+ if (SEQ_GT(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) {
+ ACKAndDrop(&RcvInfo, RcvTCB);
+ return IP_SUCCESS;
+ } else
+
+ //SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendmax
+ // If the ack matches our existing UNA, we need to see if
+ // we can update the window.
+ // Or check if fast retransmit is needed
+
+#if FAST_RETRANSMIT
+ // If it is a pure duplicate ack, check if it is
+ // time to retransmit immediately
+
+ if ( (Size == 0) && SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) &&
+ (RcvTCB->tcb_sendwin == RcvInfo.tri_window) ) {
+
+ RcvTCB->tcb_dup++;
+
+ if ((RcvTCB->tcb_dup == MaxDupAcks) ) {
+
+ //Okay. Time to retransmit the segment the receiver is asking for
+
+ STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer);
+
+ RcvTCB->tcb_rtt = 0;
+
+ if (!(RcvTCB->tcb_flags & FLOW_CNTLD)) {
+
+ // Don't let the slow start threshold go below 2
+ // segments
+
+ RcvTCB->tcb_ssthresh =
+ MAX(
+ MIN(RcvTCB->tcb_cwin,RcvTCB->tcb_sendwin) / 2,
+ (uint) RcvTCB->tcb_mss * 2 );
+ RcvTCB->tcb_cwin = RcvTCB->tcb_mss;
+ }
+
+ // Recall the segment in question and send it out
+ // Note that tcb_lock will be dereferenced by the caller
+
+ ResetAndFastSend (RcvTCB, RcvTCB->tcb_senduna);
+
+ return IP_SUCCESS;
+
+
+ } else if ((RcvTCB->tcb_dup > MaxDupAcks) ) {
+
+ int SendWin;
+ uint AmtOutstanding,AmtUnsent;
+
+ if (SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) &&
+ (SEQ_LT(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) ||
+ (SEQ_EQ(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) &&
+ SEQ_LTE(RcvTCB->tcb_sendwl2, RcvInfo.tri_ack)))) {
+
+ RcvTCB->tcb_sendwin = RcvInfo.tri_window;
+ RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin,
+ RcvInfo.tri_window);
+ RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq;
+ RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack;
+
+ // Since we've updated the window, remember to send
+ // some more.
+
+ Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0);
+ }
+
+ // Update the cwin to reflect the fact that the dup ack
+ // indicates the previous frame was received by the
+ // receiver
+
+
+ RcvTCB->tcb_cwin += RcvTCB->tcb_mss;
+
+ if ((RcvTCB->tcb_cwin+RcvTCB->tcb_mss) < RcvTCB->tcb_sendwin ) {
+ AmtOutstanding = (uint)(RcvTCB->tcb_sendnext -
+ RcvTCB->tcb_senduna);
+ AmtUnsent = RcvTCB->tcb_unacked - AmtOutstanding;
+
+ SendWin = (int)(MIN(RcvTCB->tcb_sendwin, RcvTCB->tcb_cwin) -
+ AmtOutstanding);
+
+ if (SendWin < RcvTCB->tcb_mss) {
+ RcvTCB->tcb_force=1;
+ }
+ }
+
+ Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0);
+
+ } else if ((RcvTCB->tcb_dup < MaxDupAcks)) {
+
+ int SendWin;
+ uint AmtOutstanding,AmtUnsent;
+
+ if (SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) &&
+ (SEQ_LT(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) ||
+ (SEQ_EQ(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) &&
+ SEQ_LTE(RcvTCB->tcb_sendwl2, RcvInfo.tri_ack)))) {
+
+ RcvTCB->tcb_sendwin = RcvInfo.tri_window;
+ RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin,
+ RcvInfo.tri_window);
+ RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq;
+ RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack;
+
+ // Since we've updated the window, remember to send
+ // some more.
+ }
+
+ // Check if we need to set tcb_force.
+
+ if ((RcvTCB->tcb_cwin+RcvTCB->tcb_mss) < RcvTCB->tcb_sendwin ) {
+
+ AmtOutstanding = (uint)(RcvTCB->tcb_sendnext -
+ RcvTCB->tcb_senduna);
+ AmtUnsent = RcvTCB->tcb_unacked - AmtOutstanding;
+
+ SendWin = (int)(MIN(RcvTCB->tcb_sendwin, RcvTCB->tcb_cwin) -
+ AmtOutstanding);
+ if (SendWin < RcvTCB->tcb_mss){
+ RcvTCB->tcb_force=1;
+ }
+ }
+
+ Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0);
+
+
+ } // End of all MaxDupAck cases
+
+ } else { // not a pure duplicate ack (size == 0 )
+
+ // Size !=0 or recvr is advertizing new window.
+ // update the window and check if
+ // anything needs to be sent
+
+ RcvTCB->tcb_dup = 0;
+
+ if (SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) &&
+ (SEQ_LT(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) ||
+ (SEQ_EQ(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) &&
+ SEQ_LTE(RcvTCB->tcb_sendwl2, RcvInfo.tri_ack)))) {
+ RcvTCB->tcb_sendwin = RcvInfo.tri_window;
+ RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin,
+ RcvInfo.tri_window);
+ RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq;
+ RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack;
+ // Since we've updated the window, remember to send
+ // some more.
+ Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0);
+ }
+
+ } // for SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendmax) case
+
+#else //FAST_RETRANSMIT
+
+ if (SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) &&
+ (SEQ_LT(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) ||
+ (SEQ_EQ(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) &&
+ SEQ_LTE(RcvTCB->tcb_sendwl2, RcvInfo.tri_ack)))) {
+ RcvTCB->tcb_sendwin = RcvInfo.tri_window;
+ RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin,
+ RcvInfo.tri_window);
+ RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq;
+ RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack;
+ // Since we've updated the window, remember to send
+ // some more.
+ Actions = (RcvTCB->tcb_unacked ? NEED_OUTPUT : 0);
+ }
+#endif //FAST_RETRANSMIT
+
+ }
+
+
+ NewSize = MIN((int) Size, RcvTCB->tcb_rcvwin);
+ if (NewSize != 0) {
+ RcvTCB->tcb_fastchk |= TCP_FLAG_IN_RCV;
+#ifdef VXD
+ CTEFreeLock(&RcvTCB->tcb_lock, TableHandle);
+ BytesTaken = (*RcvTCB->tcb_rcvhndlr)(RcvTCB, RcvInfo.tri_flags,
+ RcvBuf, NewSize);
+ CTEGetLock(&RcvTCB->tcb_lock, &TableHandle);
+#else
+ BytesTaken = (*RcvTCB->tcb_rcvhndlr)(RcvTCB, RcvInfo.tri_flags,
+ RcvBuf, NewSize);
+#endif
+ RcvTCB->tcb_rcvnext += BytesTaken;
+ RcvTCB->tcb_rcvwin -= BytesTaken;
+ CheckTCBRcv(RcvTCB);
+
+ RcvTCB->tcb_fastchk &= ~TCP_FLAG_IN_RCV;
+
+ Actions |= (RcvTCB->tcb_flags & SEND_AFTER_RCV ?
+ NEED_OUTPUT : 0);
+
+ RcvTCB->tcb_flags &= ~SEND_AFTER_RCV;
+ if ((RcvTCB->tcb_flags & ACK_DELAYED) || (BytesTaken != NewSize))
+ Actions |= NEED_ACK;
+ else {
+ RcvTCB->tcb_flags |= ACK_DELAYED;
+ START_TCB_TIMER(RcvTCB->tcb_delacktimer, DEL_ACK_TICKS);
+ }
+ } else {
+ // The new size is 0. If the original size was not 0, we must
+ // have a 0 rcv. win and hence need to send an ACK to this
+ // probe.
+ Actions |= (Size ? NEED_ACK : 0);
+ }
+
+ if (Actions)
+ DelayAction(RcvTCB, Actions);
+
+#ifndef VXD
+ TableHandle = DISPATCH_LEVEL;
+#endif
+ DerefTCB(RcvTCB, TableHandle);
+
+ return IP_SUCCESS;
+ }
+
+#ifndef VXD
+ TableHandle = DISPATCH_LEVEL;
+#endif
+ // Make sure we can handle this frame. We can't handle it if we're
+ // in SYN_RCVD and the accept is still pending, or we're in a
+ // non-established state and already in the receive handler.
+ if ((RcvTCB->tcb_state == TCB_SYN_RCVD &&
+ !(RcvTCB->tcb_flags & CONN_ACCEPTED)) ||
+ (RcvTCB->tcb_state != TCB_ESTAB && (RcvTCB->tcb_fastchk &
+ TCP_FLAG_IN_RCV))) {
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle);
+ return IP_SUCCESS;
+ }
+
+ // If it's closed, it's a temporary zombie TCB. Reset the sender.
+ if (RcvTCB->tcb_state == TCB_CLOSED || CLOSING(RcvTCB) ||
+ ((RcvTCB->tcb_flags & (GC_PENDING | TW_PENDING)) == GC_PENDING)) {
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle);
+ SendRSTFromHeader(TCPH, Size, Src, Dest, OptInfo);
+ return IP_SUCCESS;
+ }
+
+ // At this point, we have a connection, and it's locked. Following
+ // the 'Segment Arrives' section of 793, the next thing to check is
+ // if this connection is in SynSent state.
+
+ if (RcvTCB->tcb_state == TCB_SYN_SENT) {
+
+ CTEAssert(RcvTCB->tcb_flags & ACTIVE_OPEN);
+
+ // Check the ACK bit. Since we don't send data with our SYNs, the
+ // check we make is for the ack to exactly match our SND.NXT.
+ if (RcvInfo.tri_flags & TCP_FLAG_ACK) {
+ // ACK is set.
+ if (!SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendnext)) {
+ // Bad ACK value.
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle);
+ // Send a RST back at him.
+ SendRSTFromHeader(TCPH, Size, Src, Dest, OptInfo);
+ return IP_SUCCESS;
+ }
+ }
+
+ if (RcvInfo.tri_flags & TCP_FLAG_RST) {
+ // There's an acceptable RST. We'll persist here, sending
+ // another SYN in PERSIST_TIMEOUT ms, until we fail from too
+ // many retrys.
+ if (RcvTCB->tcb_rexmitcnt == MaxConnectRexmitCount) {
+ // We've had a positive refusal, and one more rexmit
+ // would time us out, so close the connection now.
+ CompleteConnReq(RcvTCB, OptInfo, TDI_CONN_REFUSED);
+
+ TryToCloseTCB(RcvTCB, TCB_CLOSE_REFUSED, TableHandle);
+ } else {
+ START_TCB_TIMER(RcvTCB->tcb_rexmittimer, PERSIST_TIMEOUT);
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle);
+ }
+ return IP_SUCCESS;
+ }
+
+ // See if we have a SYN. If we do, we're going to change state
+ // somehow (either to ESTABLISHED or SYN_RCVD).
+ if (RcvInfo.tri_flags & TCP_FLAG_SYN) {
+ RcvTCB->tcb_refcnt++;
+
+ // We have a SYN. Go ahead and record the sequence number and
+ // window info.
+ RcvTCB->tcb_rcvnext = ++RcvInfo.tri_seq;
+
+ if (RcvInfo.tri_flags & TCP_FLAG_URG) {
+ // Urgent data. Update the pointer.
+ if (RcvInfo.tri_urgent != 0)
+ RcvInfo.tri_urgent--;
+ else
+ RcvInfo.tri_flags &= ~TCP_FLAG_URG;
+ }
+
+ RcvTCB->tcb_remmss = FindMSS(TCPH);
+
+ // If there are options, update them now. We already have an
+ // RCE open, so if we have new options we'll have to close
+ // it and open a new one.
+ if (OptInfo->ioi_options != NULL) {
+ if (!(RcvTCB->tcb_flags & CLIENT_OPTIONS)) {
+ (*LocalNetInfo.ipi_updateopts)(OptInfo,
+ &RcvTCB->tcb_opt, Src, NULL_IP_ADDR);
+ (*LocalNetInfo.ipi_closerce)(RcvTCB->tcb_rce);
+ InitRCE(RcvTCB);
+ }
+ } else{
+ RcvTCB->tcb_mss = MIN(RcvTCB->tcb_mss, RcvTCB->tcb_remmss);
+
+ CTEAssert(RcvTCB->tcb_mss > 0);
+
+ }
+
+ RcvTCB->tcb_rexmitcnt = 0;
+ STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer);
+
+ AdjustRcvWin(RcvTCB);
+
+ if (RcvInfo.tri_flags & TCP_FLAG_ACK) {
+ // Our SYN has been acked. Update SND.UNA and stop the
+ // retrans timer.
+ RcvTCB->tcb_senduna = RcvInfo.tri_ack;
+ RcvTCB->tcb_sendwin = RcvInfo.tri_window;
+ RcvTCB->tcb_maxwin = RcvInfo.tri_window;
+ RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq;
+ RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack;
+ GoToEstab(RcvTCB);
+
+#ifdef RASAUTODIAL
+ //
+ // Set a bit that informs TCBTimeout to notify
+ // the automatic connection driver of this new
+ // connection. Only set this flag if we
+ // have binded succesfully with the automatic
+ // connection driver.
+ //
+ if (fAcdLoadedG)
+ RcvTCB->tcb_flags |= ACD_CONN_NOTIF;
+#endif // RASAUTODIAL
+
+ // Remove whatever command exists on this connection.
+ CompleteConnReq(RcvTCB, OptInfo, TDI_SUCCESS);
+
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle);
+ SendACK(RcvTCB);
+
+ // Now handle other data and controls. To do this we need
+ // to reaquire the lock, and make sure we haven't started
+ // closing it.
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle);
+ if (!CLOSING(RcvTCB)) {
+ // We haven't started closing it. Turn off the
+ // SYN flag and continue processing.
+ RcvInfo.tri_flags &= ~TCP_FLAG_SYN;
+ if ((RcvInfo.tri_flags & TCP_FLAGS_ALL) != TCP_FLAG_ACK ||
+ Size != 0)
+ goto NotSYNSent;
+ }
+ DerefTCB(RcvTCB, TableHandle);
+ return IP_SUCCESS;
+ } else {
+ // A SYN, but not an ACK. Go to SYN_RCVD.
+ RcvTCB->tcb_state = TCB_SYN_RCVD;
+ RcvTCB->tcb_sendnext = RcvTCB->tcb_senduna;
+ SendSYN(RcvTCB, TableHandle);
+
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle);
+ DerefTCB(RcvTCB, TableHandle);
+ return IP_SUCCESS;
+ }
+
+ } else {
+ // No SYN, just toss the frame.
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle);
+ return IP_SUCCESS;
+ }
+
+ }
+
+ RcvTCB->tcb_refcnt++;
+
+NotSYNSent:
+ // Not in the SYN-SENT state. Check the sequence number. If my window
+ // is 0, I'll truncate all incoming frames but look at some of the
+ // control fields. Otherwise I'll try and make this segment fit into
+ // the window.
+ if (RcvTCB->tcb_rcvwin != 0) {
+ int StateSize; // Size, including state info.
+ SeqNum LastValidSeq; // Sequence number of last valid
+ // byte at RWE.
+
+ // We are offering a window. If this segment starts in front of my
+ // receive window, clip off the front part.
+#if 1 // Bug #63900
+ //Check for the sanity of received sequence.
+ //This is to fix the 1 bit error(MSB) case in the rcv seq.
+ // Also, check the incoming size.
+
+
+ if ((SEQ_LT(RcvInfo.tri_seq, RcvTCB->tcb_rcvnext)) &&
+ ((int)Size >= 0) &&
+ (RcvTCB->tcb_rcvnext - RcvInfo.tri_seq ) > 0) {
+
+#else
+
+ if (SEQ_LT(RcvInfo.tri_seq, RcvTCB->tcb_rcvnext)) {
+#endif
+
+ int AmountToClip, FinByte;
+
+ if (RcvInfo.tri_flags & TCP_FLAG_SYN) {
+ // Had a SYN. Clip it off and update the sequence number.
+ RcvInfo.tri_flags &= ~TCP_FLAG_SYN;
+ RcvInfo.tri_seq++;
+ RcvInfo.tri_urgent--;
+ }
+
+ // Advance the receive buffer to point at the new data.
+ AmountToClip = RcvTCB->tcb_rcvnext - RcvInfo.tri_seq;
+ CTEAssert(AmountToClip >= 0);
+
+ // If there's a FIN on this segment, we'll need to account for
+ // it.
+ FinByte = ((RcvInfo.tri_flags & TCP_FLAG_FIN) ? 1: 0);
+
+ if (AmountToClip >= (((int) Size) + FinByte)) {
+ // Falls entirely before the window. We have more special
+ // case code here - if the ack. number acks something,
+ // we'll go ahead and take it, faking the sequence number
+ // to be rcvnext. This prevents problems on full duplex
+ // connections, where data has been received but not acked,
+ // and retransmission timers reset the seq. number to
+ // below our rcvnext.
+ if ((RcvInfo.tri_flags & TCP_FLAG_ACK) &&
+ SEQ_LT(RcvTCB->tcb_senduna, RcvInfo.tri_ack) &&
+ SEQ_LTE(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) {
+ // This contains valid ACK info. Fudge the information
+ // to get through the rest of this.
+ Size = 0;
+ AmountToClip = 0;
+ RcvInfo.tri_seq = RcvTCB->tcb_rcvnext;
+ RcvInfo.tri_flags &= ~(TCP_FLAG_SYN | TCP_FLAG_FIN |
+ TCP_FLAG_RST | TCP_FLAG_URG);
+#ifdef DEBUG
+ FinByte = 1; // Fake out assert below.
+#endif
+ } else {
+ ACKAndDrop(&RcvInfo, RcvTCB);
+ return IP_SUCCESS;
+ }
+ }
+
+ // Trim what we have to. If we can't trim enough, the frame
+ // is too short. This shouldn't happen, but it it does we'll
+ // drop the frame.
+ Size -= AmountToClip;
+ RcvInfo.tri_seq += AmountToClip;
+ RcvInfo.tri_urgent -= AmountToClip;
+ RcvBuf = TrimRcvBuf(RcvBuf, AmountToClip);
+ CTEAssert(RcvBuf != NULL);
+ CTEAssert(RcvBuf->ipr_size != 0 ||
+ (Size == 0 && FinByte));
+
+ if (*(int *)&RcvInfo.tri_urgent < 0) {
+ RcvInfo.tri_urgent = 0;
+ RcvInfo.tri_flags &= ~TCP_FLAG_URG;
+ }
+
+ }
+
+ // We've made sure the front is OK. Now make sure part of it doesn't
+ // fall outside of the right edge of the window. If it does,
+ // we'll truncate the frame (removing the FIN, if any). If we
+ // truncate the whole frame we'll ACKAndDrop it.
+ StateSize = Size + ((RcvInfo.tri_flags & TCP_FLAG_SYN) ? 1: 0) +
+ ((RcvInfo.tri_flags & TCP_FLAG_FIN) ? 1: 0);
+
+ if (StateSize)
+ StateSize--;
+
+ // Now the incoming sequence number (RcvInfo.tri_seq) + StateSize
+ // it the last sequence number in the segment. If this is greater
+ // than the last valid byte in the window, we have some overlap
+ // to chop off.
+
+ CTEAssert(StateSize >= 0);
+ LastValidSeq = RcvTCB->tcb_rcvnext + RcvTCB->tcb_rcvwin - 1;
+ if (SEQ_GT(RcvInfo.tri_seq + StateSize, LastValidSeq)) {
+ int AmountToChop;
+
+ // At least some part of the frame is outside of our window.
+ // See if it starts outside our window.
+
+ if (SEQ_GT(RcvInfo.tri_seq, LastValidSeq)) {
+ // Falls entirely outside the window. We have special
+ // case code to deal with a pure ack that falls exactly at
+ // our right window edge. Otherwise we ack and drop it.
+ if (!SEQ_EQ(RcvInfo.tri_seq, LastValidSeq+1) || Size != 0
+ || (RcvInfo.tri_flags & (TCP_FLAG_SYN | TCP_FLAG_FIN))) {
+ ACKAndDrop(&RcvInfo, RcvTCB);
+ return IP_SUCCESS;
+ }
+ } else {
+
+ // At least some part of it is in the window. If there's a
+ // FIN, chop that off and see if that moves us inside.
+ if (RcvInfo.tri_flags & TCP_FLAG_FIN) {
+ RcvInfo.tri_flags &= ~TCP_FLAG_FIN;
+ StateSize--;
+ }
+
+ // Now figure out how much to chop off.
+ AmountToChop = (RcvInfo.tri_seq + StateSize) - LastValidSeq;
+ CTEAssert(AmountToChop >= 0);
+ Size -= AmountToChop;
+
+ }
+ }
+ } else {
+ if (!SEQ_EQ(RcvTCB->tcb_rcvnext, RcvInfo.tri_seq)) {
+
+ // If there's a RST on this segment, and he's only off by 1,
+ // take it anyway. This can happen if the remote peer is
+ // probing and sends with the seq. # after the probe.
+ if (!(RcvInfo.tri_flags & TCP_FLAG_RST) ||
+ !(SEQ_EQ(RcvTCB->tcb_rcvnext, (RcvInfo.tri_seq - 1)))) {
+ ACKAndDrop(&RcvInfo, RcvTCB);
+ return IP_SUCCESS;
+ } else
+ RcvInfo.tri_seq = RcvTCB->tcb_rcvnext;
+ }
+
+ // He's in sequence, but we have a window of 0. Truncate the
+ // size, and clear any sequence consuming bits.
+ if (Size != 0 ||
+ (RcvInfo.tri_flags & (TCP_FLAG_SYN | TCP_FLAG_FIN))) {
+ RcvInfo.tri_flags &= ~(TCP_FLAG_SYN | TCP_FLAG_FIN);
+ Size = 0;
+ if (!(RcvInfo.tri_flags & TCP_FLAG_RST))
+ DelayAction(RcvTCB, NEED_ACK);
+ }
+ }
+
+ // At this point, the segment is in our window and does not overlap
+ // on either end. If it's the next sequence number we expect, we can
+ // handle the data now. Otherwise we'll queue it for later. In either
+ // case we'll handle RST and ACK information right now.
+ CTEAssert((*(int *)&Size) >= 0);
+
+ // Now, following 793, we check the RST bit.
+ if (RcvInfo.tri_flags & TCP_FLAG_RST) {
+ uchar Reason;
+ // We can't go back into the LISTEN state from SYN-RCVD here,
+ // because we may have notified the client via a listen completing
+ // or a connect indication. So, if came from an active open we'll
+ // give back a 'connection refused' notice. For all other cases
+ // we'll just destroy the connection.
+
+ if (RcvTCB->tcb_state == TCB_SYN_RCVD) {
+ if (RcvTCB->tcb_flags & ACTIVE_OPEN)
+ Reason = TCB_CLOSE_REFUSED;
+ else
+ Reason = TCB_CLOSE_RST;
+ } else
+ Reason = TCB_CLOSE_RST;
+
+ TryToCloseTCB(RcvTCB, Reason, TableHandle);
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle);
+
+ if (RcvTCB->tcb_state != TCB_TIME_WAIT) {
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle);
+ RemoveTCBFromConn(RcvTCB);
+ NotifyOfDisc(RcvTCB, OptInfo, TDI_CONNECTION_RESET);
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle);
+ }
+
+ DerefTCB(RcvTCB, TableHandle);
+ return IP_SUCCESS;
+ }
+
+ // Next check the SYN bit.
+ if (RcvInfo.tri_flags & TCP_FLAG_SYN) {
+ // Again, we can't quietly go back into the LISTEN state here, even
+ // if we came from a passive open.
+ TryToCloseTCB(RcvTCB, TCB_CLOSE_ABORTED, TableHandle);
+ SendRSTFromHeader(TCPH, Size, Src, Dest, OptInfo);
+
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle);
+
+ if (RcvTCB->tcb_state != TCB_TIME_WAIT) {
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle);
+ RemoveTCBFromConn(RcvTCB);
+ NotifyOfDisc(RcvTCB, OptInfo, TDI_CONNECTION_RESET);
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle);
+ }
+
+ DerefTCB(RcvTCB, TableHandle);
+ return IP_SUCCESS;
+ }
+
+ // Check the ACK field. If it's not on drop the segment.
+ if (RcvInfo.tri_flags & TCP_FLAG_ACK) {
+ uint UpdateWindow;
+
+ // If we're in SYN-RCVD, go to ESTABLISHED.
+ if (RcvTCB->tcb_state == TCB_SYN_RCVD) {
+ if (SEQ_LT(RcvTCB->tcb_senduna, RcvInfo.tri_ack) &&
+ SEQ_LTE(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) {
+ // The ack is valid.
+
+#ifdef SYN_ATTACK
+ if (SynAttackProtect) {
+ CTELockHandle Handle;
+
+ //
+ // We will be reiniting the tcprexmitcnt to 0. If we are
+ // configured for syn-attack protection and the rexmit cnt
+ // is >1, decrement the count of connections that are
+ // in the half-open-retried state. Check whether we are
+ // below a low-watermark. If we are, increase the rexmit
+ // count back to configured values
+ //
+ CTEGetLockAtDPC(&SynAttLock, &Handle);
+ if (RcvTCB->tcb_rexmitcnt >= ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT) {
+ BOOLEAN Trigger;
+ Trigger = (TCPHalfOpen < TCPMaxHalfOpen) ||
+ (--TCPHalfOpenRetried <= TCPMaxHalfOpenRetriedLW);
+ if (Trigger && (MaxConnectResponseRexmitCountTmp == ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT))
+ {
+ MaxConnectResponseRexmitCountTmp = MAX_CONNECT_RESPONSE_REXMIT_CNT;
+ }
+
+ }
+ //
+ // Decrement the # of conn. in half open state
+ //
+ TCPHalfOpen--;
+ CTEFreeLockFromDPC(&SynAttLock, Handle);
+ }
+#endif
+ RcvTCB->tcb_rexmitcnt = 0;
+ STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer);
+ RcvTCB->tcb_senduna++;
+ RcvTCB->tcb_sendwin = RcvInfo.tri_window;
+ RcvTCB->tcb_maxwin = RcvInfo.tri_window;
+ RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq;
+ RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack;
+ GoToEstab(RcvTCB);
+
+ // Now complete whatever we can here.
+ CompleteConnReq(RcvTCB, OptInfo, TDI_SUCCESS);
+ } else {
+ DerefTCB(RcvTCB, TableHandle);
+ SendRSTFromHeader(TCPH, Size, Src, Dest, OptInfo);
+ return IP_SUCCESS;
+ }
+ } else {
+
+ // We're not in SYN-RCVD. See if this acknowledges anything.
+ if (SEQ_LT(RcvTCB->tcb_senduna, RcvInfo.tri_ack) &&
+ SEQ_LTE(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) {
+ uint CWin;
+
+ // The ack acknowledes something. Pull the
+ // appropriate amount off the send q.
+ ACKData(RcvTCB, RcvInfo.tri_ack);
+
+ // If this acknowledges something we were running a RTT on,
+ // update that stuff now.
+ if (RcvTCB->tcb_rtt != 0 && SEQ_GT(RcvInfo.tri_ack,
+ RcvTCB->tcb_rttseq)) {
+ short RTT;
+
+ RTT = (short)(TCPTime - RcvTCB->tcb_rtt);
+ RcvTCB->tcb_rtt = 0;
+ RTT -= (RcvTCB->tcb_smrtt >> 3);
+ RcvTCB->tcb_smrtt += RTT;
+ RTT = (RTT >= 0 ? RTT : -RTT);
+ RTT -= (RcvTCB->tcb_delta >> 3);
+ RcvTCB->tcb_delta += RTT;
+ RcvTCB->tcb_rexmit = MIN(MAX(REXMIT_TO(RcvTCB),
+ MIN_RETRAN_TICKS), MAX_REXMIT_TO);
+ }
+
+ // If we're probing for a PMTU black hole we've found one, so turn off
+ // the detection. The size is already down, so leave it there.
+ if (RcvTCB->tcb_flags & PMTU_BH_PROBE) {
+ RcvTCB->tcb_flags &= ~PMTU_BH_PROBE;
+ RcvTCB->tcb_bhprobecnt = 0;
+ if (--(RcvTCB->tcb_slowcount) == 0) {
+ RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+ }
+ }
+
+ // Update the congestion window now.
+ CWin = RcvTCB->tcb_cwin;
+ if (CWin < RcvTCB->tcb_maxwin) {
+ if (CWin < RcvTCB->tcb_ssthresh)
+ CWin += RcvTCB->tcb_mss;
+ else
+ CWin += (RcvTCB->tcb_mss * RcvTCB->tcb_mss)/CWin;
+
+ RcvTCB->tcb_cwin = MIN(CWin, RcvTCB->tcb_maxwin);
+ }
+
+ CTEAssert(*(int *)&RcvTCB->tcb_cwin > 0);
+
+ // We've acknowledged something, so reset the rexmit count.
+ // If there's still stuff outstanding, restart the rexmit
+ // timer.
+ RcvTCB->tcb_rexmitcnt = 0;
+ if (!SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendmax))
+ START_TCB_TIMER(RcvTCB->tcb_rexmittimer,
+ RcvTCB->tcb_rexmit);
+ else
+ STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer);
+
+ // If we've sent a FIN, and this acknowledges it, we
+ // need to complete the client's close request and
+ // possibly transition our state.
+
+ if (RcvTCB->tcb_flags & FIN_SENT) {
+ // We have sent a FIN. See if it's been acknowledged.
+ // Once we've sent a FIN, tcb_sendmax
+ // can't advance, so our FIN must have seq. number
+ // tcb_sendmax - 1. Thus our FIN is acknowledged
+ // if the incoming ack is equal to tcb_sendmax.
+ if (SEQ_EQ(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) {
+ // He's acked our FIN. Turn off the flags,
+ // and complete the request. We'll leave the
+ // FIN_OUTSTANDING flag alone, to force early
+ // outs in the send code.
+ RcvTCB->tcb_flags &= ~(FIN_NEEDED | FIN_SENT);
+
+ CTEAssert(RcvTCB->tcb_unacked == 0);
+ CTEAssert(RcvTCB->tcb_sendnext ==
+ RcvTCB->tcb_sendmax);
+
+ // Now figure out what we need to do. In FIN_WAIT1
+ // or FIN_WAIT, just complete the disconnect req.
+ // and continue. Otherwise, it's a bit trickier,
+ // since we can't complete the connreq until we
+ // remove the TCB from it's connection.
+ switch (RcvTCB->tcb_state) {
+
+ case TCB_FIN_WAIT1:
+ RcvTCB->tcb_state = TCB_FIN_WAIT2;
+ CompleteConnReq(RcvTCB, OptInfo,
+ TDI_SUCCESS);
+
+ // Start a timer in case we never get
+ // out of FIN_WAIT2. Set the retransmit
+ // count high to force a timeout the
+ // first time the timer fires.
+ RcvTCB->tcb_rexmitcnt = MaxDataRexmitCount;
+ START_TCB_TIMER(RcvTCB->tcb_rexmittimer,
+ FinWait2TO);
+
+ // Fall through to FIN-WAIT-2 processing.
+ case TCB_FIN_WAIT2:
+ break;
+ case TCB_CLOSING:
+ GracefulClose(RcvTCB, TRUE, FALSE,
+ TableHandle);
+ return IP_SUCCESS;
+ break;
+ case TCB_LAST_ACK:
+ GracefulClose(RcvTCB, FALSE, FALSE,
+ TableHandle);
+ return IP_SUCCESS;
+ break;
+ default:
+ DEBUGCHK;
+ break;
+ }
+ }
+
+ }
+ UpdateWindow = TRUE;
+ } else {
+ // It doesn't ack anything. If it's an ack for something
+ // larger than we've sent then ACKAndDrop it, otherwise
+ // ignore it. If we're in FIN_WAIT2, we'll restart the timer.
+ // We don't make this check above because we know no
+ // data can be acked when we're in FIN_WAIT2.
+
+ if (RcvTCB->tcb_state == TCB_FIN_WAIT2)
+ START_TCB_TIMER(RcvTCB->tcb_rexmittimer, FinWait2TO);
+
+ if (SEQ_GT(RcvInfo.tri_ack, RcvTCB->tcb_sendmax)) {
+ ACKAndDrop(&RcvInfo, RcvTCB);
+ return IP_SUCCESS;
+ } else {
+ // Now update the window if we can.
+ if (SEQ_EQ(RcvTCB->tcb_senduna, RcvInfo.tri_ack) &&
+ (SEQ_LT(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) ||
+ (SEQ_EQ(RcvTCB->tcb_sendwl1, RcvInfo.tri_seq) &&
+ SEQ_LTE(RcvTCB->tcb_sendwl2, RcvInfo.tri_ack)))) {
+ UpdateWindow = TRUE;
+ } else
+ UpdateWindow = FALSE;
+ }
+ }
+
+ if (UpdateWindow) {
+ RcvTCB->tcb_sendwin = RcvInfo.tri_window;
+ RcvTCB->tcb_maxwin = MAX(RcvTCB->tcb_maxwin,
+ RcvInfo.tri_window);
+ RcvTCB->tcb_sendwl1 = RcvInfo.tri_seq;
+ RcvTCB->tcb_sendwl2 = RcvInfo.tri_ack;
+ if (RcvInfo.tri_window == 0) {
+ // We've got a zero window.
+ if (!EMPTYQ(&RcvTCB->tcb_sendq)) {
+ RcvTCB->tcb_flags &= ~NEED_OUTPUT;
+ RcvTCB->tcb_rexmitcnt = 0;
+ START_TCB_TIMER(RcvTCB->tcb_rexmittimer,
+ RcvTCB->tcb_rexmit);
+ if (!(RcvTCB->tcb_flags & FLOW_CNTLD)) {
+ RcvTCB->tcb_flags |= FLOW_CNTLD;
+ RcvTCB->tcb_slowcount++;
+ RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+ }
+ }
+ } else {
+ if (RcvTCB->tcb_flags & FLOW_CNTLD) {
+ RcvTCB->tcb_rexmitcnt = 0;
+ RcvTCB->tcb_rexmit = MIN(MAX(REXMIT_TO(RcvTCB),
+ MIN_RETRAN_TICKS), MAX_REXMIT_TO);
+ if (TCB_TIMER_RUNNING(RcvTCB->tcb_rexmittimer)) {
+ START_TCB_TIMER(RcvTCB->tcb_rexmittimer,
+ RcvTCB->tcb_rexmit);
+ }
+ RcvTCB->tcb_flags &= ~(FLOW_CNTLD | FORCE_OUTPUT);
+ // Reset send next to the left edge of the window,
+ // because it might be at senduna+1 if we've been
+ // probing.
+ ResetSendNext(RcvTCB, RcvTCB->tcb_senduna);
+ if (--(RcvTCB->tcb_slowcount) == 0) {
+ RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+ }
+ }
+
+ // Since we've updated the window, see if we can send
+ // some more.
+ if (RcvTCB->tcb_unacked != 0 ||
+ (RcvTCB->tcb_flags & FIN_NEEDED))
+ DelayAction(RcvTCB, NEED_OUTPUT);
+
+ }
+ }
+
+ }
+
+ // We've handled all the acknowledgment stuff. If the size
+ // is greater than 0 or important bits are set process it further,
+ // otherwise it's a pure ack and we're done with it.
+ if (Size > 0 || (RcvInfo.tri_flags & TCP_FLAG_FIN)) {
+
+ // If we're not in a state where we can process incoming data
+ // or FINs, there's no point in going further. Just send an
+ // ack and drop this segment.
+ if (!DATA_RCV_STATE(RcvTCB->tcb_state) ||
+ (RcvTCB->tcb_flags & GC_PENDING)) {
+ ACKAndDrop(&RcvInfo, RcvTCB);
+ return IP_SUCCESS;
+ }
+
+ // If it's in sequence process it now, otherwise reassemble it.
+ if (SEQ_EQ(RcvInfo.tri_seq, RcvTCB->tcb_rcvnext)) {
+
+ // If we're already in the recv. handler, this is a
+ // duplicate. We'll just toss it.
+ if (RcvTCB->tcb_fastchk & TCP_FLAG_IN_RCV) {
+ DerefTCB(RcvTCB, TableHandle);
+ return IP_SUCCESS;
+ }
+
+ RcvTCB->tcb_fastchk |= TCP_FLAG_IN_RCV;
+
+ // Now loop, pulling things from the reassembly queue, until
+ // the queue is empty, or we can't take all of the data,
+ // or we hit a FIN.
+
+ do {
+
+ // Handle urgent data, if any.
+ if (RcvInfo.tri_flags & TCP_FLAG_URG) {
+ HandleUrgent(RcvTCB, &RcvInfo, RcvBuf, &Size);
+
+ // Since we may have freed the lock, we need to recheck
+ // and see if we're closing here.
+ if (CLOSING(RcvTCB))
+ break;
+
+ }
+
+
+ // OK, the data is in sequence, we've updated the
+ // reassembly queue and handled any urgent data. If we
+ // have any data go ahead and process it now.
+ if (Size > 0) {
+
+#ifdef VXD
+ CTEFreeLock(&RcvTCB->tcb_lock, TableHandle);
+ BytesTaken = (*RcvTCB->tcb_rcvhndlr)(RcvTCB,
+ RcvInfo.tri_flags, RcvBuf, Size);
+ CTEGetLock(&RcvTCB->tcb_lock, &TableHandle);
+#else
+ BytesTaken = (*RcvTCB->tcb_rcvhndlr)(RcvTCB,
+ RcvInfo.tri_flags, RcvBuf, Size);
+#endif
+ RcvTCB->tcb_rcvnext += BytesTaken;
+ RcvTCB->tcb_rcvwin -= BytesTaken;
+
+ CheckTCBRcv(RcvTCB);
+ if (RcvTCB->tcb_flags & ACK_DELAYED)
+ DelayAction(RcvTCB, NEED_ACK);
+ else {
+ RcvTCB->tcb_flags |= ACK_DELAYED;
+ START_TCB_TIMER(RcvTCB->tcb_delacktimer,
+ DEL_ACK_TICKS);
+ }
+
+ if (BytesTaken != Size) {
+ // We didn't take everything we could. No
+ // use in further processing, just bail
+ // out.
+ DelayAction(RcvTCB, NEED_ACK);
+ break;
+ }
+
+ // If we're closing now, we're done, so get out.
+ if (CLOSING(RcvTCB))
+ break;
+ }
+
+ // See if we need to advance over some urgent data.
+ if (RcvTCB->tcb_flags & URG_VALID) {
+ uint AdvanceNeeded;
+
+ // We only need to advance if we're not doing
+ // urgent inline. Urgent inline also has some
+ // implications for when we can clear the URG_VALID
+ // flag. If we're not doing urgent inline, we can
+ // clear it when rcvnext advances beyond urgent end.
+ // If we are doing inline, we clear it when rcvnext
+ // advances one receive window beyond urgend.
+ if (!(RcvTCB->tcb_flags & URG_INLINE)) {
+ if (RcvTCB->tcb_rcvnext == RcvTCB->tcb_urgstart)
+ RcvTCB->tcb_rcvnext = RcvTCB->tcb_urgend +
+ 1;
+ else
+ CTEAssert(SEQ_LT(RcvTCB->tcb_rcvnext,
+ RcvTCB->tcb_urgstart) ||
+ SEQ_GT(RcvTCB->tcb_rcvnext,
+ RcvTCB->tcb_urgend));
+ AdvanceNeeded = 0;
+ } else
+ AdvanceNeeded = RcvTCB->tcb_defaultwin;
+
+ // See if we can clear the URG_VALID flag.
+ if (SEQ_GT(RcvTCB->tcb_rcvnext - AdvanceNeeded,
+ RcvTCB->tcb_urgend)) {
+ RcvTCB->tcb_flags &= ~URG_VALID;
+ if (--(RcvTCB->tcb_slowcount) == 0) {
+ RcvTCB->tcb_fastchk &= ~TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+ }
+ }
+
+ }
+
+ // We've handled the data. If the FIN bit is set, we
+ // have more processing.
+ if (RcvInfo.tri_flags & TCP_FLAG_FIN) {
+ uint Notify = FALSE;
+
+ RcvTCB->tcb_rcvnext++;
+ DelayAction(RcvTCB, NEED_ACK);
+
+ PushData(RcvTCB);
+
+ switch (RcvTCB->tcb_state) {
+
+ case TCB_SYN_RCVD:
+ // I don't think we can get here - we
+ // should have discarded the frame if it
+ // had no ACK, or gone to established if
+ // it did.
+ DEBUGCHK;
+ case TCB_ESTAB:
+ RcvTCB->tcb_state = TCB_CLOSE_WAIT;
+ // We left established, we're off the
+ // fast path.
+ RcvTCB->tcb_slowcount++;
+ RcvTCB->tcb_fastchk |= TCP_FLAG_SLOW;
+ CheckTCBRcv(RcvTCB);
+ Notify = TRUE;
+ break;
+ case TCB_FIN_WAIT1:
+ RcvTCB->tcb_state = TCB_CLOSING;
+ Notify = TRUE;
+ break;
+ case TCB_FIN_WAIT2:
+ // Stop the FIN_WAIT2 timer.
+ STOP_TCB_TIMER(RcvTCB->tcb_rexmittimer);
+ RcvTCB->tcb_refcnt++;
+ GracefulClose(RcvTCB, TRUE, TRUE,
+ TableHandle);
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock,
+ &TableHandle);
+ break;
+ default:
+ DEBUGCHK;
+ break;
+ }
+
+ if (Notify) {
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock,
+ TableHandle);
+ NotifyOfDisc(RcvTCB, OptInfo, TDI_GRACEFUL_DISC);
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock,
+ &TableHandle);
+ }
+
+ break; // Exit out of WHILE loop.
+ }
+
+ // If the reassembly queue isn't empty, get what we
+ // can now.
+ RcvBuf = PullFromRAQ(RcvTCB, &RcvInfo, &Size);
+
+ CheckRBList(RcvBuf, Size);
+
+ } while (RcvBuf != NULL);
+
+ RcvTCB->tcb_fastchk &= ~TCP_FLAG_IN_RCV;
+ if (RcvTCB->tcb_flags & SEND_AFTER_RCV) {
+ RcvTCB->tcb_flags &= ~SEND_AFTER_RCV;
+ DelayAction(RcvTCB, NEED_OUTPUT);
+ }
+
+ DerefTCB(RcvTCB, TableHandle);
+ return IP_SUCCESS;
+
+ } else {
+
+ // It's not in sequence. Since it needs further processing,
+ // put in on the reassembly queue.
+ if (DATA_RCV_STATE(RcvTCB->tcb_state) &&
+ !(RcvTCB->tcb_flags & GC_PENDING)) {
+ PutOnRAQ(RcvTCB, &RcvInfo, RcvBuf, Size);
+ CTEFreeLockFromDPC(&RcvTCB->tcb_lock, TableHandle);
+ SendACK(RcvTCB);
+ CTEGetLockAtDPC(&RcvTCB->tcb_lock, &TableHandle);
+ DerefTCB(RcvTCB, TableHandle);
+ } else
+ ACKAndDrop(&RcvInfo, RcvTCB);
+
+ return IP_SUCCESS;
+ }
+ }
+
+ } else {
+ // No ACK. Just drop the segment and return.
+ DerefTCB(RcvTCB, TableHandle);
+ return IP_SUCCESS;
+ }
+
+ DerefTCB(RcvTCB, TableHandle);
+ } else // DataOffset <= Size
+ TStats.ts_inerrs++;
+ } else {
+ // Bump bad xsum counter.
+ TStats.ts_inerrs++;
+
+ }
+
+ } else // IsBCast
+ TStats.ts_inerrs++;
+
+
+ return IP_SUCCESS;
+
+}
+
+#pragma BEGIN_INIT
+
+//* InitTCPRcv - Initialize TCP receive side.
+//
+// Called during init time to initialize our TCP receive side.
+//
+// Input: Nothing.
+//
+// Returns: TRUE.
+//
+int
+InitTCPRcv(void)
+{
+#ifdef NT
+ ExInitializeSListHead(&TCPRcvReqFree);
+#endif
+
+ CTEInitLock(&RequestCompleteLock);
+ CTEInitLock(&TCBDelayLock);
+ CTEInitLock(&TCPRcvReqFreeLock);
+ INITQ(&ConnRequestCompleteQ);
+ INITQ(&SendCompleteQ);
+ INITQ(&TCBDelayQ);
+ RequestCompleteFlags = 0;
+ TCBDelayRtnCount = 0;
+
+#ifdef VXD
+ TCBDelayRtnLimit = 1;
+#endif
+#ifdef NT
+ TCBDelayRtnLimit = (uint) (** (PCHAR *) &KeNumberProcessors);
+ if (TCBDelayRtnLimit > TCB_DELAY_RTN_LIMIT)
+ TCBDelayRtnLimit = TCB_DELAY_RTN_LIMIT;
+#endif
+
+ DummyBuf.ipr_owner = IPR_OWNER_IP;
+ DummyBuf.ipr_size = 0;
+ DummyBuf.ipr_next = 0;
+ DummyBuf.ipr_buffer = NULL;
+ return TRUE;
+}
+
+//* UnInitTCPRcv - Uninitialize our receive side.
+//
+// Called if initialization fails to uninitialize our receive side.
+//
+//
+// Input: Nothing.
+//
+// Returns: Nothing.
+//
+void
+UnInitTCPRcv(void)
+{
+
+}
+
+
+#pragma END_INIT
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/tcprcv.h b/private/ntos/tdi/tcpip/tcp/tcprcv.h
new file mode 100644
index 000000000..629eb5cf8
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcprcv.h
@@ -0,0 +1,74 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCPRCV.H - TCP receive protocol definitions.
+//
+// This file contains the definitions for structures used by the receive code.
+//
+
+#define CONN_REQUEST_COMPLETE 0x01
+#define SEND_REQUEST_COMPLETE 0x02
+
+#define IN_RCV_COMPLETE 0x10
+#define ANY_REQUEST_COMPLETE (CONN_REQUEST_COMPLETE | SEND_REQUEST_COMPLETE)
+
+#define trh_signature 0x20485254 // 'TRH '
+
+typedef struct TCPRAHdr {
+#ifdef DEBUG
+ ulong trh_sig; // Signature.
+#endif
+ struct TCPRAHdr *trh_next; // Next pointer.
+ SeqNum trh_start; // First sequence number.
+ uint trh_size; // Size in bytes of data in this TRH.
+ uint trh_flags; // Flags for this segment.
+ uint trh_urg; // Urgent pointer from this seg.
+ IPRcvBuf *trh_buffer; // Head of buffer list for this TRH.
+ IPRcvBuf *trh_end; // End of buffer list for this TRH.
+
+} TCPRAHdr;
+
+//* Structure of a TCP receive request.
+
+#define trr_signature 0x20525254 // 'TRR '
+
+typedef struct TCPRcvReq {
+#ifdef DEBUG
+ ulong trr_sig; // Signature.
+#endif
+ struct TCPRcvReq *trr_next; // Next in chain.
+ CTEReqCmpltRtn trr_rtn; // Completion routine.
+ PVOID trr_context; // User context.
+ uint trr_amt; // Number of bytes currently in buffer.
+ uint trr_offset; // Offset into first buffer on chain
+ // at which to start copying.
+ uint trr_flags; // Flags for this recv.
+ ushort *trr_uflags; // Pointer to user specifed flags.
+ uint trr_size; // Total size of buffer chain.
+ PNDIS_BUFFER trr_buffer; // Pointer to useable NDIS buffer chain.
+} TCPRcvReq;
+
+#define TRR_PUSHED 0x80000000 // This buffer has been pushed.
+
+
+extern uint RequestCompleteFlags;
+
+extern Queue SendCompleteQ;
+extern Queue TCBDelayQ;
+
+EXTERNAL_LOCK(RequestCompleteLock)
+EXTERNAL_LOCK(TCBDelayLock)
+
+extern void TCPRcvComplete(void);
+extern void FreeRBChain(IPRcvBuf *RBChain);
+
+extern void DelayAction(struct TCB *DelayTCB, uint Action);
+extern void ProcessTCBDelayQ(void);
+extern void AdjustRcvWin(struct TCB *WinTCB);
+
+
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/tcpsend.c b/private/ntos/tdi/tcpip/tcp/tcpsend.c
new file mode 100644
index 000000000..6c97e9f8a
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcpsend.c
@@ -0,0 +1,2666 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCPSEND.C - TCP send protocol code.
+//
+// This file contains the code for sending Data and Control segments.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#ifdef VXD
+#include "tdivxd.h"
+#include "tdistat.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "tcp.h"
+#include "tcb.h"
+#include "tcpconn.h"
+#include "tcpsend.h"
+#include "tcprcv.h"
+#include "tlcommon.h"
+#include "info.h"
+#include "tcpcfg.h"
+#include "secfltr.h"
+
+#if FAST_RETRANSMIT
+void
+TCPFastSend(TCB *SendTCB,
+ PNDIS_BUFFER in_SendBuf,
+ uint in_SendOfs,
+ TCPSendReq *in_SendReq,
+ uint in_SendSize);
+#endif
+
+
+
+#ifdef NT
+SLIST_HEADER TCPSendFree;
+#else
+PNDIS_BUFFER TCPSendFree;
+#endif
+
+DEFINE_LOCK_STRUCTURE(TCPSendFreeLock);
+
+ulong TCPCurrentSendFree;
+ulong TCPMaxSendFree = TCP_MAX_HDRS;
+
+void *TCPProtInfo; // TCP protocol info for IP.
+
+#ifdef NT
+SLIST_HEADER TCPSendReqFree; // Send req. free list.
+#else
+TCPSendReq *TCPSendReqFree; // Send req. free list.
+#endif
+
+DEFINE_LOCK_STRUCTURE(TCPSendReqFreeLock);
+DEFINE_LOCK_STRUCTURE(TCPSendReqCompleteLock);
+uint NumTCPSendReq; // Current number of SendReqs in system.
+uint MaxSendReq = 0xffffffff; // Maximum allowed number of SendReqs.
+
+
+NDIS_HANDLE TCPSendBufferPool;
+
+typedef struct TCPHdrBPoolEntry {
+ struct TCPHdrBPoolEntry *the_next;
+ NDIS_HANDLE the_handle;
+ uchar *the_buffer;
+} TCPHdrBPoolEntry;
+
+TCPHdrBPoolEntry *TCPHdrBPoolList;
+
+extern IPInfo LocalNetInfo;
+
+#ifdef CHICAGO
+extern uchar TransportName[];
+#endif // CHICAGO
+
+EXTERNAL_LOCK(TCBTableLock)
+
+//
+// All of the init code can be discarded.
+//
+#ifdef NT
+
+int InitTCPSend(void);
+void UnInitTCPSend(void);
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(INIT, InitTCPSend)
+#pragma alloc_text(INIT, UnInitTCPSend)
+
+#endif // ALLOC_PRAGMA
+#endif
+
+#ifdef CHICAGO
+extern int RegisterAddrChangeHndlr(void *Handler, uint Add);
+extern void AddrChange(IPAddr Addr, IPMask Mask, void *Context,
+ uint Added);
+#endif
+
+extern void ResetSendNext(TCB *SeqTCB, SeqNum NewSeq);
+
+
+//* GrowTCPHeaderList - Try to grow the tcp header list.
+//
+// Called when we run out of buffers on the TCP header list, and need
+// to grow it. We look to see if we're already at the maximum size, and
+// if not we'll allocate the need structures and free them to the list.
+//
+// Input: Nothing.
+//
+// Returns: A pointer to a new TCP header buffer if we have one, or NULL.
+//
+PNDIS_BUFFER
+GrowTCPHeaderList(void)
+{
+ TCPHdrBPoolEntry *NewEntry;
+ CTELockHandle Handle;
+ NDIS_STATUS Status;
+ uint HeaderSize;
+ uchar *TCPSendHP;
+ uint i;
+ PNDIS_BUFFER Buffer;
+ PNDIS_BUFFER ReturnBuffer;
+
+ CTEGetLock(&TCPSendFreeLock, &Handle);
+
+ if (TCPCurrentSendFree < TCPMaxSendFree) {
+
+ // Still room to grow the list.
+ NewEntry = CTEAllocMem(sizeof(TCPHdrBPoolEntry));
+
+ if (NewEntry == NULL) {
+ // Couldn't get the memory.
+ CTEFreeLock(&TCPSendFreeLock, Handle);
+ return NULL;
+ }
+
+
+ NdisAllocateBufferPool(&Status, &NewEntry->the_handle,
+ NUM_TCP_HEADERS);
+
+ if (Status != NDIS_STATUS_SUCCESS) {
+ // Couldn't get a new set of buffers. Fail.
+ CTEFreeMem(NewEntry);
+ CTEFreeLock(&TCPSendFreeLock, Handle);
+ return NULL;
+ }
+
+ HeaderSize = sizeof(TCPHeader) +
+ MAX(MSS_OPT_SIZE, sizeof(SendCmpltContext)) +
+ LocalNetInfo.ipi_hsize;
+
+ TCPSendHP = CTEAllocMem(HeaderSize * NUM_TCP_HEADERS);
+
+ if (TCPSendHP == NULL) {
+ NdisFreeBufferPool(NewEntry->the_handle);
+ CTEFreeMem(NewEntry);
+ CTEFreeLock(&TCPSendFreeLock, Handle);
+ return NULL;
+ }
+
+ NewEntry->the_buffer = TCPSendHP;
+ TCPCurrentSendFree += NUM_TCP_HEADERS;
+ NewEntry->the_next = TCPHdrBPoolList;
+ TCPHdrBPoolList = NewEntry;
+ ReturnBuffer = NULL;
+ CTEFreeLock(&TCPSendFreeLock, Handle);
+
+ for (i = 0; i < NUM_TCP_HEADERS; i++) {
+ NdisAllocateBuffer(&Status, &Buffer, NewEntry->the_handle,
+ TCPSendHP + (i * HeaderSize), HeaderSize);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ CTEAssert(FALSE); // This is probably harmless, but check.
+ break;
+ }
+
+ NdisBufferLength(Buffer) = sizeof(TCPHeader);
+
+ if (i != 0)
+ FreeTCPHeader(Buffer);
+ else
+ ReturnBuffer = Buffer;
+ }
+
+ // Update the count with what we didn't allocate, if any.
+ CTEInterlockedAddUlong(&TCPCurrentSendFree, i - NUM_TCP_HEADERS,
+ &TCPSendFreeLock);
+
+ return ReturnBuffer;
+
+ } else {
+ // At the limit already. It's possible someone snuck in and grew
+ // the list before we got to, so check and see if it's still empty.
+#ifdef VXD
+ if (TCPSendFree != NULL) {
+ ReturnBuffer = TCPSendFree;
+ TCPSendFree = NDIS_BUFFER_LINKAGE(ReturnBuffer);
+
+#else
+ PSINGLE_LIST_ENTRY BufferLink;
+
+ CTEFreeLock(&TCPSendFreeLock, Handle);
+
+ BufferLink = ExInterlockedPopEntrySList(
+ &TCPSendFree,
+ &TCPSendFreeLock
+ );
+
+ if (BufferLink != NULL) {
+ ReturnBuffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next);
+ } else
+ ReturnBuffer = NULL;
+
+ return ReturnBuffer;
+#endif
+#ifdef VXD
+ } else
+ ReturnBuffer = NULL;
+#endif
+
+ }
+
+ CTEFreeLock(&TCPSendFreeLock, Handle);
+ return ReturnBuffer;
+
+
+}
+
+//* GetTCPHeader - Get a TCP header buffer.
+//
+// Called when we need to get a TCP header buffer. This routine is
+// specific to the particular environment (VxD or NT). All we
+// need to do is pop the buffer from the free list.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to an NDIS buffer, or NULL is none.
+//
+PNDIS_BUFFER
+GetTCPHeader(void)
+{
+ PNDIS_BUFFER NewBuffer;
+
+#ifdef VXD
+ NewBuffer = TCPSendFree;
+ if (NewBuffer != NULL) {
+ TCPSendFree = NDIS_BUFFER_LINKAGE(NewBuffer);
+ return NewBuffer;
+ }
+
+#else
+
+ PSINGLE_LIST_ENTRY BufferLink;
+
+ BufferLink = ExInterlockedPopEntrySList(
+ &TCPSendFree,
+ &TCPSendFreeLock
+ );
+ if (BufferLink != NULL) {
+ NewBuffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next);
+ return NewBuffer;
+ }
+#endif
+ else
+ return GrowTCPHeaderList();
+}
+
+//* FreeTCPHeader - Free a TCP header buffer.
+//
+// Called to free a TCP header buffer.
+//
+// Input: Buffer to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeTCPHeader(PNDIS_BUFFER FreedBuffer)
+{
+
+ CTEAssert(FreedBuffer != NULL);
+
+ NdisBufferLength(FreedBuffer) = sizeof(TCPHeader);
+
+#ifdef VXD
+
+ NDIS_BUFFER_LINKAGE(FreedBuffer) = TCPSendFree;
+ TCPSendFree = FreedBuffer;
+
+#else
+
+ ExInterlockedPushEntrySList(
+ &TCPSendFree,
+ STRUCT_OF(SINGLE_LIST_ENTRY, &(FreedBuffer->Next), Next),
+ &TCPSendFreeLock
+ );
+
+#endif
+}
+
+//* FreeSendReq - Free a send request structure.
+//
+// Called to free a send request structure.
+//
+// Input: FreedReq - Connection request structure to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeSendReq(TCPSendReq *FreedReq)
+{
+#ifdef NT
+ PSINGLE_LIST_ENTRY BufferLink;
+
+ CTEStructAssert(FreedReq, tsr);
+
+ BufferLink = STRUCT_OF(
+ SINGLE_LIST_ENTRY,
+ &(FreedReq->tsr_req.tr_q.q_next),
+ Next
+ );
+
+ ExInterlockedPushEntrySList(
+ &TCPSendReqFree,
+ BufferLink,
+ &TCPSendReqFreeLock
+ );
+
+#else // NT
+
+ TCPSendReq **Temp;
+
+ CTEStructAssert(FreedReq, tsr);
+
+ Temp = (TCPSendReq **)&FreedReq->tsr_req.tr_q.q_next;
+ *Temp = TCPSendReqFree;
+ TCPSendReqFree = FreedReq;
+
+#endif // NT
+}
+
+//* GetSendReq - Get a send request structure.
+//
+// Called to get a send request structure.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to SendReq structure, or NULL if none.
+//
+TCPSendReq *
+GetSendReq(void)
+{
+ TCPSendReq *Temp;
+
+#ifdef NT
+ PSINGLE_LIST_ENTRY BufferLink;
+ Queue *QueuePtr;
+ TCPReq *ReqPtr;
+
+
+ BufferLink = ExInterlockedPopEntrySList(
+ &TCPSendReqFree,
+ &TCPSendReqFreeLock
+ );
+
+ if (BufferLink != NULL) {
+ QueuePtr = STRUCT_OF(Queue, BufferLink, q_next);
+ ReqPtr = STRUCT_OF(TCPReq, QueuePtr, tr_q);
+ Temp = STRUCT_OF(TCPSendReq, ReqPtr, tsr_req);
+ CTEStructAssert(Temp, tsr);
+ }
+ else {
+ if (NumTCPSendReq < MaxSendReq)
+ Temp = CTEAllocMem(sizeof(TCPSendReq));
+ else
+ Temp = NULL;
+
+ if (Temp != NULL) {
+ ExInterlockedAddUlong(&NumTCPSendReq, 1, &TCPSendReqFreeLock);
+#ifdef DEBUG
+ Temp->tsr_req.tr_sig = tr_signature;
+ Temp->tsr_sig = tsr_signature;
+#endif
+ }
+ }
+
+#else // NT
+
+ Temp = TCPSendReqFree;
+ if (Temp != NULL)
+ TCPSendReqFree = (TCPSendReq *)Temp->tsr_req.tr_q.q_next;
+ else {
+ if (NumTCPSendReq < MaxSendReq)
+ Temp = CTEAllocMem(sizeof(TCPSendReq));
+ else
+ Temp = NULL;
+
+ if (Temp != NULL) {
+ NumTCPSendReq++;
+#ifdef DEBUG
+ Temp->tsr_req.tr_sig = tr_signature;
+ Temp->tsr_sig = tsr_signature;
+#endif
+ }
+ }
+
+#endif // NT
+
+ return Temp;
+}
+
+
+
+//* TCPSendComplete - Complete a TCP send.
+//
+// Called by IP when a send we've made is complete. We free the buffer,
+// and possibly complete some sends. Each send queued on a TCB has a ref.
+// count with it, which is the number of times a pointer to a buffer
+// associated with the send has been passed to the underlying IP layer. We
+// can't complete a send until that count it 0. If this send was actually
+// from a send of data, we'll go down the chain of send and decrement the
+// refcount on each one. If we have one going to 0 and the send has already
+// been acked we'll complete the send. If it hasn't been acked we'll leave
+// it until the ack comes in.
+//
+// NOTE: We aren't protecting any of this with locks. When we port this to
+// NT we'll need to fix this, probably with a global lock. See the comments
+// in ACKSend() in TCPRCV.C for more details.
+//
+// Input: Context - Context we gave to IP.
+// BufferChain - BufferChain for send.
+//
+// Returns: Nothing.
+//
+void
+TCPSendComplete(void *Context, PNDIS_BUFFER BufferChain)
+{
+ CTELockHandle SendHandle;
+ PNDIS_BUFFER CurrentBuffer;
+
+
+ if (Context != NULL) {
+ SendCmpltContext *SCContext = (SendCmpltContext *)Context;
+ TCPSendReq *CurrentSend;
+ uint i;
+
+ CTEStructAssert(SCContext, scc);
+
+ // First, loop through and free any NDIS buffers here that need to be.
+ // freed. We'll skip any 'user' buffers, and then free our buffers. We
+ // need to do this before decrementing the reference count to avoid
+ // destroying the buffer chain if we have to zap tsr_lastbuf->Next to
+ // NULL.
+
+ CurrentBuffer = NDIS_BUFFER_LINKAGE(BufferChain);
+ for (i = 0; i < (uint)SCContext->scc_ubufcount; i++) {
+ CTEAssert(CurrentBuffer != NULL);
+ CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
+ }
+
+ for (i = 0; i < (uint)SCContext->scc_tbufcount; i++) {
+ PNDIS_BUFFER TempBuffer;
+
+ CTEAssert(CurrentBuffer != NULL);
+
+ TempBuffer = CurrentBuffer;
+ CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
+ NdisFreeBuffer(TempBuffer);
+ }
+
+ CurrentSend = SCContext->scc_firstsend;
+
+ i = 0;
+ while (i < SCContext->scc_count) {
+ Queue *TempQ;
+ long Result;
+
+
+ TempQ = QNEXT(&CurrentSend->tsr_req.tr_q);
+
+ CTEStructAssert(CurrentSend, tsr);
+
+ Result = CTEInterlockedDecrementLong(
+ &(CurrentSend->tsr_refcnt)
+ );
+
+ CTEAssert(Result >= 0);
+
+ if (Result <= 0) {
+
+ // Reference count has gone to 0 which means the send has
+ // been ACK'd or cancelled. Complete it now.
+
+ // If we've sent directly from this send, NULL out the next
+ // pointer for the last buffer in the chain.
+ if (CurrentSend->tsr_lastbuf != NULL) {
+ NDIS_BUFFER_LINKAGE(CurrentSend->tsr_lastbuf) = NULL;
+ CurrentSend->tsr_lastbuf = NULL;
+ }
+
+ CTEGetLock(&RequestCompleteLock, &SendHandle);
+ ENQUEUE(&SendCompleteQ, &CurrentSend->tsr_req.tr_q);
+ RequestCompleteFlags |= SEND_REQUEST_COMPLETE;
+ CTEFreeLock(&RequestCompleteLock, SendHandle);
+ }
+
+ CurrentSend = STRUCT_OF(TCPSendReq, QSTRUCT(TCPReq, TempQ, tr_q),
+ tsr_req);
+
+ i++;
+ }
+
+ }
+
+ FreeTCPHeader(BufferChain);
+
+ if (RequestCompleteFlags & SEND_REQUEST_COMPLETE)
+ TCPRcvComplete();
+
+}
+
+//* RcvWin - Figure out the receive window to offer in an ack.
+//
+// A routine to figure out what window to offer on a connection. We
+// take into account SWS avoidance, what the default connection window is,
+// and what the last window we offered is.
+//
+// Input: WinTCB - TCB on which to perform calculations.
+//
+// Returns: Window to be offered.
+//
+uint
+RcvWin(TCB *WinTCB)
+{
+ int CouldOffer; // The window size we could offer.
+
+ CTEStructAssert(WinTCB, tcb);
+
+ CheckRBList(WinTCB->tcb_pendhead, WinTCB->tcb_pendingcnt);
+
+ CTEAssert(WinTCB->tcb_rcvwin >= 0);
+
+ CouldOffer = WinTCB->tcb_defaultwin - WinTCB->tcb_pendingcnt;
+
+ CTEAssert(CouldOffer >= 0);
+ CTEAssert(CouldOffer >= WinTCB->tcb_rcvwin);
+
+ if ((CouldOffer - WinTCB->tcb_rcvwin) >= (int) MIN(WinTCB->tcb_defaultwin/2,
+ WinTCB->tcb_mss))
+ WinTCB->tcb_rcvwin = CouldOffer;
+
+ return WinTCB->tcb_rcvwin;
+}
+
+//* SendSYN - Send a SYN segment.
+//
+// This is called during connection establishment time to send a SYN
+// segment to the peer. We get a buffer if we can, and then fill
+// it in. There's a tricky part here where we have to build the MSS
+// option in the header - we find the MSS by finding the MSS offered
+// by the net for the local address. After that, we send it.
+//
+// Input: SYNTcb - TCB from which SYN is to be sent.
+// TCBHandle - Handle for lock on TCB.
+//
+// Returns: Nothing.
+//
+void
+SendSYN(TCB *SYNTcb, CTELockHandle TCBHandle)
+{
+ PNDIS_BUFFER HeaderBuffer;
+ TCPHeader *SYNHeader;
+ uchar *OptPtr;
+ IP_STATUS SendStatus;
+
+ CTEStructAssert(SYNTcb, tcb);
+
+ HeaderBuffer = GetTCPHeader();
+
+ // Go ahead and set the retransmission timer now, in case we didn't get a
+ // buffer. In the future we might want to queue the connection for
+ // when we free a buffer.
+ START_TCB_TIMER(SYNTcb->tcb_rexmittimer, SYNTcb->tcb_rexmit);
+ if (HeaderBuffer != NULL) {
+ ushort TempWin;
+ ushort MSS;
+ uchar FoundMSS;
+
+ SYNHeader = (TCPHeader *)(
+ (uchar *)NdisBufferVirtualAddress(HeaderBuffer) +
+ LocalNetInfo.ipi_hsize);
+
+ NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL;
+ NdisBufferLength(HeaderBuffer) = sizeof(TCPHeader) + MSS_OPT_SIZE;
+ SYNHeader->tcp_src = SYNTcb->tcb_sport;
+ SYNHeader->tcp_dest = SYNTcb->tcb_dport;
+ SYNHeader->tcp_seq = net_long(SYNTcb->tcb_sendnext);
+ SYNTcb->tcb_sendnext++;
+
+ if (SEQ_GT(SYNTcb->tcb_sendnext, SYNTcb->tcb_sendmax)) {
+ TStats.ts_outsegs++;
+ SYNTcb->tcb_sendmax = SYNTcb->tcb_sendnext;
+ } else
+ TStats.ts_retranssegs++;
+
+ SYNHeader->tcp_ack = net_long(SYNTcb->tcb_rcvnext);
+ if (SYNTcb->tcb_state == TCB_SYN_RCVD) {
+ SYNHeader->tcp_flags = MAKE_TCP_FLAGS(6, TCP_FLAG_SYN | TCP_FLAG_ACK);
+#ifdef SYN_ATTACK
+ //
+ // if this is the second time we are trying to send the SYN-ACK,
+ // increment the count of retried half-connections
+ //
+ if (SynAttackProtect && (SYNTcb->tcb_rexmitcnt == ADAPTED_MAX_CONNECT_RESPONSE_REXMIT_CNT)) {
+ CTEInterlockedAddUlong(&TCPHalfOpenRetried, 1, &SynAttLock);
+ }
+#endif
+ } else {
+ SYNHeader->tcp_flags = MAKE_TCP_FLAGS(6, TCP_FLAG_SYN);
+ }
+
+ TempWin = (ushort)SYNTcb->tcb_rcvwin;
+ SYNHeader->tcp_window = net_short(TempWin);
+ SYNHeader->tcp_xsum = 0;
+ OptPtr = (uchar *)(SYNHeader + 1);
+ FoundMSS = (*LocalNetInfo.ipi_getlocalmtu)(SYNTcb->tcb_saddr, &MSS);
+
+ if (!FoundMSS) {
+ DEBUGCHK;
+ CTEFreeLock(&SYNTcb->tcb_lock, TCBHandle);
+ FreeTCPHeader(HeaderBuffer);
+ return;
+ }
+
+ MSS -= sizeof(TCPHeader);
+
+ *OptPtr++ = TCP_OPT_MSS;
+ *OptPtr++ = MSS_OPT_SIZE;
+ **(ushort **)&OptPtr = net_short(MSS);
+
+ SYNTcb->tcb_refcnt++;
+ SYNHeader->tcp_xsum = ~XsumSendChain(SYNTcb->tcb_phxsum +
+ (uint)net_short(sizeof(TCPHeader) + MSS_OPT_SIZE), HeaderBuffer);
+ CTEFreeLock(&SYNTcb->tcb_lock, TCBHandle);
+
+
+ SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer,
+ sizeof(TCPHeader) + MSS_OPT_SIZE, SYNTcb->tcb_daddr,
+ SYNTcb->tcb_saddr, &SYNTcb->tcb_opt, SYNTcb->tcb_rce,
+ PROTOCOL_TCP);
+
+ SYNTcb->tcb_error = SendStatus;
+ if (SendStatus != IP_PENDING) {
+ FreeTCPHeader(HeaderBuffer);
+ }
+
+ CTEGetLock(&SYNTcb->tcb_lock, &TCBHandle);
+ DerefTCB(SYNTcb, TCBHandle);
+
+ } else {
+ CTEFreeLock(&SYNTcb->tcb_lock, TCBHandle);
+ return;
+ }
+
+}
+
+//* SendKA - Send a keep alive segment.
+//
+// This is called when we want to send a keep alive.
+//
+// Input: KATcb - TCB from which keep alive is to be sent.
+// Handle - Handle for lock on TCB.
+//
+// Returns: Nothing.
+//
+void
+SendKA(TCB *KATcb, CTELockHandle Handle)
+{
+ PNDIS_BUFFER HeaderBuffer;
+ TCPHeader *Header;
+ IP_STATUS SendStatus;
+
+ CTEStructAssert(KATcb, tcb);
+
+ HeaderBuffer = GetTCPHeader();
+
+ if (HeaderBuffer != NULL) {
+ ushort TempWin;
+ SeqNum TempSeq;
+
+ Header = (TCPHeader *)(
+ (uchar *)NdisBufferVirtualAddress(HeaderBuffer) +
+ LocalNetInfo.ipi_hsize);
+
+ NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL;
+ NdisBufferLength(HeaderBuffer) = sizeof(TCPHeader) + 1;
+ Header->tcp_src = KATcb->tcb_sport;
+ Header->tcp_dest = KATcb->tcb_dport;
+ TempSeq = KATcb->tcb_senduna - 1;
+ Header->tcp_seq = net_long(TempSeq);
+
+ TStats.ts_retranssegs++;
+
+ Header->tcp_ack = net_long(KATcb->tcb_rcvnext);
+ Header->tcp_flags = MAKE_TCP_FLAGS(5, TCP_FLAG_ACK);
+
+ TempWin = (ushort)RcvWin(KATcb);
+ Header->tcp_window = net_short(TempWin);
+ Header->tcp_xsum = 0;
+
+ Header->tcp_xsum = ~XsumSendChain(KATcb->tcb_phxsum +
+ (uint)net_short(sizeof(TCPHeader) + 1), HeaderBuffer);
+
+ KATcb->tcb_kacount++;
+ CTEFreeLock(&KATcb->tcb_lock, Handle);
+
+
+ SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer,
+ sizeof(TCPHeader) + 1, KATcb->tcb_daddr,
+ KATcb->tcb_saddr, &KATcb->tcb_opt, KATcb->tcb_rce, PROTOCOL_TCP);
+
+ if (SendStatus != IP_PENDING) {
+ FreeTCPHeader(HeaderBuffer);
+ }
+
+
+ } else {
+ CTEFreeLock(&KATcb->tcb_lock, Handle);
+ }
+
+}
+
+//* SendACK - Send an ACK segment.
+//
+// This is called whenever we need to send an ACK for some reason. Nothing
+// fancy, we just do it.
+//
+// Input: ACKTcb - TCB from which ACK is to be sent.
+//
+// Returns: Nothing.
+//
+void
+SendACK(TCB *ACKTcb)
+{
+ PNDIS_BUFFER HeaderBuffer;
+ TCPHeader *ACKHeader;
+ IP_STATUS SendStatus;
+ CTELockHandle TCBHandle;
+ SeqNum SendNext;
+
+ CTEStructAssert(ACKTcb, tcb);
+
+ if ((ACKTcb->tcb_state == TCB_TIME_WAIT) &&
+ (ACKTcb->tcb_flags & TW_PENDING)) {
+ CTEGetLock(&ACKTcb->tcb_lock, &TCBHandle);
+ STOP_TCB_TIMER(ACKTcb->tcb_delacktimer);
+ ACKTcb->tcb_flags &= ~(NEED_ACK | ACK_DELAYED);
+ ACKTcb->tcb_error = IP_SUCCESS;
+ CTEFreeLock(&ACKTcb->tcb_lock, TCBHandle);
+ return;
+ }
+
+ HeaderBuffer = GetTCPHeader();
+
+
+ if (HeaderBuffer != NULL) {
+ ushort TempWin;
+
+ CTEGetLock(&ACKTcb->tcb_lock, &TCBHandle);
+
+ ACKHeader = (TCPHeader *)(
+ (uchar *)NdisBufferVirtualAddress(HeaderBuffer) +
+ LocalNetInfo.ipi_hsize);
+ NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL;
+
+ ACKHeader->tcp_src = ACKTcb->tcb_sport;
+ ACKHeader->tcp_dest = ACKTcb->tcb_dport;
+ ACKHeader->tcp_ack = net_long(ACKTcb->tcb_rcvnext);
+
+ // If the remote peer is advertising a window of zero, we need to
+ // send this ack with a seq. number of his rcv_next (which in that case
+ // should be our senduna). We have code here ifdef'd out that makes
+ // sure that we don't send outside the RWE, but this doesn't work. We
+ // need to be able to send a pure ACK exactly at the RWE.
+
+ if (ACKTcb->tcb_sendwin != 0) {
+ SeqNum MaxValidSeq;
+
+ SendNext = ACKTcb->tcb_sendnext;
+#if 0
+ MaxValidSeq = ACKTcb->tcb_senduna + ACKTcb->tcb_sendwin - 1;
+
+ SendNext = (SEQ_LT(SendNext, MaxValidSeq) ? SendNext : MaxValidSeq);
+#endif
+
+ } else
+ SendNext = ACKTcb->tcb_senduna;
+
+ if ((ACKTcb->tcb_flags & FIN_SENT) &&
+ SEQ_EQ(SendNext, ACKTcb->tcb_sendmax - 1)) {
+ ACKHeader->tcp_flags = MAKE_TCP_FLAGS(5,
+ TCP_FLAG_FIN | TCP_FLAG_ACK);
+ } else
+ ACKHeader->tcp_flags = MAKE_TCP_FLAGS(5, TCP_FLAG_ACK);
+
+ ACKHeader->tcp_seq = net_long(SendNext);
+
+ TempWin = (ushort)RcvWin(ACKTcb);
+ ACKHeader->tcp_window = net_short(TempWin);
+ ACKHeader->tcp_xsum = 0;
+
+ ACKHeader->tcp_xsum = ~XsumSendChain(ACKTcb->tcb_phxsum +
+ (uint)net_short(sizeof(TCPHeader)), HeaderBuffer);
+
+ STOP_TCB_TIMER(ACKTcb->tcb_delacktimer);
+ ACKTcb->tcb_flags &= ~(NEED_ACK | ACK_DELAYED);
+
+ if (ACKTcb->tcb_flags & TW_PENDING) {
+ ACKTcb->tcb_state = TCB_TIME_WAIT;
+ }
+
+ CTEFreeLock(&ACKTcb->tcb_lock, TCBHandle);
+
+ TStats.ts_outsegs++;
+ SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer,
+ sizeof(TCPHeader), ACKTcb->tcb_daddr, ACKTcb->tcb_saddr,
+ &ACKTcb->tcb_opt, ACKTcb->tcb_rce, PROTOCOL_TCP);
+
+ ACKTcb->tcb_error = SendStatus;
+ if (SendStatus != IP_PENDING)
+ FreeTCPHeader(HeaderBuffer);
+ }
+
+ return;
+
+
+}
+
+//* SendRSTFromTCB - Send a RST from a TCB.
+//
+// This is called during close when we need to send a RST.
+//
+// Input: RSTTcb - TCB from which RST is to be sent.
+//
+// Returns: Nothing.
+//
+void
+SendRSTFromTCB(TCB *RSTTcb)
+{
+ PNDIS_BUFFER HeaderBuffer;
+ TCPHeader *RSTHeader;
+ IP_STATUS SendStatus;
+
+ CTEStructAssert(RSTTcb, tcb);
+
+ CTEAssert(RSTTcb->tcb_state == TCB_CLOSED);
+
+ HeaderBuffer = GetTCPHeader();
+
+
+ if (HeaderBuffer != NULL) {
+ SeqNum RSTSeq;
+
+ RSTHeader = (TCPHeader *)(
+ (uchar *)NdisBufferVirtualAddress(HeaderBuffer) +
+ LocalNetInfo.ipi_hsize);
+ NDIS_BUFFER_LINKAGE(HeaderBuffer) = NULL;
+
+ RSTHeader->tcp_src = RSTTcb->tcb_sport;
+ RSTHeader->tcp_dest = RSTTcb->tcb_dport;
+
+ // If the remote peer has a window of 0, send with a seq. # equal
+ // to senduna so he'll accept it. Otherwise send with send max.
+ if (RSTTcb->tcb_sendwin != 0)
+ RSTSeq = RSTTcb->tcb_sendmax;
+ else
+ RSTSeq = RSTTcb->tcb_senduna;
+
+ RSTHeader->tcp_seq = net_long(RSTSeq);
+ RSTHeader->tcp_flags = MAKE_TCP_FLAGS(sizeof(TCPHeader)/sizeof(ulong),
+ TCP_FLAG_RST);
+
+ RSTHeader->tcp_window = 0;
+ RSTHeader->tcp_xsum = 0;
+
+ RSTHeader->tcp_xsum = ~XsumSendChain(RSTTcb->tcb_phxsum +
+ (uint)net_short(sizeof(TCPHeader)), HeaderBuffer);
+
+ TStats.ts_outsegs++;
+ TStats.ts_outrsts++;
+ SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, HeaderBuffer,
+ sizeof(TCPHeader), RSTTcb->tcb_daddr, RSTTcb->tcb_saddr,
+ &RSTTcb->tcb_opt, RSTTcb->tcb_rce, PROTOCOL_TCP);
+
+ if (SendStatus != IP_PENDING)
+ FreeTCPHeader(HeaderBuffer);
+ }
+
+ return;
+
+
+}
+//* SendRSTFromHeader - Send a RST back, based on a header.
+//
+// Called when we need to send a RST, but don't necessarily have a TCB.
+//
+// Input: TCPH - TCP header to be RST.
+// Length - Length of the incoming segment.
+// Dest - Destination IP address for RST.
+// Src - Source IP address for RST.
+// OptInfo - IP Options to use on RST.
+//
+// Returns: Nothing.
+//
+void
+SendRSTFromHeader(TCPHeader UNALIGNED *TCPH, uint Length, IPAddr Dest,
+ IPAddr Src, IPOptInfo *OptInfo)
+{
+ PNDIS_BUFFER Buffer;
+ TCPHeader *RSTHdr;
+ IPOptInfo NewInfo;
+ IP_STATUS SendStatus;
+
+ if (TCPH->tcp_flags & TCP_FLAG_RST)
+ return;
+
+ Buffer = GetTCPHeader();
+
+ if (Buffer != NULL) {
+ // Got a buffer. Fill in the header so as to make it believable to
+ // the remote guy, and send it.
+
+ RSTHdr = (TCPHeader *)((uchar *)NdisBufferVirtualAddress(Buffer) +
+ LocalNetInfo.ipi_hsize);
+
+ NDIS_BUFFER_LINKAGE(Buffer) = NULL;
+
+ if (TCPH->tcp_flags & TCP_FLAG_SYN)
+ Length++;
+
+ if (TCPH->tcp_flags & TCP_FLAG_FIN)
+ Length++;
+
+ if (TCPH->tcp_flags & TCP_FLAG_ACK) {
+ RSTHdr->tcp_seq = TCPH->tcp_ack;
+ RSTHdr->tcp_flags = MAKE_TCP_FLAGS(sizeof(TCPHeader)/sizeof(ulong),
+ TCP_FLAG_RST);
+ } else {
+ SeqNum TempSeq;
+
+ RSTHdr->tcp_seq = 0;
+ TempSeq = net_long(TCPH->tcp_seq);
+ TempSeq += Length;
+ RSTHdr->tcp_ack = net_long(TempSeq);
+ RSTHdr->tcp_flags = MAKE_TCP_FLAGS(sizeof(TCPHeader)/sizeof(ulong),
+ TCP_FLAG_RST | TCP_FLAG_ACK);
+ }
+
+ RSTHdr->tcp_window = 0;
+ RSTHdr->tcp_dest = TCPH->tcp_src;
+ RSTHdr->tcp_src = TCPH->tcp_dest;
+ RSTHdr->tcp_xsum = 0;
+
+ RSTHdr->tcp_xsum = ~XsumSendChain(PHXSUM(Src, Dest, PROTOCOL_TCP,
+ sizeof(TCPHeader)), Buffer);
+
+ (*LocalNetInfo.ipi_initopts)(&NewInfo);
+
+ if (OptInfo->ioi_options != NULL)
+ (*LocalNetInfo.ipi_updateopts)(OptInfo, &NewInfo, Dest, NULL_IP_ADDR);
+
+ TStats.ts_outsegs++;
+ TStats.ts_outrsts++;
+ SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, NULL, Buffer,
+ sizeof(TCPHeader), Dest, Src, &NewInfo, NULL, PROTOCOL_TCP);
+
+ if (SendStatus != IP_PENDING)
+ FreeTCPHeader(Buffer);
+
+ (*LocalNetInfo.ipi_freeopts)(&NewInfo);
+ }
+
+}
+//* GoToEstab - Transition to the established state.
+//
+// Called when we are going to the established state and need to finish up
+// initializing things that couldn't be done until now. We assume the TCB
+// lock is held by the caller on the TCB we're called with.
+//
+// Input: EstabTCB - TCB to transition.
+//
+// Returns: Nothing.
+//
+void
+GoToEstab(TCB *EstabTCB)
+{
+
+ // Initialize our slow start and congestion control variables.
+ EstabTCB->tcb_cwin = 2 * EstabTCB->tcb_mss;
+ EstabTCB->tcb_ssthresh = 0xffffffff;
+
+ EstabTCB->tcb_state = TCB_ESTAB;
+
+ // We're in established. We'll subtract one from slow count for this fact,
+ // and if the slowcount goes to 0 we'll move onto the fast path.
+
+ if (--(EstabTCB->tcb_slowcount) == 0)
+ EstabTCB->tcb_fastchk &= ~TCP_FLAG_SLOW;
+
+ TStats.ts_currestab++;
+
+ EstabTCB->tcb_flags &= ~ACTIVE_OPEN; // Turn off the active opening flag.
+
+}
+
+//* InitSendState - Initialize the send state of a connection.
+//
+// Called during connection establishment to initialize our send state.
+// (In this case, this refers to all information we'll put on the wire as
+// well as pure send state). We pick an ISS, set up a rexmit timer value,
+// etc. We assume the tcb_lock is held on the TCB when we are called.
+//
+// Input: NewTCB - TCB to be set up.
+//
+// Returns: Nothing.
+void
+InitSendState(TCB *NewTCB)
+{
+ CTEStructAssert(NewTCB, tcb);
+
+ NewTCB->tcb_sendnext = CTESystemUpTime();
+ NewTCB->tcb_senduna = NewTCB->tcb_sendnext;
+ NewTCB->tcb_sendmax = NewTCB->tcb_sendnext;
+ NewTCB->tcb_error = IP_SUCCESS;
+
+ // Initialize pseudo-header xsum.
+ NewTCB->tcb_phxsum = PHXSUM(NewTCB->tcb_saddr, NewTCB->tcb_daddr,
+ PROTOCOL_TCP, 0);
+
+ // Initialize retransmit and delayed ack stuff.
+ NewTCB->tcb_rexmitcnt = 0;
+ NewTCB->tcb_rtt = 0;
+ NewTCB->tcb_smrtt = 0;
+ NewTCB->tcb_delta = MS_TO_TICKS(6000);
+ NewTCB->tcb_rexmit = MS_TO_TICKS(3000);
+ STOP_TCB_TIMER(NewTCB->tcb_rexmittimer);
+ STOP_TCB_TIMER(NewTCB->tcb_delacktimer);
+
+}
+
+//* TCPStatus - Handle a status indication.
+//
+// This is the TCP status handler, called by IP when a status event
+// occurs. For most of these we do nothing. For certain severe status
+// events we will mark the local address as invalid.
+//
+// Entry: StatusType - Type of status (NET or HW). NET status
+// is usually caused by a received ICMP
+// message. HW status indicate a HW
+// problem.
+// StatusCode - Code identifying IP_STATUS.
+// OrigDest - If this is NET status, the original dest. of
+// DG that triggered it.
+// OrigSrc - " " " " " , the original src.
+// Src - IP address of status originator (could be local
+// or remote).
+// Param - Additional information for status - i.e. the
+// param field of an ICMP message.
+// Data - Data pertaining to status - for NET status, this
+// is the first 8 bytes of the original DG.
+//
+// Returns: Nothing
+//
+void
+TCPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest,
+ IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data)
+{
+ CTELockHandle TableHandle, TCBHandle;
+ TCB *StatusTCB;
+ TCPHeader UNALIGNED *Header = (TCPHeader UNALIGNED *)Data;
+ SeqNum DropSeq;
+
+ // Handle NET status codes differently from HW status codes.
+ if (StatusType == IP_NET_STATUS) {
+ // It's a NET code. Find a matching TCB.
+ CTEGetLock(&TCBTableLock, &TableHandle);
+ StatusTCB = FindTCB(OrigSrc, OrigDest, Header->tcp_dest,
+ Header->tcp_src);
+ if (StatusTCB != NULL) {
+ // Found one. Get the lock on it, and continue.
+ CTEStructAssert(StatusTCB, tcb);
+
+ CTEGetLock(&StatusTCB->tcb_lock, &TCBHandle);
+ CTEFreeLock(&TCBTableLock, TCBHandle);
+
+
+ // Make sure the TCB is in a state that is interesting.
+ if (StatusTCB->tcb_state == TCB_CLOSED ||
+ StatusTCB->tcb_state == TCB_TIME_WAIT ||
+ CLOSING(StatusTCB)) {
+ CTEFreeLock(&StatusTCB->tcb_lock, TableHandle);
+ return;
+ }
+
+ switch (StatusCode) {
+ // Hard errors - Destination protocol unreachable. We treat
+ // these as fatal errors. Close the connection now.
+ case IP_DEST_PROT_UNREACHABLE:
+ StatusTCB->tcb_error = StatusCode;
+ StatusTCB->tcb_refcnt++;
+ TryToCloseTCB(StatusTCB, TCB_CLOSE_UNREACH, TableHandle);
+
+ RemoveTCBFromConn(StatusTCB);
+ NotifyOfDisc(StatusTCB, NULL,
+ MapIPError(StatusCode, TDI_DEST_UNREACHABLE));
+ CTEGetLock(&StatusTCB->tcb_lock, &TCBHandle);
+ DerefTCB(StatusTCB, TCBHandle);
+ return;
+ break;
+
+ // Soft errors. Save the error in case it time out.
+ case IP_DEST_NET_UNREACHABLE:
+ case IP_DEST_HOST_UNREACHABLE:
+ case IP_DEST_PORT_UNREACHABLE:
+ case IP_PACKET_TOO_BIG:
+ case IP_BAD_ROUTE:
+ case IP_TTL_EXPIRED_TRANSIT:
+ case IP_TTL_EXPIRED_REASSEM:
+ case IP_PARAM_PROBLEM:
+ StatusTCB->tcb_error = StatusCode;
+ break;
+
+ case IP_SPEC_MTU_CHANGE:
+ // A TCP datagram has triggered an MTU change. Figure out
+ // which connection it is, and update him to retransmit the
+ // segment. The Param value is the new MTU. We'll need to
+ // retransmit if the new MTU is less than our existing MTU
+ // and the sequence of the dropped packet is less than our
+ // current send next.
+
+ Param -= sizeof(TCPHeader) -
+ StatusTCB->tcb_opt.ioi_optlength;
+ DropSeq = net_long(Header->tcp_seq);
+
+ if (*(ushort *)&Param <= StatusTCB->tcb_mss &&
+ (SEQ_GTE(DropSeq, StatusTCB->tcb_senduna) &&
+ SEQ_LT(DropSeq, StatusTCB->tcb_sendnext))) {
+
+ // Need to initiate a retranmsit.
+ ResetSendNext(StatusTCB, DropSeq);
+ // Set the congestion window to allow only one packet.
+ // This may prevent us from sending anything if we
+ // didn't just set sendnext to senduna. This is OK,
+ // we'll retransmit later, or send when we get an ack.
+ StatusTCB->tcb_cwin = Param;
+ DelayAction(StatusTCB, NEED_OUTPUT);
+ }
+
+ StatusTCB->tcb_mss = (ushort)MIN(Param,
+ (ulong)StatusTCB->tcb_remmss);
+
+ CTEAssert(StatusTCB->tcb_mss > 0);
+
+
+ //
+ // Reset the Congestion Window if necessary
+ //
+ if (StatusTCB->tcb_cwin < StatusTCB->tcb_mss) {
+ StatusTCB->tcb_cwin = StatusTCB->tcb_mss;
+
+ //
+ // Make sure the slow start threshold is at least
+ // 2 segments
+ //
+ if ( StatusTCB->tcb_ssthresh <
+ ((uint) StatusTCB->tcb_mss*2)
+ ) {
+ StatusTCB->tcb_ssthresh = StatusTCB->tcb_mss * 2;
+ }
+ }
+
+ break;
+
+ // Source quench. This will cause us to reinitiate our
+ // slow start by resetting our congestion window and
+ // adjusting our slow start threshold.
+ case IP_SOURCE_QUENCH:
+ StatusTCB->tcb_ssthresh =
+ MAX(
+ MIN(
+ StatusTCB->tcb_cwin,
+ StatusTCB->tcb_sendwin
+ ) / 2,
+ (uint) StatusTCB->tcb_mss * 2
+ );
+ StatusTCB->tcb_cwin = StatusTCB->tcb_mss;
+ break;
+
+ default:
+ DEBUGCHK;
+ break;
+ }
+
+ CTEFreeLock(&StatusTCB->tcb_lock, TableHandle);
+ } else {
+ // Couldn't find a matching TCB. Just free the lock and return.
+ CTEFreeLock(&TCBTableLock, TableHandle);
+ }
+ } else {
+ uint NewMTU;
+
+ // 'Hardware' or 'global' status. Figure out what to do.
+ switch (StatusCode) {
+ case IP_ADDR_DELETED:
+ // Local address has gone away. OrigDest is the IPAddr which is
+ // gone.
+
+#ifndef _PNP_POWER
+ //
+ // Delete all TCBs with that as a source address.
+ // This is done via TDI notifications in the PNP world.
+ //
+ TCBWalk(DeleteTCBWithSrc, &OrigDest, NULL, NULL);
+
+#endif // _PNP_POWER
+
+#ifdef SECFLTR
+ //
+ // Delete any security filters associated with this address
+ //
+ DeleteProtocolSecurityFilter(OrigDest, PROTOCOL_TCP);
+
+#endif // SECFLTR
+
+ break;
+
+ case IP_ADDR_ADDED:
+
+#ifdef SECFLTR
+ //
+ // An address has materialized. OrigDest identifies the address.
+ // Data is a handle to the IP configuration information for the
+ // interface on which the address is instantiated.
+ //
+ AddProtocolSecurityFilter(OrigDest, PROTOCOL_TCP,
+ (NDIS_HANDLE) Data);
+#endif // SECFLTR
+
+ break;
+
+ case IP_MTU_CHANGE:
+ NewMTU = Param - sizeof(TCPHeader);
+ TCBWalk(SetTCBMTU, &OrigDest, &OrigSrc, &NewMTU);
+ break;
+#ifdef CHICAGO
+ case IP_UNLOAD:
+ // IP is telling us we're being unloaded. First, deregister
+ // with VTDI, and then call CTEUnload().
+ (void)TLRegisterProtocol(PROTOCOL_TCP, NULL, NULL, NULL, NULL);
+ TLRegisterDispatch(TransportName, NULL);
+ (void)RegisterAddrChangeHndlr(AddrChange, FALSE);
+ CTEUnload(TransportName);
+ break;
+#endif // CHICAGO
+ default:
+ DEBUGCHK;
+ break;
+ }
+ }
+}
+
+//* FillTCPHeader - Fill the TCP header in.
+//
+// A utility routine to fill in the TCP header.
+//
+// Input: SendTCB - TCB to fill from.
+// Header - Header to fill into.
+//
+// Returns: Nothing.
+//
+void
+FillTCPHeader(TCB *SendTCB, TCPHeader *Header)
+{
+#ifdef VXD
+
+// STUPID FUCKING COMPILER generates incorrect code for this. Put it back on
+// the blessed day we get a real compiler.
+
+#if 0
+ _asm {
+ mov edx, dword ptr SendTCB
+ mov ecx, dword ptr Header
+ mov ax, word ptr [edx].tcb_sport
+ xchg al, ah
+ mov word ptr [ecx].tcp_src, ax
+ mov ax, [edx].tcb_dport
+ xchg ah, al
+ mov [ecx].tcp_dest, ax
+
+ mov eax, [edx].tcb_sendnext
+ xchg ah, al
+ ror eax, 16
+ xchg ah, al
+ mov [ecx].tcp_seq, eax
+
+ mov eax, [edx].tcb_rcvnext
+ xchg ah, al
+ ror eax, 16
+ xchg ah, al
+ mov [ecx].tcp_ack, eax
+
+ mov [ecx].tcp_flags, 1050H
+ mov dword ptr [ecx].tcp_xsum, 0
+
+ push edx
+ call near ptr RcvWin
+ add esp, 4
+
+ mov ecx, Header
+ xchg ah, al
+ mov [ecx].tcp_window, ax
+
+ }
+#else
+ ushort S;
+ ulong L;
+
+
+ Header->tcp_src = SendTCB->tcb_sport;
+ Header->tcp_dest = SendTCB->tcb_dport;
+ L = SendTCB->tcb_sendnext;
+ Header->tcp_seq = net_long(L);
+ L = SendTCB->tcb_rcvnext;
+ Header->tcp_ack = net_long(L);
+ Header->tcp_flags = 0x1050;
+ *(ulong *)&Header->tcp_xsum = 0;
+ S = RcvWin(SendTCB);
+ Header->tcp_window = net_short(S);
+#endif
+#else
+
+ //
+ // BUGBUG: Is this worth coding in assembly?
+ //
+ ushort S;
+ ulong L;
+
+
+ Header->tcp_src = SendTCB->tcb_sport;
+ Header->tcp_dest = SendTCB->tcb_dport;
+ L = SendTCB->tcb_sendnext;
+ Header->tcp_seq = net_long(L);
+ L = SendTCB->tcb_rcvnext;
+ Header->tcp_ack = net_long(L);
+ Header->tcp_flags = 0x1050;
+ *(ulong *)&Header->tcp_xsum = 0;
+ S = RcvWin(SendTCB);
+ Header->tcp_window = net_short(S);
+#endif
+
+
+}
+
+//* TCPSend - Send data from a TCP connection.
+//
+// This is the main 'send data' routine. We go into a loop, trying
+// to send data until we can't for some reason. First we compute
+// the useable window, use it to figure the amount we could send. If
+// the amount we could send meets certain criteria we'll build a frame
+// and send it, after setting any appropriate control bits. We assume
+// the caller has put a reference on the TCB.
+//
+// Input: SendTCB - TCB to be sent from.
+// TCBHandle - Lock handle for TCB.
+//
+// Returns: Nothing.
+//
+void
+#ifdef VXD
+TCPSend(TCB *SendTCB)
+#else
+TCPSend(TCB *SendTCB, CTELockHandle TCBHandle)
+#endif
+{
+ int SendWin; // Useable send window.
+ uint AmountToSend; // Amount to send this time.
+ uint AmountLeft;
+ TCPHeader *Header; // TCP header for a send.
+ PNDIS_BUFFER FirstBuffer, CurrentBuffer;
+ TCPSendReq *CurSend;
+ SendCmpltContext *SCC;
+ SeqNum OldSeq;
+ IP_STATUS SendStatus;
+ uint AmtOutstanding, AmtUnsent;
+ int ForceWin; // Window we're force to use.
+#ifdef VXD
+ CTELockHandle TCBHandle;
+
+ CTEGetLock(&SendTCB->tcb_lock, &TCBHandle);
+#endif
+
+
+ CTEStructAssert(SendTCB, tcb);
+ CTEAssert(SendTCB->tcb_refcnt != 0);
+
+ CTEAssert(*(int *)&SendTCB->tcb_sendwin >= 0);
+ CTEAssert(*(int *)&SendTCB->tcb_cwin >= SendTCB->tcb_mss);
+
+ CTEAssert(!(SendTCB->tcb_flags & FIN_OUTSTANDING) ||
+ (SendTCB->tcb_sendnext == SendTCB->tcb_sendmax));
+
+ if (!(SendTCB->tcb_flags & IN_TCP_SEND) &&
+ !(SendTCB->tcb_fastchk & TCP_FLAG_IN_RCV)) {
+ SendTCB->tcb_flags |= IN_TCP_SEND;
+
+ // We'll continue this loop until we send a FIN, or we break out
+ // internally for some other reason.
+ while (!(SendTCB->tcb_flags & FIN_OUTSTANDING)) {
+
+ CheckTCBSends(SendTCB);
+
+ AmtOutstanding = (uint)(SendTCB->tcb_sendnext -
+ SendTCB->tcb_senduna);
+ AmtUnsent = SendTCB->tcb_unacked - AmtOutstanding;
+
+ CTEAssert(*(int *)&AmtUnsent >= 0);
+
+ SendWin = (int)(MIN(SendTCB->tcb_sendwin, SendTCB->tcb_cwin) -
+ AmtOutstanding);
+
+#if FAST_RETRANSMIT
+ // if this send is after the fast recovery
+ // and sendwin is zero because of amt outstanding
+ // then, at least force 1 segment to prevent delayed
+ // ack timeouts from the remote
+
+ if (SendTCB->tcb_force) {
+ SendTCB->tcb_force=0;
+ if (SendWin < SendTCB->tcb_mss ){
+
+ SendWin = SendTCB->tcb_mss;
+ }
+ }
+
+#endif
+
+
+
+ // Since the window could have shrank, need to get it to zero at
+ // least.
+ ForceWin = (int)((SendTCB->tcb_flags & FORCE_OUTPUT) >>
+ FORCE_OUT_SHIFT);
+ SendWin = MAX(SendWin, ForceWin);
+
+ AmountToSend = MIN(MIN((uint)SendWin, AmtUnsent), SendTCB->tcb_mss);
+
+ CTEAssert(SendTCB->tcb_mss > 0);
+
+ // See if we have enough to send. We'll send if we have at least a
+ // segment, or if we really have some data to send and we can send
+ // all that we have, or the send window is > 0 and we need to force
+ // output or send a FIN (note that if we need to force output
+ // SendWin will be at least 1 from the check above), or if we can
+ // send an amount == to at least half the maximum send window
+ // we've seen.
+ if (AmountToSend == SendTCB->tcb_mss ||
+ (AmountToSend != 0 && AmountToSend == AmtUnsent) ||
+ (SendWin != 0 &&
+ ((SendTCB->tcb_flags & (FORCE_OUTPUT | FIN_NEEDED)) ||
+ AmountToSend >= (SendTCB->tcb_maxwin / 2)))) {
+
+ // It's OK to send something. Try to get a header buffer now.
+ FirstBuffer = GetTCPHeader();
+ if (FirstBuffer != NULL) {
+
+ // Got a header buffer. Loop through the sends on the TCB,
+ // building a frame.
+ CurrentBuffer = FirstBuffer;
+ CurSend = SendTCB->tcb_cursend;
+
+ Header = (TCPHeader *)(
+ (uchar *)NdisBufferVirtualAddress(FirstBuffer) +
+ LocalNetInfo.ipi_hsize);
+
+ SCC = (SendCmpltContext *)(Header + 1);
+#ifdef DEBUG
+ SCC->scc_sig = scc_signature;
+#endif
+ FillTCPHeader(SendTCB, Header);
+
+ SCC->scc_ubufcount = 0;
+ SCC->scc_tbufcount = 0;
+ SCC->scc_count = 0;
+
+ AmountLeft = AmountToSend;
+
+ if (AmountToSend != 0) {
+ long Result;
+
+
+ CTEStructAssert(CurSend, tsr);
+ SCC->scc_firstsend = CurSend;
+
+ do {
+ CTEAssert(CurSend->tsr_refcnt > 0);
+ Result = CTEInterlockedIncrementLong(
+ &(CurSend->tsr_refcnt)
+ );
+
+ CTEAssert(Result > 0);
+
+ SCC->scc_count++;
+ // If the current send offset is 0 and the current
+ // send is less than or equal to what we have left
+ // to send, we haven't already put a transport
+ // buffer on this send, and nobody else is using
+ // the buffer chain directly, just use the input
+ // buffers. We check for other people using them
+ // by looking at tsr_lastbuf. If it's NULL,
+ // nobody else is using the buffers. If it's not
+ // NULL, somebody is.
+
+ if (SendTCB->tcb_sendofs == 0 &&
+ (SendTCB->tcb_sendsize <= AmountLeft) &&
+ (SCC->scc_tbufcount == 0) &&
+ CurSend->tsr_lastbuf == NULL) {
+
+ NDIS_BUFFER_LINKAGE(CurrentBuffer) =
+ SendTCB->tcb_sendbuf;
+ do {
+ SCC->scc_ubufcount++;
+ CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
+ } while (NDIS_BUFFER_LINKAGE(CurrentBuffer) != NULL);
+
+ CurSend->tsr_lastbuf = CurrentBuffer;
+ AmountLeft -= SendTCB->tcb_sendsize;
+ SendTCB->tcb_sendsize = 0;
+ } else {
+ uint AmountToDup;
+ PNDIS_BUFFER NewBuf, Buf;
+ uint Offset;
+ NDIS_STATUS NStatus;
+ uchar *VirtualAddress;
+ uint Length;
+
+ // Either the current send has more data than
+ // we want to send, or the starting offset is
+ // not 0. In either case we'll need to loop
+ // through the current send, allocating buffers.
+ Buf = SendTCB->tcb_sendbuf;
+ Offset = SendTCB->tcb_sendofs;
+
+ do {
+ CTEAssert(Buf != NULL);
+
+ NdisQueryBuffer(Buf, &VirtualAddress,
+ &Length);
+
+ CTEAssert((Offset < Length) ||
+ (Offset == 0 && Length == 0));
+
+ // Adjust the length for the offset into
+ // this buffer.
+
+ Length -= Offset;
+
+ AmountToDup = MIN(AmountLeft, Length);
+
+ NdisAllocateBuffer(&NStatus, &NewBuf,
+ TCPSendBufferPool,
+ VirtualAddress + Offset,
+ AmountToDup);
+ if (NStatus == NDIS_STATUS_SUCCESS) {
+ SCC->scc_tbufcount++;
+
+ NDIS_BUFFER_LINKAGE(CurrentBuffer) =
+ NewBuf;
+
+ CurrentBuffer = NewBuf;
+ if (AmountToDup >= Length) {
+ // Exhausted this buffer.
+ Buf = NDIS_BUFFER_LINKAGE(Buf);
+ Offset = 0;
+ } else {
+ Offset += AmountToDup;
+ CTEAssert(Offset < NdisBufferLength(Buf));
+ }
+
+ SendTCB->tcb_sendsize -= AmountToDup;
+ AmountLeft -= AmountToDup;
+ } else {
+ // Couldn't allocate a buffer. If
+ // the packet is already partly built,
+ // send what we've got, otherwise
+ // bail out.
+ if (SCC->scc_tbufcount == 0 &&
+ SCC->scc_ubufcount == 0) {
+ TCPSendComplete(SCC, FirstBuffer);
+ goto error_oor;
+ }
+ AmountToSend -= AmountLeft;
+ AmountLeft = 0;
+ }
+ } while (AmountLeft && SendTCB->tcb_sendsize);
+
+ SendTCB->tcb_sendbuf = Buf;
+ SendTCB->tcb_sendofs = Offset;
+ }
+
+ if (CurSend->tsr_flags & TSR_FLAG_URG) {
+ ushort UP;
+ // This send is urgent data. We need to figure
+ // out what the urgent data pointer should be.
+ // We know sendnext is the starting sequence
+ // number of the frame, and that at the top of
+ // this do loop sendnext identified a byte in
+ // the CurSend at that time. We advanced CurSend
+ // at the same rate we've decremented
+ // AmountLeft (AmountToSend - AmountLeft ==
+ // AmountBuilt), so sendnext +
+ // (AmountToSend - AmountLeft) identifies a byte
+ // in the current value of CurSend, and that
+ // quantity plus tcb_sendsize is the sequence
+ // number one beyond the current send.
+ UP =
+ (ushort)(AmountToSend - AmountLeft) +
+ (ushort)SendTCB->tcb_sendsize -
+ ((SendTCB->tcb_flags & BSD_URGENT) ? 0 : 1);
+
+ Header->tcp_urgent = net_short(UP);
+
+ Header->tcp_flags |= TCP_FLAG_URG;
+ }
+
+ // See if we've exhausted this send. If we have,
+ // set the PUSH bit in this frame and move on to
+ // the next send. We also need to check the
+ // urgent data bit.
+ if (SendTCB->tcb_sendsize == 0) {
+ Queue *Next;
+ uchar PrevFlags;
+
+ // We've exhausted this send. Set the PUSH bit.
+ Header->tcp_flags |= TCP_FLAG_PUSH;
+ PrevFlags = CurSend->tsr_flags;
+ Next = QNEXT(&CurSend->tsr_req.tr_q);
+ if (Next != QEND(&SendTCB->tcb_sendq)) {
+ CurSend = STRUCT_OF(TCPSendReq,
+ QSTRUCT(TCPReq, Next, tr_q), tsr_req);
+ CTEStructAssert(CurSend, tsr);
+ SendTCB->tcb_sendsize = CurSend->tsr_unasize;
+ SendTCB->tcb_sendofs = CurSend->tsr_offset;
+ SendTCB->tcb_sendbuf = CurSend->tsr_buffer;
+ SendTCB->tcb_cursend = CurSend;
+
+ // Check the urgent flags. We can't combine
+ // new urgent data on to the end of old
+ // non-urgent data.
+ if ((PrevFlags & TSR_FLAG_URG) && !
+ (CurSend->tsr_flags & TSR_FLAG_URG))
+ break;
+ } else {
+ CTEAssert(AmountLeft == 0);
+ SendTCB->tcb_cursend = NULL;
+ SendTCB->tcb_sendbuf = NULL;
+ }
+ }
+ } while (AmountLeft != 0);
+
+ } else {
+
+ // We're in the loop, but AmountToSend is 0. This
+ // should happen only when we're sending a FIN. Check
+ // this, and return if it's not true.
+ CTEAssert(AmtUnsent == 0);
+ if (!(SendTCB->tcb_flags & FIN_NEEDED)) {
+ // DEBUGCHK;
+ FreeTCPHeader(FirstBuffer);
+ break;
+ }
+
+ SCC->scc_firstsend = NULL;
+ NDIS_BUFFER_LINKAGE(FirstBuffer) = NULL;
+ }
+
+ // Adjust for what we're really going to send.
+ AmountToSend -= AmountLeft;
+
+ // Update the sequence numbers, and start a RTT measurement
+ // if needed.
+
+ OldSeq = SendTCB->tcb_sendnext;
+ SendTCB->tcb_sendnext += AmountToSend;
+
+ if (SEQ_EQ(OldSeq, SendTCB->tcb_sendmax)) {
+ // We're sending entirely new data.
+
+ // We can't advance sendmax once FIN_SENT is set.
+ CTEAssert(!(SendTCB->tcb_flags & FIN_SENT));
+ SendTCB->tcb_sendmax = SendTCB->tcb_sendnext;
+ // We've advanced sendmax, so we must be sending some
+ // new data, so bump the outsegs counter.
+ TStats.ts_outsegs++;
+
+ if (SendTCB->tcb_rtt == 0) {
+ // No RTT running, so start one.
+ SendTCB->tcb_rtt = TCPTime;
+ SendTCB->tcb_rttseq = OldSeq;
+ }
+ } else {
+ // We have at least some retransmission.
+
+ TStats.ts_retranssegs++;
+ if (SEQ_GT(SendTCB->tcb_sendnext, SendTCB->tcb_sendmax)) {
+ // But we also have some new data, so check the
+ // rtt stuff.
+ TStats.ts_outsegs++;
+ CTEAssert(!(SendTCB->tcb_flags & FIN_SENT));
+ SendTCB->tcb_sendmax = SendTCB->tcb_sendnext;
+
+ if (SendTCB->tcb_rtt == 0) {
+ // No RTT running, so start one.
+ SendTCB->tcb_rtt = TCPTime;
+ SendTCB->tcb_rttseq = OldSeq;
+ }
+ }
+ }
+
+ // We've built the frame entirely. If we've send everything
+ // we have and their's a FIN pending, OR it in.
+ if (AmtUnsent == AmountToSend) {
+ if (SendTCB->tcb_flags & FIN_NEEDED) {
+ CTEAssert(!(SendTCB->tcb_flags & FIN_SENT) ||
+ (SendTCB->tcb_sendnext == (SendTCB->tcb_sendmax - 1)));
+ // See if we still have room in the window for a FIN.
+ if (SendWin > (int) AmountToSend) {
+ Header->tcp_flags |= TCP_FLAG_FIN;
+ SendTCB->tcb_sendnext++;
+ SendTCB->tcb_sendmax = SendTCB->tcb_sendnext;
+ SendTCB->tcb_flags |= (FIN_SENT | FIN_OUTSTANDING);
+ SendTCB->tcb_flags &= ~FIN_NEEDED;
+ }
+ }
+ }
+
+ AmountToSend += sizeof(TCPHeader);
+
+ if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer))
+ START_TCB_TIMER(SendTCB->tcb_rexmittimer,
+ SendTCB->tcb_rexmit);
+
+ SendTCB->tcb_flags &= ~(NEED_ACK | ACK_DELAYED |
+ FORCE_OUTPUT);
+ STOP_TCB_TIMER(SendTCB->tcb_delacktimer);
+ STOP_TCB_TIMER(SendTCB->tcb_swstimer);
+ SendTCB->tcb_alive = TCPTime;
+
+ CTEFreeLock(&SendTCB->tcb_lock, TCBHandle);
+
+ // We're all set. Xsum it and send it.
+ Header->tcp_xsum = ~XsumSendChain(SendTCB->tcb_phxsum +
+ (uint)net_short(AmountToSend), FirstBuffer);
+
+ SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, SCC,
+ FirstBuffer, AmountToSend, SendTCB->tcb_daddr,
+ SendTCB->tcb_saddr, &SendTCB->tcb_opt, SendTCB->tcb_rce,
+ PROTOCOL_TCP);
+
+ SendTCB->tcb_error = SendStatus;
+ if (SendStatus != IP_PENDING) {
+ TCPSendComplete(SCC, FirstBuffer);
+ if (SendStatus != IP_SUCCESS) {
+ CTEGetLock(&SendTCB->tcb_lock, &TCBHandle);
+ // This packet didn't get sent. If nothing's
+ // changed in the TCB, put sendnext back to
+ // what we just tried to send. Depending on
+ // the error, we may try again.
+ if (SEQ_GTE(OldSeq, SendTCB->tcb_senduna) &&
+ SEQ_LT(OldSeq, SendTCB->tcb_sendnext))
+ ResetSendNext(SendTCB, OldSeq);
+
+ // We know this packet didn't get sent. Start
+ // the retransmit timer now, if it's not already
+ // runnimg, in case someone came in while we
+ // were in IP and stopped it.
+ if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer))
+ START_TCB_TIMER(SendTCB->tcb_rexmittimer,
+ SendTCB->tcb_rexmit);
+
+ // If it failed because of an MTU problem, get
+ // the new MTU and try again.
+ if (SendStatus == IP_PACKET_TOO_BIG) {
+ uint NewMTU;
+
+ // The MTU has changed. Update it, and try
+ // again.
+ SendStatus = (*LocalNetInfo.ipi_getpinfo)(
+ SendTCB->tcb_daddr, SendTCB->tcb_saddr,
+ &NewMTU, NULL);
+
+ if (SendStatus != IP_SUCCESS)
+ break;
+
+ // We have a new MTU. Make sure it's big enough
+ // to use. If not, correct this and turn off
+ // MTU discovery on this TCB. Otherwise use the
+ // new MTU.
+ if (NewMTU <= (sizeof(TCPHeader) +
+ SendTCB->tcb_opt.ioi_optlength)) {
+
+ // The new MTU is too small to use. Turn off
+ // PMTU discovery on this TCB, and drop to
+ // our off net MTU size.
+ SendTCB->tcb_opt.ioi_flags &= ~IP_FLAG_DF;
+ SendTCB->tcb_mss = MIN((ushort)MAX_REMOTE_MSS,
+ SendTCB->tcb_remmss);
+ } else {
+
+ // The new MTU is adequate. Adjust it for
+ // the header size and options length, and use
+ // it.
+ NewMTU -= sizeof(TCPHeader) -
+ SendTCB->tcb_opt.ioi_optlength;
+ SendTCB->tcb_mss = MIN((ushort)NewMTU,
+ SendTCB->tcb_remmss);
+ }
+
+ CTEAssert(SendTCB->tcb_mss > 0);
+
+ continue;
+ }
+ break;
+ }
+ }
+
+ CTEGetLock(&SendTCB->tcb_lock, &TCBHandle);
+ continue;
+ } else // FirstBuffer != NULL.
+ goto error_oor;
+ } else {
+ // We've decided we can't send anything now. Figure out why, and
+ // see if we need to set a timer.
+ if (SendTCB->tcb_sendwin == 0) {
+ if (!(SendTCB->tcb_flags & FLOW_CNTLD)) {
+ SendTCB->tcb_flags |= FLOW_CNTLD;
+ SendTCB->tcb_rexmitcnt = 0;
+ START_TCB_TIMER(SendTCB->tcb_rexmittimer,
+ SendTCB->tcb_rexmit);
+ SendTCB->tcb_slowcount++;
+ SendTCB->tcb_fastchk |= TCP_FLAG_SLOW;
+ } else
+ if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer))
+ START_TCB_TIMER(SendTCB->tcb_rexmittimer,
+ SendTCB->tcb_rexmit);
+ } else
+ if (AmountToSend != 0)
+ // We have something to send, but we're not sending
+ // it, presumably due to SWS avoidance.
+ if (!TCB_TIMER_RUNNING(SendTCB->tcb_swstimer))
+ START_TCB_TIMER(SendTCB->tcb_swstimer, SWS_TO);
+
+ break;
+ }
+ } // while (!FIN_OUTSTANDING)
+
+ // We're done sending, so we don't need the output flags set.
+ SendTCB->tcb_flags &= ~(IN_TCP_SEND | NEED_OUTPUT | FORCE_OUTPUT |
+ SEND_AFTER_RCV);
+ } else
+ SendTCB->tcb_flags |= SEND_AFTER_RCV;
+
+ DerefTCB(SendTCB, TCBHandle);
+ return;
+
+// Common case error handling code for out of resource conditions. Start the
+// retransmit timer if it's not already running (so that we try this again
+// later), clean up and return.
+error_oor:
+ if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer))
+ START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit);
+
+ // We had an out of resource problem, so clear the OUTPUT flags.
+ SendTCB->tcb_flags &= ~(IN_TCP_SEND | NEED_OUTPUT | FORCE_OUTPUT);
+ DerefTCB(SendTCB, TCBHandle);
+ return;
+
+}
+
+
+#if FAST_RETRANSMIT
+//* ResetSendNextAndFastSend - Set the sendnext value of a TCB.
+//
+// Called to handle fast retransmit of the segment which the reveiver
+// is asking for.
+// tcb_lock will be held while entering (called by TCPRcv)
+// and will be released in this routine after doing IP xmit.
+//
+// Input: SeqTCB - Pointer to TCB to be updated.
+// NewSeq - Sequence number to set.
+//
+// Returns: Nothing.
+//
+void
+ResetAndFastSend(TCB *SeqTCB, SeqNum NewSeq)
+{
+
+ TCPSendReq *SendReq;
+ uint AmtForward;
+ Queue *CurQ;
+ PNDIS_BUFFER Buffer;
+ uint Offset;
+ uint SendSize;
+ CTELockHandle TCBHandle;
+
+ CTEStructAssert(SeqTCB, tcb);
+ CTEAssert(SEQ_GTE(NewSeq, SeqTCB->tcb_senduna));
+
+ // The new seq must be less than send max, or NewSeq, senduna, sendnext,
+ // and sendmax must all be equal. (The latter case happens when we're
+ // called exiting TIME_WAIT, or possibly when we're retransmitting
+ // during a flow controlled situation).
+
+ CTEAssert(SEQ_LT(NewSeq, SeqTCB->tcb_sendmax) ||
+ (SEQ_EQ(SeqTCB->tcb_senduna, SeqTCB->tcb_sendnext) &&
+ SEQ_EQ(SeqTCB->tcb_senduna, SeqTCB->tcb_sendmax) &&
+ SEQ_EQ(SeqTCB->tcb_senduna, NewSeq)));
+
+
+
+ //KdPrint(("Resetandfastsend TCB %x, seq %x\n", SeqTCB, NewSeq));
+
+ if (SYNC_STATE(SeqTCB->tcb_state) && SeqTCB->tcb_state != TCB_TIME_WAIT) {
+ // In these states we need to update the send queue.
+
+ if (!EMPTYQ(&SeqTCB->tcb_sendq)) {
+
+ CurQ = QHEAD(&SeqTCB->tcb_sendq);
+
+ SendReq = (TCPSendReq *)STRUCT_OF(TCPReq, CurQ, tr_q);
+
+ // SendReq points to the first send request on the send queue.
+ // We're pointing at the proper send req now. We need to go down
+
+ // SendReq points to the cursend
+ // SendSize point to sendsize in the cursend
+
+ SendSize = SendReq->tsr_unasize;
+
+ Buffer = SendReq->tsr_buffer;
+ Offset = SendReq->tsr_offset;
+
+ // Call the fast retransmit send now
+
+ //KdPrint(("Calling fastsend buf %x, Offset %x\n", Buffer,Offset));
+
+ TCPFastSend(SeqTCB, Buffer, Offset, SendReq, SendSize);
+
+
+ } else {
+
+ CTEAssert(SeqTCB->tcb_cursend == NULL);
+ }
+
+ }
+
+#ifndef VXD
+ TCBHandle = DISPATCH_LEVEL;
+#endif
+
+ DerefTCB(SeqTCB, TCBHandle);
+ return;
+
+}
+
+//* TCPFastSend - To send a segment without changing TCB state
+//
+// Called to handle fast retransmit of the segment
+// tcb_lock will be held while entering (called by TCPRcv)
+//
+// Input: SendTCB - Pointer to TCB
+// in_sendBuf - Pointer to ndis_buffer
+// in_sendofs - Send Offset
+// in_sendreq - current send request
+// in_sendsize - size of this send
+//
+// Returns: Nothing.
+//
+
+void
+TCPFastSend(TCB *SendTCB,
+ PNDIS_BUFFER in_SendBuf,
+ uint in_SendOfs,
+ TCPSendReq *in_SendReq,
+ uint in_SendSize)
+{
+
+ int SendWin; // Useable send window.
+ uint AmountToSend; // Amount to send this time.
+ uint AmountLeft;
+ TCPHeader *Header; // TCP header for a send.
+ PNDIS_BUFFER FirstBuffer, CurrentBuffer;
+ TCPSendReq *CurSend;
+ SendCmpltContext *SCC;
+ SeqNum OldSeq;
+ IP_STATUS SendStatus;
+ uint AmtOutstanding, AmtUnsent;
+ int ForceWin; // Window we're force to use.
+ CTELockHandle TCBHandle;
+
+ uint SendOfs = in_SendOfs;
+ uint SendSize = in_SendSize;
+ PNDIS_BUFFER SendBuf;
+
+#ifndef VXD
+ TCBHandle = DISPATCH_LEVEL;
+#endif
+
+
+ CTEStructAssert(SendTCB, tcb);
+ CTEAssert(SendTCB->tcb_refcnt != 0);
+
+ CTEAssert(*(int *)&SendTCB->tcb_sendwin >= 0);
+ CTEAssert(*(int *)&SendTCB->tcb_cwin >= SendTCB->tcb_mss);
+
+ CTEAssert(!(SendTCB->tcb_flags & FIN_OUTSTANDING) ||
+ (SendTCB->tcb_sendnext == SendTCB->tcb_sendmax));
+
+
+
+ AmtOutstanding = (uint)(SendTCB->tcb_sendnext -
+ SendTCB->tcb_senduna);
+
+ AmtUnsent = SendTCB->tcb_unacked - AmtOutstanding;
+
+ CTEAssert(*(int *)&AmtUnsent >= 0);
+
+ SendWin = SendTCB->tcb_mss;
+
+
+ AmountToSend = MIN(in_SendSize, SendTCB->tcb_mss);
+
+ CTEAssert (AmountToSend >= 0);
+
+
+ CTEAssert(SendTCB->tcb_mss > 0);
+
+ // See if we have enough to send. We'll send if we have at least a
+ // segment, or if we really have some data to send and we can send
+ // all that we have, or the send window is > 0 and we need to force
+ // output or send a FIN (note that if we need to force output
+ // SendWin will be at least 1 from the check above), or if we can
+ // send an amount == to at least half the maximum send window
+ // we've seen.
+
+ //KdPrint(("In fastsend Sendwin %x, Amttosend %x\n", SendWin,AmountToSend));
+
+ if (AmountToSend >= 0) {
+
+
+ // It's OK to send something. Try to get a header buffer now.
+ // Mark the TCB for debugging.
+ // This should be removed for shipping version.
+
+ SendTCB->tcb_fastchk |= TCP_FLAG_FASTREC;
+
+ FirstBuffer = GetTCPHeader();
+
+ if (FirstBuffer != NULL) {
+
+ // Got a header buffer. Loop through the sends on the TCB,
+ // building a frame.
+
+ CurrentBuffer = FirstBuffer;
+ CurSend = in_SendReq;
+ SendOfs = in_SendOfs;
+
+ Header = (TCPHeader *)(
+ (uchar *)NdisBufferVirtualAddress(FirstBuffer) +
+ LocalNetInfo.ipi_hsize);
+
+ SCC = (SendCmpltContext *)(Header + 1);
+
+#ifdef DEBUG
+ SCC->scc_sig = scc_signature;
+#endif
+ FillTCPHeader(SendTCB, Header);
+ {
+ ulong L = SendTCB->tcb_senduna;
+ Header->tcp_seq = net_long(L);
+
+ }
+
+ SCC->scc_ubufcount = 0;
+ SCC->scc_tbufcount = 0;
+ SCC->scc_count = 0;
+
+ AmountLeft = AmountToSend;
+
+ if (AmountToSend != 0) {
+ long Result;
+
+ CTEStructAssert(CurSend, tsr);
+ SCC->scc_firstsend = CurSend;
+
+ do {
+
+ CTEAssert(CurSend->tsr_refcnt > 0);
+
+ Result = CTEInterlockedIncrementLong(
+ &(CurSend->tsr_refcnt)
+ );
+
+ CTEAssert(Result > 0);
+
+ SCC->scc_count++;
+
+ // If the current send offset is 0 and the current
+ // send is less than or equal to what we have left
+ // to send, we haven't already put a transport
+ // buffer on this send, and nobody else is using
+ // the buffer chain directly, just use the input
+ // buffers. We check for other people using them
+ // by looking at tsr_lastbuf. If it's NULL,
+ // nobody else is using the buffers. If it's not
+ // NULL, somebody is.
+
+ if (SendOfs == 0 &&
+ (SendSize <= AmountLeft) &&
+ (SCC->scc_tbufcount == 0) &&
+ CurSend->tsr_lastbuf == NULL) {
+
+ NDIS_BUFFER_LINKAGE(CurrentBuffer) = in_SendBuf;
+
+ do {
+ SCC->scc_ubufcount++;
+ CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
+ } while (NDIS_BUFFER_LINKAGE(CurrentBuffer) != NULL);
+
+ CurSend->tsr_lastbuf = CurrentBuffer;
+ AmountLeft -= SendSize;
+ //KdPrint(("nobody using this CurSend %x\n",CurSend ));
+ // SendSize = 0;
+ } else {
+ uint AmountToDup;
+ PNDIS_BUFFER NewBuf, Buf;
+ uint Offset;
+ NDIS_STATUS NStatus;
+ uchar *VirtualAddress;
+ uint Length;
+
+ // Either the current send has more data than
+ // we want to send, or the starting offset is
+ // not 0. In either case we'll need to loop
+ // through the current send, allocating buffers.
+
+ Buf = in_SendBuf;
+
+ Offset = SendOfs;
+
+ do {
+ CTEAssert(Buf != NULL);
+
+ NdisQueryBuffer(Buf, &VirtualAddress,
+ &Length);
+
+ CTEAssert((Offset < Length) ||
+ (Offset == 0 && Length == 0));
+
+ // Adjust the length for the offset into
+ // this buffer.
+
+ Length -= Offset;
+
+ AmountToDup = MIN(AmountLeft, Length);
+
+ NdisAllocateBuffer(&NStatus, &NewBuf,
+ TCPSendBufferPool,
+ VirtualAddress + Offset,
+ AmountToDup);
+
+ if (NStatus == NDIS_STATUS_SUCCESS) {
+
+ SCC->scc_tbufcount++;
+
+ NDIS_BUFFER_LINKAGE(CurrentBuffer) = NewBuf;
+
+ CurrentBuffer = NewBuf;
+
+ if (AmountToDup >= Length) {
+
+ // Exhausted this buffer.
+
+ Buf = NDIS_BUFFER_LINKAGE(Buf);
+ Offset = 0;
+
+ } else {
+
+ Offset += AmountToDup;
+ CTEAssert(Offset < NdisBufferLength(Buf));
+ }
+
+ SendSize -= AmountToDup;
+ AmountLeft -= AmountToDup;
+
+ } else {
+
+ // Couldn't allocate a buffer. If
+ // the packet is already partly built,
+ // send what we've got, otherwise
+ // bail out.
+
+ if (SCC->scc_tbufcount == 0 &&
+ SCC->scc_ubufcount == 0) {
+ TCPSendComplete(SCC, FirstBuffer);
+ goto error_oor;
+ }
+
+ AmountToSend -= AmountLeft;
+ AmountLeft = 0;
+
+ }
+ } while (AmountLeft && SendSize);
+
+ SendBuf = Buf;
+ SendOfs = Offset;
+ //KdPrint(("Ready to send. SendBuf %x SendOfs %x\n",SendBuf, SendOfs ));
+
+ }
+
+ if (CurSend->tsr_flags & TSR_FLAG_URG) {
+ ushort UP;
+
+ KdPrint(("Fast send in URG %x\n", CurSend));
+
+ // This send is urgent data. We need to figure
+ // out what the urgent data pointer should be.
+ // We know sendnext is the starting sequence
+ // number of the frame, and that at the top of
+ // this do loop sendnext identified a byte in
+ // the CurSend at that time. We advanced CurSend
+ // at the same rate we've decremented
+ // AmountLeft (AmountToSend - AmountLeft ==
+ // AmountBuilt), so sendnext +
+ // (AmountToSend - AmountLeft) identifies a byte
+ // in the current value of CurSend, and that
+ // quantity plus tcb_sendsize is the sequence
+ // number one beyond the current send.
+
+ UP =
+ (ushort)(AmountToSend - AmountLeft) +
+ (ushort)SendTCB->tcb_sendsize -
+ ((SendTCB->tcb_flags & BSD_URGENT) ? 0 : 1);
+
+ Header->tcp_urgent = net_short(UP);
+
+ Header->tcp_flags |= TCP_FLAG_URG;
+ }
+
+ // See if we've exhausted this send. If we have,
+ // set the PUSH bit in this frame and move on to
+ // the next send. We also need to check the
+ // urgent data bit.
+
+ if (SendSize == 0) {
+ Queue *Next;
+ uchar PrevFlags;
+
+ // We've exhausted this send. Set the PUSH bit.
+ Header->tcp_flags |= TCP_FLAG_PUSH;
+ PrevFlags = CurSend->tsr_flags;
+ Next = QNEXT(&CurSend->tsr_req.tr_q);
+ if (Next != QEND(&SendTCB->tcb_sendq)) {
+ CurSend = STRUCT_OF(TCPSendReq,
+ QSTRUCT(TCPReq, Next, tr_q), tsr_req);
+ CTEStructAssert(CurSend, tsr);
+ SendSize = CurSend->tsr_unasize;
+ SendOfs = CurSend->tsr_offset;
+ SendBuf = CurSend->tsr_buffer;
+ CurSend = CurSend;
+
+ // Check the urgent flags. We can't combine
+ // new urgent data on to the end of old
+ // non-urgent data.
+ if ((PrevFlags & TSR_FLAG_URG) && !
+ (CurSend->tsr_flags & TSR_FLAG_URG))
+ break;
+ } else {
+ CTEAssert(AmountLeft == 0);
+ CurSend = NULL;
+ SendBuf = NULL;
+ }
+ }
+
+ } while (AmountLeft != 0);
+
+ } else {
+
+ // Amt to send is 0.
+ // Just bail out and strat timer.
+
+ if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer))
+ START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit);
+
+ FreeTCPHeader(FirstBuffer);
+ return;
+
+ }
+
+ // Adjust for what we're really going to send.
+
+ AmountToSend -= AmountLeft;
+
+
+ TStats.ts_retranssegs++;
+
+ // We've built the frame entirely. If we've send everything
+ // we have and their's a FIN pending, OR it in.
+
+ AmountToSend += sizeof(TCPHeader);
+
+
+ SendTCB->tcb_flags &= ~(NEED_ACK | ACK_DELAYED |
+ FORCE_OUTPUT);
+
+
+
+ STOP_TCB_TIMER(SendTCB->tcb_delacktimer);
+ STOP_TCB_TIMER(SendTCB->tcb_swstimer);
+
+ SendTCB->tcb_alive = TCPTime;
+
+ SendTCB->tcb_fastchk &= ~TCP_FLAG_FASTREC;
+
+ CTEFreeLock(&SendTCB->tcb_lock, TCBHandle);
+
+ //KdPrint (("Going out to IP SendTCB %x, Firstbuf %x\n", SendTCB, FirstBuffer));
+ // We're all set. Xsum it and send it.
+
+ Header->tcp_xsum = ~XsumSendChain(SendTCB->tcb_phxsum +
+ (uint)net_short(AmountToSend), FirstBuffer);
+
+ SendStatus = (*LocalNetInfo.ipi_xmit)(TCPProtInfo, SCC,
+ FirstBuffer, AmountToSend, SendTCB->tcb_daddr,
+ SendTCB->tcb_saddr, &SendTCB->tcb_opt, SendTCB->tcb_rce,
+ PROTOCOL_TCP);
+
+ SendTCB->tcb_error = SendStatus;
+ if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer))
+ START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit);
+
+
+ if (SendStatus != IP_PENDING) {
+ TCPSendComplete(SCC, FirstBuffer);
+ }
+
+ //Reacquire Lock to keep DerefTCB happy
+ //Bug #63904
+
+ CTEGetLock(&SendTCB->tcb_lock, &TCBHandle);
+
+
+ } else { // FirstBuffer != NULL.
+ goto error_oor;
+ }
+ } else{
+
+ SendTCB->tcb_flags |= SEND_AFTER_RCV;
+ if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer))
+ START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit);
+
+ }
+
+
+ SendTCB->tcb_flags |= NEED_OUTPUT;
+
+ return;
+
+// Common case error handling code for out of resource conditions. Start the
+// retransmit timer if it's not already running (so that we try this again
+// later), clean up and return.
+
+error_oor:
+ if (!TCB_TIMER_RUNNING(SendTCB->tcb_rexmittimer))
+ START_TCB_TIMER(SendTCB->tcb_rexmittimer, SendTCB->tcb_rexmit);
+
+ // We had an out of resource problem, so clear the OUTPUT flags.
+ SendTCB->tcb_flags &= ~(IN_TCP_SEND | NEED_OUTPUT | FORCE_OUTPUT);
+
+ return;
+
+
+}
+
+#endif //FAST_RETRANSMIT
+
+
+
+
+//* TDISend - Send data on a connection.
+//
+// The main TDI send entry point. We take the input parameters, validate them,
+// allocate a send request, etc. We then put the send request on the queue.
+// If we have no other sends on the queue or Nagling is disabled we'll
+// call TCPSend to send the data.
+//
+// Input: Request - The TDI request for the call.
+// Flags - Flags for this send.
+// SendLength - Length in bytes of send.
+// SendBuffer - Pointer to buffer chain to be sent.
+//
+// Returns: Status of attempt to send.
+//
+TDI_STATUS
+TdiSend(PTDI_REQUEST Request, ushort Flags, uint SendLength,
+ PNDIS_BUFFER SendBuffer)
+{
+ TCPConn *Conn;
+ TCB *SendTCB;
+ TCPSendReq *SendReq;
+ CTELockHandle ConnTableHandle, TCBHandle;
+ TDI_STATUS Error;
+ uint EmptyQ;
+
+#ifdef DEBUG
+ uint RealSendSize;
+ PNDIS_BUFFER Temp;
+
+ // Loop through the buffer chain, and make sure that the length matches
+ // up with SendLength.
+
+ Temp = SendBuffer;
+ RealSendSize = 0;
+ do {
+ CTEAssert(Temp != NULL);
+
+ RealSendSize += NdisBufferLength(Temp);
+ Temp = NDIS_BUFFER_LINKAGE(Temp);
+ } while (Temp != NULL);
+
+ CTEAssert(RealSendSize == SendLength);
+
+#endif
+
+
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+
+ Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ SendTCB = Conn->tc_tcb;
+ if (SendTCB != NULL) {
+ CTEStructAssert(SendTCB, tcb);
+ CTEGetLockAtDPC(&SendTCB->tcb_lock, &TCBHandle);
+ CTEFreeLockFromDPC(&ConnTableLock, TCBHandle);
+ if (DATA_SEND_STATE(SendTCB->tcb_state) && !CLOSING(SendTCB)) {
+ // We have a TCB, and it's valid. Get a send request now.
+
+ CheckTCBSends(SendTCB);
+
+ if (SendLength != 0) {
+
+ SendReq = GetSendReq();
+ if (SendReq != NULL) {
+ SendReq->tsr_req.tr_rtn = Request->RequestNotifyObject;
+ SendReq->tsr_req.tr_context = Request->RequestContext;
+ SendReq->tsr_buffer = SendBuffer;
+ SendReq->tsr_size = SendLength;
+ SendReq->tsr_unasize = SendLength;
+ SendReq->tsr_refcnt = 1; // ACK will decrement this ref
+ SendReq->tsr_offset = 0;
+ SendReq->tsr_lastbuf = NULL;
+ SendReq->tsr_time = TCPTime;
+ SendReq->tsr_flags = (Flags & TDI_SEND_EXPEDITED) ?
+ TSR_FLAG_URG : 0;
+ SendTCB->tcb_unacked += SendLength;
+
+ EmptyQ = EMPTYQ(&SendTCB->tcb_sendq);
+ ENQUEUE(&SendTCB->tcb_sendq, &SendReq->tsr_req.tr_q);
+ if (SendTCB->tcb_cursend == NULL) {
+ SendTCB->tcb_cursend = SendReq;
+ SendTCB->tcb_sendbuf = SendBuffer;
+ SendTCB->tcb_sendofs = 0;
+ SendTCB->tcb_sendsize = SendLength;
+ }
+ if (EmptyQ) {
+ SendTCB->tcb_refcnt++;
+#ifdef VXD
+ CTEFreeLock(&SendTCB->tcb_lock, ConnTableHandle);
+ TCPSend(SendTCB);
+#else
+ TCPSend(SendTCB, ConnTableHandle);
+#endif
+ } else
+ if (!(SendTCB->tcb_flags & NAGLING) ||
+ (SendTCB->tcb_unacked - (SendTCB->tcb_sendmax -
+ SendTCB->tcb_senduna)) >= SendTCB->tcb_mss) {
+ SendTCB->tcb_refcnt++;
+#ifdef VXD
+ CTEFreeLock(&SendTCB->tcb_lock,
+ ConnTableHandle);
+ TCPSend(SendTCB);
+#else
+ TCPSend(SendTCB, ConnTableHandle);
+#endif
+ } else
+ CTEFreeLock(&SendTCB->tcb_lock,
+ ConnTableHandle);
+
+ return TDI_PENDING;
+ } else
+ Error = TDI_NO_RESOURCES;
+ } else
+ Error = TDI_SUCCESS;
+ } else
+ Error = TDI_INVALID_STATE;
+
+ CTEFreeLock(&SendTCB->tcb_lock, ConnTableHandle);
+ return Error;
+ } else
+ Error = TDI_INVALID_STATE;
+ } else
+ Error = TDI_INVALID_CONNECTION;
+
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ return Error;
+
+}
+
+#pragma BEGIN_INIT
+extern void *TLRegisterProtocol(uchar Protocol, void *RcvHandler,
+ void *XmitHandler, void *StatusHandler,
+ void *RcvCmpltHandler);
+
+extern IP_STATUS TCPRcv(void *IPContext, IPAddr Dest, IPAddr Src,
+ IPAddr LocalAddr, IPAddr SrcAddr,
+ IPHeader UNALIGNED *IPH, uint IPHLength,
+ IPRcvBuf *RcvBuf, uint Size, uchar IsBCast,
+ uchar Protocol, IPOptInfo *OptInfo);
+extern void TCPRcvComplete(void);
+
+uchar SendInited = FALSE;
+
+//* FreeTCPHeaderList - Free the list of TCP header buffers.
+//
+// Called when we want to free the list of TCP header buffers.
+//
+// Input: Nothing.
+//
+// Returns: Nothing.
+//
+void
+FreeTCPHeaderList(void)
+{
+ CTELockHandle Handle;
+ TCPHdrBPoolEntry *Entry;
+
+ CTEGetLock(&TCPSendFreeLock, &Handle);
+
+ Entry = TCPHdrBPoolList;
+ TCPHdrBPoolList = NULL;
+
+ TCPCurrentSendFree = 0;
+
+ while (Entry != NULL) {
+ TCPHdrBPoolEntry *OldEntry;
+
+ NdisFreeBufferPool(Entry->the_handle);
+ CTEFreeMem(Entry->the_buffer);
+ OldEntry = Entry;
+ Entry = Entry->the_next;
+ CTEFreeMem(OldEntry);
+ }
+
+ CTEFreeLock(&TCPSendFreeLock, Handle);
+
+
+}
+
+//* InitTCPSend - Initialize our send side.
+//
+// Called during init time to initialize our TCP send state.
+//
+// Input: Nothing.
+//
+// Returns: TRUE if we inited, false if we didn't.
+//
+int
+InitTCPSend(void)
+{
+ PNDIS_BUFFER Buffer;
+ NDIS_STATUS Status;
+
+
+#ifdef NT
+ ExInitializeSListHead(&TCPSendFree);
+ ExInitializeSListHead(&TCPSendReqFree);
+#endif
+
+ CTEInitLock(&TCPSendReqFreeLock);
+ CTEInitLock(&TCPSendFreeLock);
+ CTEInitLock(&TCPSendReqCompleteLock);
+
+ TCPHdrBPoolList = NULL;
+ TCPCurrentSendFree = 0;
+
+ Buffer = GrowTCPHeaderList();
+
+ if (Buffer != NULL)
+ FreeTCPHeader(Buffer);
+ else
+ return FALSE;
+
+ NdisAllocateBufferPool(&Status, &TCPSendBufferPool, NUM_TCP_BUFFERS);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ FreeTCPHeaderList();
+ return FALSE;
+ }
+
+ TCPProtInfo = TLRegisterProtocol(PROTOCOL_TCP, TCPRcv, TCPSendComplete,
+ TCPStatus, TCPRcvComplete);
+
+ if (TCPProtInfo == NULL) {
+ FreeTCPHeaderList();
+ NdisFreeBufferPool(TCPSendBufferPool);
+ return FALSE;
+ }
+
+ SendInited = TRUE;
+ return TRUE;
+}
+
+//* UnInitTCPSend - UnInitialize our send side.
+//
+// Called during init time if we're going to fail to initialize.
+//
+// Input: Nothing.
+//
+// Returns: TRUE if we inited, false if we didn't.
+//
+void
+UnInitTCPSend(void)
+{
+ if (!SendInited)
+ return;
+
+ TLRegisterProtocol(PROTOCOL_TCP, NULL, NULL, NULL, NULL);
+ FreeTCPHeaderList();
+ NdisFreeBufferPool(TCPSendBufferPool);
+
+}
+#pragma END_INIT
+
diff --git a/private/ntos/tdi/tcpip/tcp/tcpsend.h b/private/ntos/tdi/tcpip/tcp/tcpsend.h
new file mode 100644
index 000000000..b74c69861
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tcpsend.h
@@ -0,0 +1,105 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TCPSEND.H - TCP send protocol definitions.
+//
+// This file contains the definitions of TCP send protocol things.
+//
+
+#ifdef NT
+
+#define NUM_TCP_HEADERS 32
+#define NUM_TCP_BUFFERS 150
+#define TCP_MAX_HDRS 0xffffffff
+
+#else // NT
+
+#define NUM_TCP_HEADERS 16
+#define NUM_TCP_BUFFERS 100
+#define TCP_MAX_HDRS 320
+
+#endif // NT
+
+//#define SEND_DEBUG 1
+
+#ifdef SEND_DEBUG
+#define SEND_TICKS 10
+EXTERNAL_LOCK(SendUseLock)
+extern struct TCPSendReq *SendUseList;
+#endif
+
+//* Structure of a TCP send request.
+
+#define tsr_signature 0x20525354 // 'TSR '
+
+typedef struct TCPSendReq {
+ struct TCPReq tsr_req; // General request structure.
+#ifdef DEBUG
+ ulong tsr_sig;
+#endif
+ uint tsr_size; // Size in bytes of data in send.
+ long tsr_refcnt; // Reference count for this send.
+ uchar tsr_flags; // Flags for this send.
+ uchar tsr_pad[3]; // Pad to dword boundary.
+ uint tsr_unasize; // Number of bytes unacked.
+ uint tsr_offset; // Offset into first buffer in chain
+ // of start of unacked data..
+ PNDIS_BUFFER tsr_buffer; // Pointer to start of unacked buffer
+ // chain.
+ PNDIS_BUFFER tsr_lastbuf; // Pointer to last buffer in chain.
+ // Valid iff we've sent directly from
+ // the buffer chain w/o doing an
+ // NdisCopyBuffer.
+ uint tsr_time; // TCP time this was received.
+#ifdef SEND_DEBUG
+ struct TCPSendReq *tsr_next; // Debug next field.
+ uint tsr_timer; // Timer field.
+ uint tsr_cmplt; // Who completed it.
+#endif
+} TCPSendReq;
+
+#define TSR_FLAG_URG 0x01 // Urgent data.
+
+//* Structure defining the context received during a send completes.
+
+#define scc_signature 0x20434353 // 'SCC '
+
+typedef struct SendCmpltContext {
+#ifdef DEBUG
+ ulong scc_sig;
+#endif
+
+ TCPSendReq *scc_firstsend; // First send in this context.
+ uint scc_count; // Number of sends in count.
+ ushort scc_ubufcount; // Number of 'user' buffers in send.
+ ushort scc_tbufcount; // Number of transport buffers in send.
+} SendCmpltContext;
+
+EXTERNAL_LOCK(TCPSendReqCompleteLock)
+
+extern void InitSendState(struct TCB *NewTCB);
+extern void SendSYN(struct TCB *SYNTcb, CTELockHandle);
+extern void SendKA(struct TCB *KATCB, CTELockHandle Handle);
+extern void SendRSTFromHeader(struct TCPHeader UNALIGNED *TCPH, uint Length,
+ IPAddr Dest, IPAddr Src, IPOptInfo *OptInfo);
+extern void SendACK(struct TCB *ACKTcb);
+extern void SendRSTFromTCB(struct TCB *RSTTcb);
+extern void GoToEstab(struct TCB *EstabTCB);
+extern void FreeSendReq(TCPSendReq *FreedReq);
+extern void FreeTCPHeader(PNDIS_BUFFER FreedBuffer);
+
+extern int InitTCPSend(void);
+extern void UnInitTCPSend(void);
+
+#ifdef VXD
+extern void TCPSend(struct TCB *SendTCB);
+#else
+extern void TCPSend(struct TCB *SendTCB, CTELockHandle Handle);
+#endif
+
+extern TDI_STATUS TdiSend(PTDI_REQUEST Request, ushort Flags, uint SendLength,
+ PNDIS_BUFFER SendBuffer);
+extern uint RcvWin(struct TCB *WinTCB);
diff --git a/private/ntos/tdi/tcpip/tcp/tlcommon.c b/private/ntos/tdi/tcpip/tcp/tlcommon.c
new file mode 100644
index 000000000..902378004
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tlcommon.c
@@ -0,0 +1,553 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TLCOMMON.C - Common transport layer code.
+//
+// This file contains the code for routines that are common to
+// both TCP and UDP.
+//
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#include "tdistat.h"
+#ifdef NT
+#include "tdikrnl.h"
+#endif
+#include "tlcommon.h"
+
+extern uint tcpxsum(uint Seed, void *Ptr, uint Length);
+extern IPInfo LocalNetInfo;
+
+//* XsumSendChain - Checksum a chain of NDIS send buffers.
+//
+// Called to xsum a chain of NDIS send buffers. We're given the
+// pseudo-header xsum to start with, and we call xsum on each
+// buffer. We assume that this is a send chain, and that the
+// first buffer of the chain has room for an IP header that we
+// need to skip.
+//
+// Input: PHXsum - Pseudo-header xsum.
+// BufChain - Pointer to NDIS_BUFFER chain.
+//
+// Returns: The computed xsum.
+//
+
+ushort
+XsumSendChain(uint PHXsum, PNDIS_BUFFER BufChain)
+{
+ uint HeaderSize;
+ uint OldLength;
+ uint SwapCount;
+ uchar *Ptr;
+
+ HeaderSize = LocalNetInfo.ipi_hsize;
+ OldLength = 0;
+ SwapCount = 0;
+
+ //
+ // ***** The following line of code can be removed if the pseudo
+ // checksum never has any bits sets in the upper word.
+ //
+
+ PHXsum = (((PHXsum << 16) | (PHXsum >> 16)) + PHXsum) >> 16;
+ do {
+
+ //
+ // If the length of the last buffer was odd, then swap the checksum.
+ //
+
+ if ((OldLength & 1) != 0) {
+ PHXsum = ((PHXsum & 0xff) << 8) | (PHXsum >> 8);
+ SwapCount ^= 1;
+ }
+
+ Ptr = (uchar *)NdisBufferVirtualAddress(BufChain) + HeaderSize;
+ PHXsum = tcpxsum(PHXsum, Ptr, NdisBufferLength(BufChain));
+ HeaderSize = 0;
+ OldLength = NdisBufferLength(BufChain);
+ BufChain = NDIS_BUFFER_LINKAGE(BufChain);
+ } while(BufChain != NULL);
+
+ //
+ // If an odd number of swaps were done, then swap the xsum again.
+ //
+ // N.B. At this point the checksum is only a word.
+ //
+
+ if (SwapCount != 0) {
+ PHXsum = ((PHXsum & 0xff) << 8) | (PHXsum >> 8);
+ }
+
+ return (ushort)PHXsum;
+}
+
+//* XsumRcvBuf - Checksum a chain of IP receive buffers.
+//
+// Called to xsum a chain of IP receive buffers. We're given the
+// pseudo-header xsum to start with, and we call xsum on each buffer.
+//
+// We assume that this rcv buf chain has no odd sized buffers, except
+// possibly the last one.
+//
+// Input: PHXsum - Pseudo-header xsum.
+// BufChain - Pointer to IPRcvBuf chain.
+//
+// Returns: The computed xsum.
+//
+
+ushort
+XsumRcvBuf(uint PHXsum, IPRcvBuf *BufChain)
+{
+
+ //
+ // ***** The following line of code can be removed if the pseudo
+ // checksum never has any bits sets in the upper word.
+ //
+
+ PHXsum = (((PHXsum << 16) | (PHXsum >> 16)) + PHXsum) >> 16;
+ do {
+ CTEAssert(!(BufChain->ipr_size & 1) || (BufChain->ipr_next == NULL));
+
+ PHXsum = tcpxsum(PHXsum, BufChain->ipr_buffer, BufChain->ipr_size);
+ BufChain = BufChain->ipr_next;
+ } while (BufChain != NULL);
+
+ return (ushort)(PHXsum);
+}
+
+
+//* CopyRcvToNdis - Copy from an IPRcvBuf chain to an NDIS buffer chain.
+//
+// This is the function we use to copy from a chain of IP receive buffers
+// to a chain of NDIS buffers. The caller specifies the source and destination,
+// a maximum size to copy, and an offset into the first buffer to start
+// copying from. We copy as much as possible up to the size, and return
+// the size copied.
+//
+// Input: RcvBuf - Pointer to receive buffer chain.
+// DestBuf - Pointer to NDIS buffer chain.
+// Size - Size in bytes to copy.
+// RcvOffset - Offset into first buffer to copy from.
+// DestOffset - Offset into dest buffer to start copying at.
+//
+// Returns: Bytes copied.
+//
+
+#ifdef NT
+
+uint
+CopyRcvToNdis(IPRcvBuf *RcvBuf, PNDIS_BUFFER DestBuf, uint Size,
+ uint RcvOffset, uint DestOffset)
+{
+ uint TotalBytesCopied = 0; // Bytes we've copied so far.
+ uint BytesCopied = 0; // Bytes copied out of each buffer.
+ uint DestSize, RcvSize; // Size left in current destination and
+ // recv. buffers, respectively.
+ uint BytesToCopy; // How many bytes to copy this time.
+ NTSTATUS Status;
+
+
+ CTEAssert(RcvBuf != NULL);
+
+ CTEAssert(RcvOffset <= RcvBuf->ipr_size);
+
+ // The destination buffer can be NULL - this is valid, if odd.
+ if (DestBuf != NULL) {
+
+ RcvSize = RcvBuf->ipr_size - RcvOffset;
+ DestSize = NdisBufferLength(DestBuf);
+
+ if (Size < DestSize) {
+ DestSize = Size;
+ }
+
+ do {
+ // Compute the amount to copy, and then copy from the
+ // appropriate offsets.
+ BytesToCopy = MIN(DestSize, RcvSize);
+
+ Status = TdiCopyBufferToMdl(RcvBuf->ipr_buffer, RcvOffset,
+ BytesToCopy, DestBuf, DestOffset, &BytesCopied);
+
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+
+ CTEAssert(BytesCopied == BytesToCopy);
+
+ TotalBytesCopied += BytesCopied;
+ DestSize -= BytesCopied;
+ DestOffset += BytesCopied;
+ RcvSize -= BytesToCopy;
+
+ if (!RcvSize) {
+ // Exhausted this buffer.
+
+ RcvBuf = RcvBuf->ipr_next;
+
+ // If we have another one, use it.
+ if (RcvBuf != NULL) {
+ RcvOffset = 0;
+ RcvSize = RcvBuf->ipr_size;
+ }
+ else {
+ break;
+ }
+ }
+ else { // Buffer not exhausted, update offset.
+ RcvOffset += BytesToCopy;
+ }
+
+ } while (DestSize);
+
+ }
+
+ return TotalBytesCopied;
+
+
+}
+
+#else // NT
+
+uint
+CopyRcvToNdis(IPRcvBuf *RcvBuf, PNDIS_BUFFER DestBuf, uint Size,
+ uint RcvOffset, uint DestOffset)
+{
+ uint BytesCopied = 0; // Bytes we've copied so far.
+ uint DestSize, RcvSize; // Size left in current destination and
+ // recv. buffers, respectively.
+ uint BytesToCopy; // How many bytes to copy this time.
+
+ CTEAssert(RcvBuf != NULL);
+
+ CTEAssert(RcvOffset <= RcvBuf->ipr_size);
+
+ // The destination buffer can be NULL - this is valid, if odd.
+ if (DestBuf != NULL) {
+
+ DestSize = NdisBufferLength(DestBuf);
+ RcvSize = RcvBuf->ipr_size - RcvOffset;
+
+ //
+ // Skip over DestOffset bytes
+ //
+ while (DestOffset >= DestSize) {
+ DestOffset -= DestSize;
+ DestBuf = NDIS_BUFFER_LINKAGE(DestBuf);
+
+ if (DestBuf == NULL) {
+ return(0);
+ }
+
+ DestSize = NdisBufferLength(DestBuf);
+ }
+
+ DestSize -= DestOffset;
+
+ do {
+
+ // Compute the amount to copy, and then copy from the
+ // appropriate offsets.
+ BytesToCopy = MIN(MIN(DestSize, RcvSize), Size);
+
+ // Do the copy using the intrinsic - we might want to
+ // do this with a function that does a smarter job of
+ // copying.
+
+ CTEMemCopy((uchar *)NdisBufferVirtualAddress(DestBuf) + DestOffset,
+ RcvBuf->ipr_buffer + RcvOffset, BytesToCopy);
+
+ BytesCopied += BytesToCopy;
+
+ if (!(RcvSize -= BytesToCopy)) {
+ // Exhausted this buffer.
+
+ RcvBuf = RcvBuf->ipr_next;
+
+ // If we have another one, use it.
+ if (RcvBuf != NULL) {
+ RcvOffset = 0;
+ RcvSize = RcvBuf->ipr_size;
+ } else
+ break;
+ } else // Buffer not exhausted, update offset.
+ RcvOffset += BytesToCopy;
+
+ // Now do the same thing for the destination buffer.
+ if (!(DestSize -= BytesToCopy)) {
+ // Exhausted this buffer.
+
+ DestBuf = NDIS_BUFFER_LINKAGE(DestBuf);
+
+ // If we have another one, use it.
+ if (DestBuf != NULL) {
+ DestOffset = 0;
+ DestSize = NdisBufferLength(DestBuf);
+ } else
+ break;
+ } else // Buffer not exhausted, update offset.
+ DestOffset += BytesToCopy;
+
+ Size -= BytesToCopy; // Decrement amount left to copy.
+ } while (Size);
+
+ }
+
+ return BytesCopied;
+
+
+}
+#endif // NT
+
+
+//* CopyRcvToBuffer - Copy from an IPRcvBuf chain to a flat buffer.
+//
+// Called during receive processing to copy from an IPRcvBuffer chain to a
+// flag buffer. We skip Offset bytes in the src chain, and then
+// copy Size bytes.
+//
+// Input: DestBuf - Pointer to destination buffer.
+// SrcRB - Pointer to SrcRB chain.
+// Size - Size in bytes to copy.
+// SrcOffset - Offset in SrcRB to start copying from.
+//
+// Returns: Nothing.
+//
+void
+CopyRcvToBuffer(uchar *DestBuf, IPRcvBuf *SrcRB, uint Size, uint SrcOffset)
+{
+#ifdef DEBUG
+ IPRcvBuf *TempRB;
+ uint TempSize;
+#endif
+
+ CTEAssert(DestBuf != NULL);
+ CTEAssert(SrcRB != NULL);
+
+ // In debug versions check to make sure we're copying a reasonable size
+ // and from a reasonable offset.
+
+#ifdef DEBUG
+ TempRB = SrcRB;
+ TempSize = 0;
+ while (TempRB != NULL) {
+ TempSize += TempRB->ipr_size;
+ TempRB = TempRB->ipr_next;
+ }
+
+ CTEAssert(SrcOffset < TempSize);
+ CTEAssert((SrcOffset + Size) <= TempSize);
+#endif
+
+ // First, skip Offset bytes.
+ while (SrcOffset >= SrcRB->ipr_size) {
+ SrcOffset -= SrcRB->ipr_size;
+ SrcRB = SrcRB->ipr_next;
+ }
+
+ while (Size != 0) {
+ uint BytesToCopy, SrcSize;
+
+ CTEAssert(SrcRB != NULL);
+
+ SrcSize = SrcRB->ipr_size - SrcOffset;
+ BytesToCopy = MIN(Size, SrcSize);
+ CTEMemCopy(DestBuf, SrcRB->ipr_buffer + SrcOffset, BytesToCopy);
+
+ if (BytesToCopy == SrcSize) {
+ // Copied everything from this buffer.
+ SrcRB = SrcRB->ipr_next;
+ SrcOffset = 0;
+ }
+
+ DestBuf += BytesToCopy;
+ Size -= BytesToCopy;
+ }
+
+}
+
+//* CopyFlatToNdis - Copy a flat buffer to an NDIS_BUFFER chain.
+//
+// A utility function to copy a flat buffer to an NDIS buffer chain. We
+// assume that the NDIS_BUFFER chain is big enough to hold the copy amount;
+// in a debug build we'll debugcheck if this isn't true. We return a pointer
+// to the buffer where we stopped copying, and an offset into that buffer.
+// This is useful for copying in pieces into the chain.
+//
+// Input: DestBuf - Destination NDIS_BUFFER chain.
+// SrcBuf - Src flat buffer.
+// Size - Size in bytes to copy.
+// StartOffset - Pointer to start of offset into first buffer in
+// chain. Filled in on return with the offset to
+// copy into next.
+// BytesCopied - Pointer to a variable into which to store the
+// number of bytes copied by this operation
+//
+// Returns: Pointer to next buffer in chain to copy into.
+//
+
+#ifdef NT
+
+PNDIS_BUFFER
+CopyFlatToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size,
+ uint *StartOffset, uint *BytesCopied)
+{
+ NTSTATUS Status = 0;
+
+ *BytesCopied = 0;
+
+ Status = TdiCopyBufferToMdl(SrcBuf, 0, Size, DestBuf, *StartOffset,
+ BytesCopied);
+
+ *StartOffset += *BytesCopied;
+
+ //
+ // Always return the first buffer, since the TdiCopy function handles
+ // finding the appropriate buffer based on offset.
+ //
+ return(DestBuf);
+
+}
+
+#else // NT
+
+PNDIS_BUFFER
+CopyFlatToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size,
+ uint *StartOffset, uint *BytesCopied)
+{
+ uint CopySize;
+ uchar *DestPtr;
+ uint DestSize;
+ uint Offset = *StartOffset;
+ uint bytesCopied = 0;
+
+ CTEAssert(DestBuf != NULL);
+ CTEAssert(SrcBuf != NULL);
+
+ CTEAssert(NdisBufferLength(DestBuf) >= Offset);
+ DestPtr = ((uchar *) NdisBufferVirtualAddress(DestBuf)) + Offset;
+ DestSize = NdisBufferLength(DestBuf) - Offset;
+
+ for (;;) {
+ CopySize = MIN(Size, DestSize);
+ CTEMemCopy(DestPtr, SrcBuf, CopySize);
+
+ DestPtr += CopySize;
+ SrcBuf += CopySize;
+ bytesCopied += CopySize;
+
+ if ((Size -= CopySize) == 0)
+ break;
+
+ if ((DestSize -= CopySize) == 0) {
+ DestBuf = NDIS_BUFFER_LINKAGE(DestBuf);
+ CTEAssert(DestBuf != NULL);
+ DestPtr = NdisBufferVirtualAddress(DestBuf);
+ DestSize = NdisBufferLength(DestBuf);
+ }
+ }
+
+ *StartOffset = DestPtr - NdisBufferVirtualAddress(DestBuf);
+ *BytesCopied = bytesCopied;
+
+ return DestBuf;
+
+}
+
+#endif // NT
+
+
+//* BuildTDIAddress - Build a TDI address structure.
+//
+// Called when we need to build a TDI address structure. We fill in
+// the specifed buffer with the correct information in the correct
+// format.
+//
+// Input: Buffer - Buffer to be filled in as TDI address structure.
+// Addr - IP Address to fill in.
+// Port - Port to be filled in.
+//
+// Returns: Nothing.
+//
+void
+BuildTDIAddress(uchar *Buffer, IPAddr Addr, ushort Port)
+{
+ PTRANSPORT_ADDRESS XportAddr;
+ PTA_ADDRESS TAAddr;
+
+ XportAddr = (PTRANSPORT_ADDRESS)Buffer;
+ XportAddr->TAAddressCount = 1;
+ TAAddr = XportAddr->Address;
+ TAAddr->AddressType = TDI_ADDRESS_TYPE_IP;
+ TAAddr->AddressLength = sizeof(TDI_ADDRESS_IP);
+ ((PTDI_ADDRESS_IP)TAAddr->Address)->sin_port = Port;
+ ((PTDI_ADDRESS_IP)TAAddr->Address)->in_addr = Addr;
+}
+
+//* UpdateConnInfo - Update a connection information structure.
+//
+// Called when we need to update a connection information structure. We
+// copy any options, and create a transport address. If any buffer is
+// too small we return an error.
+//
+// Input: ConnInfo - Pointer to TDI_CONNECTION_INFORMATION struc
+// to be filled in.
+// OptInfo - Pointer to IP options information.
+// SrcAddress - Source IP address.
+// SrcPort - Source port.
+//
+// Returns: TDI_SUCCESS if it worked, TDI_BUFFER_OVERFLOW for an error.
+//
+TDI_STATUS
+UpdateConnInfo(PTDI_CONNECTION_INFORMATION ConnInfo, IPOptInfo *OptInfo,
+ IPAddr SrcAddress, ushort SrcPort)
+{
+ TDI_STATUS Status = TDI_SUCCESS; // Default status to return.
+ uint AddrLength, OptLength;
+
+
+ if (ConnInfo != NULL) {
+ ConnInfo->UserDataLength = 0; // No user data.
+
+ // Fill in the options. If the provided buffer is too small,
+ // we'll truncate the options and return an error. Otherwise
+ // we'll copy the whole IP option buffer.
+ if (ConnInfo->OptionsLength) {
+ if (ConnInfo->OptionsLength < OptInfo->ioi_optlength) {
+ Status = TDI_BUFFER_OVERFLOW;
+ OptLength = ConnInfo->OptionsLength;
+ } else
+ OptLength = OptInfo->ioi_optlength;
+
+ CTEMemCopy(ConnInfo->Options, OptInfo->ioi_options, OptLength);
+
+ ConnInfo->OptionsLength = OptLength;
+ }
+
+ // Options are copied. Build a TRANSPORT_ADDRESS structure in
+ // the buffer.
+ if (AddrLength = ConnInfo->RemoteAddressLength) {
+
+ // Make sure we have at least enough to fill in the count and type.
+ if (AddrLength >= TCP_TA_SIZE) {
+
+ // The address fits. Fill it in.
+ ConnInfo->RemoteAddressLength = TCP_TA_SIZE;
+ BuildTDIAddress(ConnInfo->RemoteAddress, SrcAddress, SrcPort);
+
+ } else {
+ ConnInfo->RemoteAddressLength = 0;
+ Status = TDI_INVALID_PARAMETER;
+ }
+ }
+
+ }
+
+ return Status;
+
+}
diff --git a/private/ntos/tdi/tcpip/tcp/tlcommon.h b/private/ntos/tdi/tcpip/tcp/tlcommon.h
new file mode 100644
index 000000000..29790156c
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/tlcommon.h
@@ -0,0 +1,46 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** TLCOMMON.H - Common transport layer definitions.
+//
+// This file contains definitions for common transport layer items.
+//
+
+#define PHXSUM(s,d,p,l) (uint)( (uint)*(ushort *)&(s) + \
+ (uint)*(ushort *)((char *)&(s) + sizeof(ushort)) + \
+ (uint)*(ushort *)&(d) + \
+ (uint)*(ushort *)((char *)&(d) + sizeof(ushort)) + \
+ (uint)((ushort)net_short((p))) + \
+ (uint)((ushort)net_short((ushort)(l))) )
+
+
+#define TCP_TA_SIZE (offsetof(TRANSPORT_ADDRESS, Address->Address)+ \
+ sizeof(TDI_ADDRESS_IP))
+
+extern ushort XsumSendChain(uint PHXsum, PNDIS_BUFFER BufChain);
+extern ushort XsumRcvBuf(uint PHXsum, IPRcvBuf *BufChain);
+extern uint CopyRcvToNdis(IPRcvBuf *RcvBuf, PNDIS_BUFFER DestBuf,
+ uint Size, uint RcvOffset, uint DestOffset);
+extern TDI_STATUS UpdateConnInfo(PTDI_CONNECTION_INFORMATION ConnInfo,
+ IPOptInfo *OptInfo, IPAddr SrcAddress, ushort SrcPort);
+
+extern void BuildTDIAddress(uchar *Buffer, IPAddr Addr, ushort Port);
+
+extern void CopyRcvToBuffer(uchar *DestBuf, IPRcvBuf *SrcRB, uint Size,
+ uint Offset);
+
+extern PNDIS_BUFFER CopyFlatToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf,
+ uint Size, uint *Offset, uint *BytesCopied);
+
+extern void *TLRegisterProtocol(uchar Protocol, void *RcvHandler,
+ void *XmitHandler, void *StatusHandler,
+ void *RcvCmpltHandler);
+
+#ifdef VXD
+extern int TLRegisterDispatch(char *, struct TDIDispatchTable *);
+#endif
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/udp.c b/private/ntos/tdi/tcpip/tcp/udp.c
new file mode 100644
index 000000000..c9c284403
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/udp.c
@@ -0,0 +1,673 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** UDP.C - UDP protocol code.
+//
+// This file contains the code for the UDP protocol functions,
+// principally send and receive datagram.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#include "tdistat.h"
+#ifdef VXD
+#include "tdivxd.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "udp.h"
+#include "tlcommon.h"
+#include "info.h"
+#include "tcpcfg.h"
+#include "secfltr.h"
+
+#ifdef NT
+
+#ifdef POOL_TAGGING
+
+#ifdef ExAllocatePool
+#undef ExAllocatePool
+#endif
+
+#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'uPCT')
+
+#ifndef CTEAllocMem
+#error "CTEAllocMem is not already defined - will override tagging"
+#else
+#undef CTEAllocMem
+#endif
+
+#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'uPCT')
+
+#endif // POOL_TAGGING
+
+#endif // NT
+
+EXTERNAL_LOCK(AddrObjTableLock)
+
+void *UDPProtInfo = NULL;
+
+extern IPInfo LocalNetInfo;
+
+#ifdef CHICAGO
+extern uchar TransportName[];
+#endif
+
+#ifdef CHICAGO
+extern int RegisterAddrChangeHndlr(void *Handler, uint Add);
+extern void AddrChange(IPAddr Addr, IPMask Mask, void *Context,
+ uint Added);
+#endif
+
+
+
+//** UDPSend - Send a datagram.
+//
+// The real send datagram routine. We assume that the busy bit is
+// set on the input AddrObj, and that the address of the SendReq
+// has been verified.
+//
+// We start by sending the input datagram, and we loop until there's
+// nothing left on the send q.
+//
+// Input: SrcAO - Pointer to AddrObj doing the send.
+// SendReq - Pointer to sendreq describing send.
+//
+// Returns: Nothing
+//
+void
+UDPSend(AddrObj *SrcAO, DGSendReq *SendReq)
+{
+ UDPHeader *UH;
+ PNDIS_BUFFER UDPBuffer;
+ CTELockHandle HeaderHandle, AOHandle;
+ RouteCacheEntry *RCE; // RCE used for each send.
+ IPAddr SrcAddr; // Source address IP thinks we should
+ // use.
+ uchar DestType; // Type of destination address.
+ ushort UDPXsum; // Checksum of packet.
+ ushort SendSize; // Size we're sending.
+ IP_STATUS SendStatus; // Status of send attempt.
+ ushort MSS;
+ uint AddrValid;
+ IPOptInfo *OptInfo;
+ IPAddr OrigSrc;
+
+ CTEStructAssert(SrcAO, ao);
+ CTEAssert(SrcAO->ao_usecnt != 0);
+
+ //* Loop while we have something to send, and can get
+ // resources to send.
+ for (;;) {
+
+ CTEStructAssert(SendReq, dsr);
+
+ // Make sure we have a UDP header buffer for this send. If we
+ // don't, try to get one.
+ if ((UDPBuffer = SendReq->dsr_header) == NULL) {
+ // Don't have one, so try to get one.
+ CTEGetLock(&DGSendReqLock, &HeaderHandle);
+ UDPBuffer = GetDGHeader();
+ if (UDPBuffer != NULL)
+ SendReq->dsr_header = UDPBuffer;
+ else {
+ // Couldn't get a header buffer. Push the send request
+ // back on the queue, and queue the addr object for when
+ // we get resources.
+ CTEGetLock(&SrcAO->ao_lock, &AOHandle);
+ PUSHQ(&SrcAO->ao_sendq, &SendReq->dsr_q);
+ PutPendingQ(SrcAO);
+ CTEFreeLock(&SrcAO->ao_lock, AOHandle);
+ CTEFreeLock(&DGSendReqLock, HeaderHandle);
+ return;
+ }
+ CTEFreeLock(&DGSendReqLock, HeaderHandle);
+ }
+
+ // At this point, we have the buffer we need. Call IP to get an
+ // RCE (along with the source address if we need it), then compute
+ // the checksum and send the data.
+ CTEAssert(UDPBuffer != NULL);
+
+ if (!CLASSD_ADDR(SendReq->dsr_addr)) {
+ // This isn't a multicast send, so we'll use the ordinary
+ // information.
+ OrigSrc = SrcAO->ao_addr;
+ OptInfo = &SrcAO->ao_opt;
+ } else {
+ OrigSrc = SrcAO->ao_mcastaddr;
+ OptInfo = &SrcAO->ao_mcastopt;
+ }
+
+ if (!(SrcAO->ao_flags & AO_DHCP_FLAG)) {
+ SrcAddr = (*LocalNetInfo.ipi_openrce)(SendReq->dsr_addr,
+ OrigSrc, &RCE, &DestType, &MSS, OptInfo);
+
+ AddrValid = !IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR);
+ } else {
+ // This is a DHCP send. He really wants to send from the
+ // NULL IP address.
+ SrcAddr = NULL_IP_ADDR;
+ RCE = NULL;
+ AddrValid = TRUE;
+ }
+
+ if (AddrValid) {
+ // The OpenRCE worked. Compute the checksum, and send it.
+
+ if (!IP_ADDR_EQUAL(OrigSrc, NULL_IP_ADDR))
+ SrcAddr = OrigSrc;
+
+ UH = (UDPHeader *)((uchar *)NdisBufferVirtualAddress(UDPBuffer) +
+ LocalNetInfo.ipi_hsize);
+ NdisBufferLength(UDPBuffer) = sizeof(UDPHeader);
+ NDIS_BUFFER_LINKAGE(UDPBuffer) = SendReq->dsr_buffer;
+ UH->uh_src = SrcAO->ao_port;
+ UH->uh_dest = SendReq->dsr_port;
+ SendSize = SendReq->dsr_size + sizeof(UDPHeader);
+ UH->uh_length = net_short(SendSize);
+ UH->uh_xsum = 0;
+
+ if (AO_XSUM(SrcAO)) {
+ // Compute the header xsum, and then call XsumNdisChain
+ UDPXsum = XsumSendChain(PHXSUM(SrcAddr, SendReq->dsr_addr,
+ PROTOCOL_UDP, SendSize), UDPBuffer);
+
+ // We need to negate the checksum, unless it's already all
+ // ones. In that case negating it would take it to 0, and
+ // then we'd have to set it back to all ones.
+ if (UDPXsum != 0xffff)
+ UDPXsum =~UDPXsum;
+
+ UH->uh_xsum = UDPXsum;
+
+ }
+
+ // We've computed the xsum. Now send the packet.
+ UStats.us_outdatagrams++;
+ SendStatus = (*LocalNetInfo.ipi_xmit)(UDPProtInfo, SendReq,
+ UDPBuffer, (uint)SendSize, SendReq->dsr_addr, SrcAddr,
+ OptInfo, RCE, PROTOCOL_UDP);
+
+ (*LocalNetInfo.ipi_closerce)(RCE);
+
+ // If it completed immediately, give it back to the user.
+ // Otherwise we'll complete it when the SendComplete happens.
+ // Currently, we don't map the error code from this call - we
+ // might need to in the future.
+ if (SendStatus != IP_PENDING)
+ DGSendComplete(SendReq, UDPBuffer);
+
+ } else {
+ TDI_STATUS Status;
+
+ if (DestType == DEST_INVALID)
+ Status = TDI_BAD_ADDR;
+ else
+ Status = TDI_DEST_UNREACHABLE;
+
+ // Complete the request with an error.
+ (*SendReq->dsr_rtn)(SendReq->dsr_context, Status, 0);
+ // Now free the request.
+ SendReq->dsr_rtn = NULL;
+ DGSendComplete(SendReq, UDPBuffer);
+ }
+
+ CTEGetLock(&SrcAO->ao_lock, &AOHandle);
+
+ if (!EMPTYQ(&SrcAO->ao_sendq)) {
+ DEQUEUE(&SrcAO->ao_sendq, SendReq, DGSendReq, dsr_q);
+ CTEFreeLock(&SrcAO->ao_lock, AOHandle);
+ } else {
+ CLEAR_AO_REQUEST(SrcAO, AO_SEND);
+ CTEFreeLock(&SrcAO->ao_lock, AOHandle);
+ return;
+ }
+
+ }
+}
+
+
+//* UDPDeliver - Deliver a datagram to a user.
+//
+// This routine delivers a datagram to a UDP user. We're called with
+// the AddrObj to deliver on, and with the AddrObjTable lock held.
+// We try to find a receive on the specified AddrObj, and if we do
+// we remove it and copy the data into the buffer. Otherwise we'll
+// call the receive datagram event handler, if there is one. If that
+// fails we'll discard the datagram.
+//
+// Input: RcvAO - AO to receive the datagram.
+// SrcIP - Source IP address of datagram.
+// SrcPort - Source port of datagram.
+// RcvBuf - The IPReceive buffer containing the data.
+// RcvSize - Size received, including the UDP header.
+// TableHandle - Lock handle for AddrObj table.
+//
+// Returns: Nothing.
+//
+void
+UDPDeliver(AddrObj *RcvAO, IPAddr SrcIP, ushort SrcPort, IPRcvBuf *RcvBuf,
+ uint RcvSize, IPOptInfo *OptInfo, CTELockHandle TableHandle, uchar IsBCast)
+{
+ Queue *CurrentQ;
+ CTELockHandle AOHandle;
+ DGRcvReq *RcvReq;
+ uint BytesTaken = 0;
+ uchar AddressBuffer[TCP_TA_SIZE];
+ uint RcvdSize;
+ EventRcvBuffer *ERB = NULL;
+
+ CTEStructAssert(RcvAO, ao);
+
+ CTEGetLock(&RcvAO->ao_lock, &AOHandle);
+ CTEFreeLock(&AddrObjTableLock, AOHandle);
+
+ if (AO_VALID(RcvAO)) {
+
+ CurrentQ = QHEAD(&RcvAO->ao_rcvq);
+
+ // Walk the list, looking for a receive buffer that matches.
+ while (CurrentQ != QEND(&RcvAO->ao_rcvq)) {
+ RcvReq = QSTRUCT(DGRcvReq, CurrentQ, drr_q);
+
+ CTEStructAssert(RcvReq, drr);
+
+ // If this request is a wildcard request, or matches the source IP
+ // address, check the port.
+
+ if (IP_ADDR_EQUAL(RcvReq->drr_addr, NULL_IP_ADDR) ||
+ IP_ADDR_EQUAL(RcvReq->drr_addr, SrcIP)) {
+
+ // The local address matches, check the port. We'll match
+ // either 0 or the actual port.
+ if (RcvReq->drr_port == 0 || RcvReq->drr_port == SrcPort) {
+
+ TDI_STATUS Status;
+
+ // The ports matched. Remove this from the queue.
+ REMOVEQ(&RcvReq->drr_q);
+
+ // We're done. We can free the AddrObj lock now.
+ CTEFreeLock(&RcvAO->ao_lock, TableHandle);
+
+ // Call CopyRcvToNdis, and then complete the request.
+ RcvdSize = CopyRcvToNdis(RcvBuf, RcvReq->drr_buffer,
+ RcvReq->drr_size, sizeof(UDPHeader), 0);
+
+ CTEAssert(RcvdSize <= RcvReq->drr_size);
+
+ Status = UpdateConnInfo(RcvReq->drr_conninfo, OptInfo,
+ SrcIP, SrcPort);
+
+ UStats.us_indatagrams++;
+
+ (*RcvReq->drr_rtn)(RcvReq->drr_context, Status, RcvdSize);
+
+ FreeDGRcvReq(RcvReq);
+
+ return;
+ }
+ }
+
+ // Either the IP address or the port didn't match. Get the next
+ // one.
+ CurrentQ = QNEXT(CurrentQ);
+ }
+
+ // We've walked the list, and not found a buffer. Call the recv.
+ // handler now.
+
+ if (RcvAO->ao_rcvdg != NULL) {
+ PRcvDGEvent RcvEvent = RcvAO->ao_rcvdg;
+ PVOID RcvContext = RcvAO->ao_rcvdgcontext;
+ TDI_STATUS RcvStatus;
+ CTELockHandle OldLevel;
+ ULONG Flags = TDI_RECEIVE_COPY_LOOKAHEAD;
+
+
+ REF_AO(RcvAO);
+ CTEFreeLock(&RcvAO->ao_lock, TableHandle);
+
+ BuildTDIAddress(AddressBuffer, SrcIP, SrcPort);
+
+ UStats.us_indatagrams++;
+ if (IsBCast) {
+ Flags |= TDI_RECEIVE_BROADCAST;
+ }
+ RcvStatus = (*RcvEvent)(RcvContext, TCP_TA_SIZE,
+ (PTRANSPORT_ADDRESS)AddressBuffer, OptInfo->ioi_optlength,
+ OptInfo->ioi_options, Flags,
+ RcvBuf->ipr_size - sizeof(UDPHeader),
+ RcvSize - sizeof(UDPHeader), &BytesTaken,
+ RcvBuf->ipr_buffer + sizeof(UDPHeader), &ERB);
+
+ if (RcvStatus == TDI_MORE_PROCESSING) {
+ CTEAssert(ERB != NULL);
+
+ // We were passed back a receive buffer. Copy the data in now.
+
+ // He can't have taken more than was in the indicated
+ // buffer, but in debug builds we'll check to make sure.
+
+ CTEAssert(BytesTaken <= (RcvBuf->ipr_size - sizeof(UDPHeader)));
+
+#ifdef VXD
+ RcvdSize = CopyRcvToNdis(RcvBuf, ERB->erb_buffer,
+ ERB->erb_size, sizeof(UDPHeader) + BytesTaken, 0);
+
+ //
+ // Call the completion routine.
+ //
+ (*ERB->erb_rtn)(ERB->erb_context, TDI_SUCCESS, RcvdSize);
+
+#endif // VXD
+
+#ifdef NT
+ {
+ PIO_STACK_LOCATION IrpSp;
+ PTDI_REQUEST_KERNEL_RECEIVEDG DatagramInformation;
+
+ IrpSp = IoGetCurrentIrpStackLocation(ERB);
+ DatagramInformation = (PTDI_REQUEST_KERNEL_RECEIVEDG)
+ &(IrpSp->Parameters);
+
+ //
+ // Copy the remaining data to the IRP.
+ //
+ RcvdSize = CopyRcvToNdis(RcvBuf, ERB->MdlAddress,
+ RcvSize - sizeof(UDPHeader) - BytesTaken,
+ sizeof(UDPHeader) + BytesTaken, 0);
+
+ //
+ // Update the return address info
+ //
+ RcvStatus = UpdateConnInfo(
+ DatagramInformation->ReturnDatagramInformation,
+ OptInfo, SrcIP, SrcPort);
+
+ //
+ // Complete the IRP.
+ //
+ ERB->IoStatus.Information = RcvdSize;
+ ERB->IoStatus.Status = RcvStatus;
+ IoCompleteRequest(ERB, 2);
+ }
+#endif // NT
+
+ }
+ else {
+ CTEAssert(
+ (RcvStatus == TDI_SUCCESS) ||
+ (RcvStatus == TDI_NOT_ACCEPTED)
+ );
+
+ CTEAssert(ERB == NULL);
+ }
+
+ DELAY_DEREF_AO(RcvAO);
+
+ return;
+
+ } else
+ UStats.us_inerrors++;
+
+ // When we get here, we didn't have a buffer to put this data into.
+ // Fall through to the return case.
+ } else
+ UStats.us_inerrors++;
+
+ CTEFreeLock(&RcvAO->ao_lock, TableHandle);
+
+}
+
+
+//* UDPRcv - Receive a UDP datagram.
+//
+// The routine called by IP when a UDP datagram arrived. We
+// look up the port/local address pair in our address table,
+// and deliver the data to a user if we find one. For broadcast
+// frames we may deliver it to multiple users.
+//
+// Entry: IPContext - IPContext identifying physical i/f that
+// received the data.
+// Dest - IPAddr of destionation.
+// Src - IPAddr of source.
+// LocalAddr - Local address of network which caused this to be
+// received.
+// SrcAddr - Address of local interface which received the packet
+// IPH - IP Header.
+// IPHLength - Bytes in IPH.
+// RcvBuf - Pointer to receive buffer chain containing data.
+// Size - Size in bytes of data received.
+// IsBCast - Boolean indicator of whether or not this came in as
+// a bcast.
+// Protocol - Protocol this came in on - should be UDP.
+// OptInfo - Pointer to info structure for received options.
+//
+// Returns: Status of reception. Anything other than IP_SUCCESS will cause
+// IP to send a 'port unreachable' message.
+//
+IP_STATUS
+UDPRcv(void *IPContext, IPAddr Dest, IPAddr Src, IPAddr LocalAddr,
+ IPAddr SrcAddr, IPHeader UNALIGNED *IPH, uint IPHLength, IPRcvBuf *RcvBuf,
+ uint IPSize, uchar IsBCast, uchar Protocol, IPOptInfo *OptInfo)
+{
+ UDPHeader UNALIGNED *UH;
+ CTELockHandle AOTableHandle;
+ AddrObj *ReceiveingAO;
+ uint Size;
+ uchar DType;
+
+ DType = (*LocalNetInfo.ipi_getaddrtype)(Src);
+
+ // The following code relies on DEST_INVALID being a broadcast dest type.
+ // If this is changed the code here needs to change also.
+ if (IS_BCAST_DEST(DType)) {
+ if (!IP_ADDR_EQUAL(Src, NULL_IP_ADDR) || !IsBCast) {
+ UStats.us_inerrors++;
+ return IP_SUCCESS; // Bad src address.
+ }
+ }
+
+ UH = (UDPHeader *)RcvBuf->ipr_buffer;
+
+ Size = (uint)(net_short(UH->uh_length));
+
+ if (Size < sizeof(UDPHeader)) {
+ UStats.us_inerrors++;
+ return IP_SUCCESS; // Size is too small.
+ }
+
+ if (Size != IPSize) {
+ // Size doesn't match IP datagram size. If the size is larger
+ // than the datagram, throw it away. If it's smaller, truncate the
+ // recv. buffer.
+ if (Size < IPSize) {
+ IPRcvBuf *TempBuf = RcvBuf;
+ uint TempSize = Size;
+
+ while (TempBuf != NULL) {
+ TempBuf->ipr_size = MIN(TempBuf->ipr_size, TempSize);
+ TempSize -= TempBuf->ipr_size;
+ TempBuf = TempBuf->ipr_next;
+ }
+ } else {
+ // Size is too big, toss it.
+ UStats.us_inerrors++;
+ return IP_SUCCESS;
+ }
+ }
+
+
+ if (UH->uh_xsum != 0) {
+ if (XsumRcvBuf(PHXSUM(Src, Dest, PROTOCOL_UDP, Size), RcvBuf) != 0xffff) {
+ UStats.us_inerrors++;
+ return IP_SUCCESS; // Checksum failed.
+ }
+ }
+
+ CTEGetLock(&AddrObjTableLock, &AOTableHandle);
+
+#ifdef SECFLTR
+ //
+ // See if we are filtering the destination interface/port.
+ //
+ if ( !SecurityFilteringEnabled ||
+ IsPermittedSecurityFilter(
+ SrcAddr,
+ IPContext,
+ PROTOCOL_UDP,
+ (ulong) net_short(UH->uh_dest)
+ )
+ )
+ {
+#endif // SECFLTR
+
+ // Try to find an AddrObj to give this to. In the broadcast case, we
+ // may have to do this multiple times. If it isn't a broadcast, just
+ // get the best match and deliver it to them.
+
+ if (!IsBCast) {
+ ReceiveingAO = GetBestAddrObj(Dest, UH->uh_dest, PROTOCOL_UDP);
+ if (ReceiveingAO != NULL) {
+ UDPDeliver(ReceiveingAO, Src, UH->uh_src, RcvBuf, Size,
+ OptInfo, AOTableHandle, IsBCast);
+ return IP_SUCCESS;
+ } else {
+ CTEFreeLock(&AddrObjTableLock, AOTableHandle);
+ UStats.us_noports++;
+ return IP_GENERAL_FAILURE;
+ }
+ } else {
+ // This is a broadcast, we'll need to loop.
+
+ AOSearchContext Search;
+
+ DType = (*LocalNetInfo.ipi_getaddrtype)(Dest);
+
+ ReceiveingAO = GetFirstAddrObj(LocalAddr, UH->uh_dest, PROTOCOL_UDP,
+ &Search);
+ if (ReceiveingAO != NULL) {
+ do {
+ if ((DType != DEST_MCAST) ||
+ ((DType == DEST_MCAST) &&
+ MCastAddrOnAO(ReceiveingAO, Dest))) {
+ UDPDeliver(ReceiveingAO, Src, UH->uh_src, RcvBuf, Size,
+ OptInfo, AOTableHandle, IsBCast);
+ CTEGetLock(&AddrObjTableLock, &AOTableHandle);
+ }
+ ReceiveingAO = GetNextAddrObj(&Search);
+ } while (ReceiveingAO != NULL);
+ } else
+ UStats.us_noports++;
+ }
+
+#ifdef SECFLTR
+ }
+#endif // SECFLTR
+
+ CTEFreeLock(&AddrObjTableLock, AOTableHandle);
+
+ return IP_SUCCESS;
+}
+
+//* UDPStatus - Handle a status indication.
+//
+// This is the UDP status handler, called by IP when a status event
+// occurs. For most of these we do nothing. For certain severe status
+// events we will mark the local address as invalid.
+//
+// Entry: StatusType - Type of status (NET or HW). NET status
+// is usually caused by a received ICMP
+// message. HW status indicate a HW
+// problem.
+// StatusCode - Code identifying IP_STATUS.
+// OrigDest - If this is NET status, the original dest. of
+// DG that triggered it.
+// OrigSrc - " " " " " , the original src.
+// Src - IP address of status originator (could be local
+// or remote).
+// Param - Additional information for status - i.e. the
+// param field of an ICMP message.
+// Data - Data pertaining to status - for NET status, this
+// is the first 8 bytes of the original DG.
+//
+// Returns: Nothing
+//
+void
+UDPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest,
+ IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data)
+{
+ // If this is a HW status, it could be because we've had an address go
+ // away.
+ if (StatusType == IP_HW_STATUS) {
+
+ if (StatusCode == IP_ADDR_DELETED) {
+ //
+ // An address has gone away. OrigDest identifies the address.
+ //
+#ifndef _PNP_POWER
+ //
+ // This is done via TDI notifications in the PNP world.
+ //
+ InvalidateAddrs(OrigDest);
+
+#endif // _PNP_POWER
+
+#ifdef SECFLTR
+ //
+ // Delete any security filters associated with this address
+ //
+ DeleteProtocolSecurityFilter(OrigDest, PROTOCOL_UDP);
+
+#endif // SECFLTR
+
+ return;
+ }
+
+ if (StatusCode == IP_ADDR_ADDED) {
+
+#ifdef SECFLTR
+ //
+ // An address has materialized. OrigDest identifies the address.
+ // Data is a handle to the IP configuration information for the
+ // interface on which the address is instantiated.
+ //
+ AddProtocolSecurityFilter(OrigDest, PROTOCOL_UDP,
+ (NDIS_HANDLE) Data);
+#endif // SECFLTR
+
+ return;
+ }
+
+#ifdef CHICAGO
+ if (StatusCode == IP_UNLOAD) {
+ // IP is telling us we're being unloaded. First, deregister
+ // with VTDI, and then call CTEUnload().
+ (void)TLRegisterProtocol(PROTOCOL_UDP, NULL, NULL, NULL, NULL);
+
+#ifdef UDP_ONLY
+ // Only do the following in the UDP_ONLY version. TCP does it in
+ // the generic version.
+ TLRegisterDispatch(TransportName, NULL);
+ (void)RegisterAddrChangeHndlr(AddrChange, FALSE);
+ CTEUnload(TransportName);
+#endif // UDP_ONLY
+
+ return;
+ }
+#endif // CHICAGO
+ }
+}
+
diff --git a/private/ntos/tdi/tcpip/tcp/udp.h b/private/ntos/tdi/tcpip/tcp/udp.h
new file mode 100644
index 000000000..4ba5ec5e0
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/udp.h
@@ -0,0 +1,39 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** UDP. - UDP protocol definitions.
+//
+// This file contains definitions for the UDP protocol functions.
+//
+
+#include "dgram.h"
+
+#define PROTOCOL_UDP 17 // UDP protocol number
+
+//* Structure of a UDP header.
+struct UDPHeader {
+ ushort uh_src; // Source port.
+ ushort uh_dest; // Destination port.
+ ushort uh_length; // Length
+ ushort uh_xsum; // Checksum.
+}; /* UDPHeader */
+
+typedef struct UDPHeader UDPHeader;
+
+
+//* External definition of exported functions.
+extern IP_STATUS UDPRcv(void *IPContext, IPAddr Dest, IPAddr Src,
+ IPAddr LocalAddr, IPAddr SrcAddr,
+ IPHeader UNALIGNED *IPH, uint IPHLength,
+ IPRcvBuf *RcvBuf, uint Size, uchar IsBCast,
+ uchar Protocol, IPOptInfo *OptInfo);
+
+extern void UDPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest,
+ IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data);
+
+extern void UDPSend(AddrObj *SrcAO, DGSendReq *SendReq);
+
+
diff --git a/private/ntos/tdi/tcpip/tcp/up/makefile b/private/ntos/tdi/tcpip/tcp/up/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/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/tcpip/tcp/up/sources b/private/ntos/tdi/tcpip/tcp/up/sources
new file mode 100644
index 000000000..75e90b2f7
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/up/sources
@@ -0,0 +1,30 @@
+!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
+
+LINKLIBS=..\..\ip\up\obj\*\ip.lib
+TARGETPATH=obj
+
+!include ..\sources.inc
diff --git a/private/ntos/tdi/tcpip/tcp/up/tcpip.prf b/private/ntos/tdi/tcpip/tcp/up/tcpip.prf
new file mode 100644
index 000000000..4f03cef97
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/up/tcpip.prf
@@ -0,0 +1,297 @@
+TdiQueryInformation@20
+TCPDispatchInternalDeviceControl@8
+DerefTCB@8
+FreeARPBuffer@8
+ProcessTCBDelayQ@0
+ARPTransmit@16
+TCPRcv@48
+IPRcv@28
+IPRcvComplete@0
+GetARPBuffer@12
+FreeIPPacket@4
+TCBTimeout@8
+ARPTimeout@8
+ICMPTimer@4
+IPTimeout@8
+IGMPTimer@4
+IndicateData@16
+TCPSendComplete@8
+GetSendReq@0
+FreeSendReq@4
+FillTCPHeader@8
+RcvWin@4
+IPTransmit@36
+GetRcvReq@0
+GetTCPHeader@0
+FindTCB@16
+TCPSendData@8
+TdiSend@16
+TCPSend@8
+ACKData@8
+FreeRBChain@4
+FreeRcvReq@4
+TCPDataRequestComplete@12
+FindUserRcv@4
+TCPRcvComplete@0
+GetLocalNTE@8
+GetConnFromConnID@4
+ARPSendData@16
+XsumRcvBuf@8
+FreeTCPHeader@4
+ARPRcv@28
+XsumSendChain@8
+CTEStartTimer@16
+DeliverToUser@32
+GetIPPacket@0
+ARPRcvComplete@4
+CTESystemUpTime@0
+ARPSendComplete@12
+IPSendComplete@12
+DelayAction@8
+TCPPrepareIrpForCancel@12
+BufferData@16
+SendACK@4
+CompleteRcvs@4
+FreePartialRB@8
+GetAddrObj@16
+AddrOnIF@8
+FindRTE@20
+TdiCopyBufferToMdl@24
+TCPGetMdlChainByteCount@4
+IPHash@4
+LookupRTE@12
+OpenRCE@24
+TCPQueryInformation@8
+CopyFlatToNdis@20
+FindListenConn@16
+DummyDone@8
+GetAddrType@4
+InitTCBFromConn@16
+tcpxsum@12
+FindMSS@4
+DelayDerefAO@4
+FindRCE@12
+TryToCloseTCB@12
+InitRCE@4
+NotifyOfDisc@12
+UpdateConnInfo@16
+GoToEstab@4
+TCPDisconnect@8
+FreeConnReq@4
+TdiDisconnect@20
+IPGetLocalMTU@8
+CloseRCE@4
+IPInitOptions@4
+AdjustRcvWin@4
+BuildTDIAddress@12
+SendSYN@8
+AllocTCB@0
+ProcessUserOptions@8
+FreeTCB@4
+RemoveTCBFromConn@4
+InitSendState@4
+CompleteConnReq@12
+IPFreeOptions@4
+TCPRequestComplete@12
+InvalidSourceAddress@4
+AcceptConn@8
+RemoveTCB@4
+GetConnReq@0
+CloseTCB@8
+RemoveConnFromTCB@4
+FinishRemoveTCBFromConn@4
+IsBCastOnNTE@8
+InsertTCB@4
+ResetSendNext@8
+ARPLookup@12
+BCastRcv@40
+IPGetAddrType@4
+UDPRcv@48
+GetFirstAddrObj@16
+IPForward@32
+GetNextAddrObj@4
+UDPDeliver@32
+LockedDerefIF@4
+BestNTEForIF@8
+ARPInvalidate@8
+IsBCastOnIF@8
+ARPSendBCast@16
+UDPSendDatagram@8
+DGSendComplete@8
+TdiSendDatagram@20
+DerefAO@4
+FreeDGSendReq@4
+GetDGSendReq@0
+FreeDGHeader@4
+UDPSend@8
+GetAddress@12
+ARPRemoveRCE@8
+HandleARPPacket@24
+IsLocalAddr@8
+ARPLocalAddr@8
+IPRouteTimeout@8
+GetConnID@4
+TdiAssociateAddress@8
+FindEA@12
+TCPCreate@12
+TCPAssociateAddress@8
+TdiOpenConnection@8
+TCPDispatch@8
+TdiMapUserRequest@12
+IPGetPInfo@16
+SendARPPacket@40
+RemoveARPTableEntry@8
+CreateARPTableEntry@12
+SendARPRequest@20
+GrowARPHeaders@4
+GrowTCPHeaderList@0
+TdiCloseConnection@4
+RemoveConnFromAO@8
+TCPCloseObjectComplete@12
+TCPClose@8
+CloseDone@8
+TCPCleanup@12
+FreeConnID@4
+DummyCmplt@12
+LoopAddAddr@16
+FindIGMPAddr@12
+CTEInitEvent@8
+TdiSetEvent@16
+ARPSetMCastList@4
+IPGetInfo@8
+IGMPAddrChange@12
+ICMPInit@4
+IGMPInit@0
+ARPRequestComplete@12
+FreeAORequest@4
+TCPQueryInformationEx@8
+ARPFindMCast@12
+InitAdapter@4
+CTESignal@8
+CTEBlock@4
+CTEInitTimer@4
+InsertAddrObj@4
+TdiQueryInformationEx@20
+ARPAddAddr@16
+TdiOpenAddress@16
+InitNTERouting@12
+ARPAddMCast@8
+InitIGMPForNTE@4
+FindAnyAddrObj@8
+GrowDGHeaderList@0
+FindSpecificRTE@20
+InitNTE@4
+CopyToNdis@16
+IPRegisterProtocol@20
+GetAddrOptions@12
+InitInterface@8
+TdiSetInformationEx@16
+TCPConnect@8
+TCPQueryInformationExComplete@12
+TCPSetInformationEx@8
+LockedAddRoute@36
+TCPAcdBind@0
+AddRoute@36
+TdiConnect@16
+LoopXmit@16
+AddValueSecurityFilter@12
+InitGateway@4
+ARPOAComplete@12
+CTEScheduleEvent@8
+GetHashMask@8
+InitLoopback@4
+RawStatus@28
+FindInterfaceEntry@4
+FindProtocolEntry@8
+AddProtocolSecurityFilter@12
+TdiRegisterNetAddress@8
+UDPStatus@28
+ARPDynRegister@36
+FindInsertPoint@4
+IPAddInterface@20
+NotifyAddrChange@28
+TCPStatus@28
+TdiInitialize@0
+TdiRegisterDeviceObject@8
+TdiRegisterAddressChangeHandler@12
+CTEInitialize@0
+ARPBindAdapter@20
+ICMPStatus@28
+IPQueryInfo@16
+DeleteProtocolValueEntries@4
+ModifyProtocolEntry@12
+ModifyInterfaceEntry@16
+ModifySecurityFilter@16
+DoNDISRequest@24
+LoopGetEList@12
+TCPDisassociateAddress@8
+TdiDisAssociateAddress@4
+EnumRegMultiSz@12
+AddressArrival@4
+ARPGetEList@12
+GrowIPPacketList@0
+IPGetEList@8
+RTValidateContext@8
+RTReadNext@8
+GetCurrentRouteTable@4
+AddNTERoutes@4
+GetSecurityFilterList@12
+LoopXmitRtn@8
+InitDG@4
+IPGetConfig@0
+InitTimestamp@0
+IPProcessAdapterSection@8
+TCPInitializeParameter@12
+TLGetIPInfo@8
+InitializeSecurityFilters@0
+IPFreeConfig@4
+InitTCPRcv@0
+InitAddr@0
+IPInit@0
+GetTime@0
+ARPInit@0
+InitTCPConn@0
+IPProcessConfiguration@0
+IPDriverEntry@8
+tlinit@0
+InitTCPSend@0
+InitTCB@0
+GetIFAddrList@8
+ARPOpen@4
+TCPDispatchDeviceControl@8
+TCPSetEventHandler@8
+ARPRegister@12
+GetRegStringValue@16
+IsDHCPZeroAddress@4
+EnumSecurityFilterValue@12
+CloseIFConfig@4
+UseEtherSNAP@4
+OpenIFConfig@8
+IPConvertStringToAddress@8
+SetRegDWORDValue@12
+GetArpCacheLife@0
+InitRegDWORDParameter@16
+GetRegMultiSZValue@12
+OpenRegKey@8
+GetGeneralIFConfig@8
+GetAlwaysSourceRoute@0
+GetRegSZValue@16
+GetRegDWORDValue@12
+IsLLInterfaceValueNull@4
+DerefIF@4
+ARPQueryInfo@20
+DriverEntry@8
+TCPGetConfigInfo@0
+SendIPPacket@28
+GetIPHdrBuffer@0
+GrowHdrBufList@0
+FreeIPHdrBuffer@4
+InitRouting@4
+TLRegisterProtocol@20
+LookupNextHopWithBuffer@28
+SetPersistentRoutesForNTE@12
+SendARPReply@28
+GetGMTDelta@0
+SendRSTFromHeader@20
+InvalidateRCEChain@4
+LoopQInfo@20