From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/tdi/tcpip/dirs | 28 + private/ntos/tdi/tcpip/h/oscfg.h | 72 + private/ntos/tdi/tcpip/h/packoff.h | 37 + private/ntos/tdi/tcpip/h/packon.h | 35 + private/ntos/tdi/tcpip/h/queue.h | 87 + private/ntos/tdi/tcpip/h/tdint.h | 41 + private/ntos/tdi/tcpip/ip/arp.c | 4839 ++++++++++++++++++++++++++++++ private/ntos/tdi/tcpip/ip/arp.h | 21 + private/ntos/tdi/tcpip/ip/arpdef.h | 340 +++ private/ntos/tdi/tcpip/ip/dirs | 22 + private/ntos/tdi/tcpip/ip/icmp.c | 1698 +++++++++++ private/ntos/tdi/tcpip/ip/icmp.h | 70 + private/ntos/tdi/tcpip/ip/igmp.c | 799 +++++ private/ntos/tdi/tcpip/ip/igmp.h | 42 + private/ntos/tdi/tcpip/ip/info.c | 609 ++++ private/ntos/tdi/tcpip/ip/info.h | 22 + private/ntos/tdi/tcpip/ip/init.c | 3509 ++++++++++++++++++++++ private/ntos/tdi/tcpip/ip/ipdef.h | 371 +++ private/ntos/tdi/tcpip/ip/ipinit.h | 155 + private/ntos/tdi/tcpip/ip/iploop.c | 665 ++++ private/ntos/tdi/tcpip/ip/iprcv.c | 1145 +++++++ private/ntos/tdi/tcpip/ip/iproute.c | 4703 +++++++++++++++++++++++++++++ private/ntos/tdi/tcpip/ip/iproute.h | 107 + private/ntos/tdi/tcpip/ip/iprtdef.h | 135 + private/ntos/tdi/tcpip/ip/ipstatus.c | 205 ++ private/ntos/tdi/tcpip/ip/ipxmit.c | 1949 ++++++++++++ private/ntos/tdi/tcpip/ip/ipxmit.h | 34 + private/ntos/tdi/tcpip/ip/mp/makefile | 6 + private/ntos/tdi/tcpip/ip/mp/sources | 27 + private/ntos/tdi/tcpip/ip/ntip.c | 3361 +++++++++++++++++++++ private/ntos/tdi/tcpip/ip/ntirp.c | 1458 +++++++++ private/ntos/tdi/tcpip/ip/ntreg.c | 672 +++++ private/ntos/tdi/tcpip/ip/sources.inc | 59 + private/ntos/tdi/tcpip/ip/up/makefile | 6 + private/ntos/tdi/tcpip/ip/up/sources | 27 + private/ntos/tdi/tcpip/tcp/addr.c | 2061 +++++++++++++ private/ntos/tdi/tcpip/tcp/addr.h | 211 ++ private/ntos/tdi/tcpip/tcp/alpha/sources | 3 + private/ntos/tdi/tcpip/tcp/alpha/xsum.s | 271 ++ private/ntos/tdi/tcpip/tcp/dgram.c | 990 ++++++ private/ntos/tdi/tcpip/tcp/dgram.h | 89 + private/ntos/tdi/tcpip/tcp/dirs | 22 + private/ntos/tdi/tcpip/tcp/i386/sources | 4 + private/ntos/tdi/tcpip/tcp/i386/xsum.asm | 259 ++ private/ntos/tdi/tcpip/tcp/info.c | 917 ++++++ private/ntos/tdi/tcpip/tcp/info.h | 51 + private/ntos/tdi/tcpip/tcp/init.c | 597 ++++ private/ntos/tdi/tcpip/tcp/mips/sources | 4 + private/ntos/tdi/tcpip/tcp/mips/xsum.s | 243 ++ private/ntos/tdi/tcpip/tcp/mp/makefile | 6 + private/ntos/tdi/tcpip/tcp/mp/sources | 30 + private/ntos/tdi/tcpip/tcp/mp/tcpip.prf | 297 ++ private/ntos/tdi/tcpip/tcp/ntautodl.c | 258 ++ private/ntos/tdi/tcpip/tcp/ntdisp.c | 4063 +++++++++++++++++++++++++ private/ntos/tdi/tcpip/tcp/ntinit.c | 1038 +++++++ private/ntos/tdi/tcpip/tcp/ppc/sources | 4 + private/ntos/tdi/tcpip/tcp/ppc/xsum.s | 257 ++ private/ntos/tdi/tcpip/tcp/raw.c | 693 +++++ private/ntos/tdi/tcpip/tcp/raw.h | 34 + private/ntos/tdi/tcpip/tcp/secfltr.c | 1425 +++++++++ private/ntos/tdi/tcpip/tcp/secfltr.h | 61 + private/ntos/tdi/tcpip/tcp/sources.inc | 67 + private/ntos/tdi/tcpip/tcp/tcb.c | 1524 ++++++++++ private/ntos/tdi/tcpip/tcp/tcb.h | 67 + private/ntos/tdi/tcpip/tcp/tcp.h | 426 +++ private/ntos/tdi/tcpip/tcp/tcpcfg.h | 86 + private/ntos/tdi/tcpip/tcp/tcpconn.c | 2344 +++++++++++++++ private/ntos/tdi/tcpip/tcp/tcpconn.h | 124 + private/ntos/tdi/tcpip/tcp/tcpdeb.c | 169 ++ private/ntos/tdi/tcpip/tcp/tcpdeb.h | 78 + private/ntos/tdi/tcpip/tcp/tcpdeliv.c | 1971 ++++++++++++ private/ntos/tdi/tcpip/tcp/tcpdeliv.h | 44 + private/ntos/tdi/tcpip/tcp/tcpip.def | 8 + private/ntos/tdi/tcpip/tcp/tcpip.rc | 12 + private/ntos/tdi/tcpip/tcp/tcprcv.c | 3397 +++++++++++++++++++++ private/ntos/tdi/tcpip/tcp/tcprcv.h | 74 + private/ntos/tdi/tcpip/tcp/tcpsend.c | 2666 ++++++++++++++++ private/ntos/tdi/tcpip/tcp/tcpsend.h | 105 + private/ntos/tdi/tcpip/tcp/tlcommon.c | 553 ++++ private/ntos/tdi/tcpip/tcp/tlcommon.h | 46 + private/ntos/tdi/tcpip/tcp/udp.c | 673 +++++ private/ntos/tdi/tcpip/tcp/udp.h | 39 + private/ntos/tdi/tcpip/tcp/up/makefile | 6 + private/ntos/tdi/tcpip/tcp/up/sources | 30 + private/ntos/tdi/tcpip/tcp/up/tcpip.prf | 297 ++ 85 files changed, 56050 insertions(+) create mode 100644 private/ntos/tdi/tcpip/dirs create mode 100644 private/ntos/tdi/tcpip/h/oscfg.h create mode 100644 private/ntos/tdi/tcpip/h/packoff.h create mode 100644 private/ntos/tdi/tcpip/h/packon.h create mode 100644 private/ntos/tdi/tcpip/h/queue.h create mode 100644 private/ntos/tdi/tcpip/h/tdint.h create mode 100644 private/ntos/tdi/tcpip/ip/arp.c create mode 100644 private/ntos/tdi/tcpip/ip/arp.h create mode 100644 private/ntos/tdi/tcpip/ip/arpdef.h create mode 100644 private/ntos/tdi/tcpip/ip/dirs create mode 100644 private/ntos/tdi/tcpip/ip/icmp.c create mode 100644 private/ntos/tdi/tcpip/ip/icmp.h create mode 100644 private/ntos/tdi/tcpip/ip/igmp.c create mode 100644 private/ntos/tdi/tcpip/ip/igmp.h create mode 100644 private/ntos/tdi/tcpip/ip/info.c create mode 100644 private/ntos/tdi/tcpip/ip/info.h create mode 100644 private/ntos/tdi/tcpip/ip/init.c create mode 100644 private/ntos/tdi/tcpip/ip/ipdef.h create mode 100644 private/ntos/tdi/tcpip/ip/ipinit.h create mode 100644 private/ntos/tdi/tcpip/ip/iploop.c create mode 100644 private/ntos/tdi/tcpip/ip/iprcv.c create mode 100644 private/ntos/tdi/tcpip/ip/iproute.c create mode 100644 private/ntos/tdi/tcpip/ip/iproute.h create mode 100644 private/ntos/tdi/tcpip/ip/iprtdef.h create mode 100644 private/ntos/tdi/tcpip/ip/ipstatus.c create mode 100644 private/ntos/tdi/tcpip/ip/ipxmit.c create mode 100644 private/ntos/tdi/tcpip/ip/ipxmit.h create mode 100644 private/ntos/tdi/tcpip/ip/mp/makefile create mode 100644 private/ntos/tdi/tcpip/ip/mp/sources create mode 100644 private/ntos/tdi/tcpip/ip/ntip.c create mode 100644 private/ntos/tdi/tcpip/ip/ntirp.c create mode 100644 private/ntos/tdi/tcpip/ip/ntreg.c create mode 100644 private/ntos/tdi/tcpip/ip/sources.inc create mode 100644 private/ntos/tdi/tcpip/ip/up/makefile create mode 100644 private/ntos/tdi/tcpip/ip/up/sources create mode 100644 private/ntos/tdi/tcpip/tcp/addr.c create mode 100644 private/ntos/tdi/tcpip/tcp/addr.h create mode 100644 private/ntos/tdi/tcpip/tcp/alpha/sources create mode 100644 private/ntos/tdi/tcpip/tcp/alpha/xsum.s create mode 100644 private/ntos/tdi/tcpip/tcp/dgram.c create mode 100644 private/ntos/tdi/tcpip/tcp/dgram.h create mode 100644 private/ntos/tdi/tcpip/tcp/dirs create mode 100644 private/ntos/tdi/tcpip/tcp/i386/sources create mode 100644 private/ntos/tdi/tcpip/tcp/i386/xsum.asm create mode 100644 private/ntos/tdi/tcpip/tcp/info.c create mode 100644 private/ntos/tdi/tcpip/tcp/info.h create mode 100644 private/ntos/tdi/tcpip/tcp/init.c create mode 100644 private/ntos/tdi/tcpip/tcp/mips/sources create mode 100644 private/ntos/tdi/tcpip/tcp/mips/xsum.s create mode 100644 private/ntos/tdi/tcpip/tcp/mp/makefile create mode 100644 private/ntos/tdi/tcpip/tcp/mp/sources create mode 100644 private/ntos/tdi/tcpip/tcp/mp/tcpip.prf create mode 100644 private/ntos/tdi/tcpip/tcp/ntautodl.c create mode 100644 private/ntos/tdi/tcpip/tcp/ntdisp.c create mode 100644 private/ntos/tdi/tcpip/tcp/ntinit.c create mode 100644 private/ntos/tdi/tcpip/tcp/ppc/sources create mode 100644 private/ntos/tdi/tcpip/tcp/ppc/xsum.s create mode 100644 private/ntos/tdi/tcpip/tcp/raw.c create mode 100644 private/ntos/tdi/tcpip/tcp/raw.h create mode 100644 private/ntos/tdi/tcpip/tcp/secfltr.c create mode 100644 private/ntos/tdi/tcpip/tcp/secfltr.h create mode 100644 private/ntos/tdi/tcpip/tcp/sources.inc create mode 100644 private/ntos/tdi/tcpip/tcp/tcb.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcb.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcp.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpcfg.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpconn.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcpconn.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpdeb.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcpdeb.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpdeliv.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcpdeliv.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpip.def create mode 100644 private/ntos/tdi/tcpip/tcp/tcpip.rc create mode 100644 private/ntos/tdi/tcpip/tcp/tcprcv.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcprcv.h create mode 100644 private/ntos/tdi/tcpip/tcp/tcpsend.c create mode 100644 private/ntos/tdi/tcpip/tcp/tcpsend.h create mode 100644 private/ntos/tdi/tcpip/tcp/tlcommon.c create mode 100644 private/ntos/tdi/tcpip/tcp/tlcommon.h create mode 100644 private/ntos/tdi/tcpip/tcp/udp.c create mode 100644 private/ntos/tdi/tcpip/tcp/udp.h create mode 100644 private/ntos/tdi/tcpip/tcp/up/makefile create mode 100644 private/ntos/tdi/tcpip/tcp/up/sources create mode 100644 private/ntos/tdi/tcpip/tcp/up/tcpip.prf (limited to 'private/ntos/tdi/tcpip') 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 + +#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 +#include + +#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 + +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 +#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 +#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 +#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 + +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; iirae_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 + +#ifdef NT +#include +#include +#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 + ®istrationData, + 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; iici_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; inte_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 +#include +#include +#include +#include "ipdef.h" +#include "ipinit.h" +#include +#include +#include + +// +// 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(®Key, 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 +#include +#include +#include +#include "ipdef.h" +#include "ipinit.h" +#include "icmp.h" +#include +#include +#include + + +// +// 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 +#include +#include + + +#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 <%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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "queue.h" +#include "addr.h" +#include "tcp.h" +#include "tcb.h" +#include "tcpconn.h" +#include "udp.h" +#include "tlcommon.h" +#include + +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "queue.h" +#include "addr.h" +#include "tcp.h" +#include "udp.h" +#include "raw.h" +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#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 \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 , +// 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 , +// 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; ipe_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 +#include +#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; TempTickCount > 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 + +#include + +#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 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 -- cgit v1.2.3