Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2246 lines
74 KiB

/********************************************************************/
/** 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 "precomp.h"
#include "iproute.h"
#include "ipstatus.h"
#include "igmp.h"
#include "iprtdef.h"
#include "info.h"
#include "lookup.h"
LIST_ENTRY PendingIPEventList;
uint gIPEventSequenceNo = 0;
uint DampingInterval = 20; //5*4 sec default
uint ConnectDampingInterval = 10; //5*2 sec default
PWSTR IPBindList = NULL;
extern IPSecNdisStatusRtn IPSecNdisStatusPtr;
extern ProtInfo IPProtInfo[]; // Protocol information table.
extern int NextPI; // Next PI field to be used.
extern ProtInfo *RawPI; // Raw IP protinfo
extern NetTableEntry *LoopNTE;
extern NetTableEntry **NewNetTableList; // hash table for NTEs
extern uint NET_TABLE_SIZE;
extern DisableMediaSenseEventLog;
extern Interface *DampingIFList;
extern PIRP PendingIPGetIPEventRequest;
extern DisableTaskOffload;
extern uint DisableMediaSense;
extern Interface *IFList;
extern Interface LoopInterface;
extern void DecrInitTimeInterfaces(Interface * IF);
extern void RePlumbStaticAddr(CTEEvent * AddAddrEvent, PVOID Context);
extern void RemoveStaticAddr(CTEEvent * AddAddrEvent, PVOID Context);
extern uint GetDefaultGWList(uint * numberOfGateways, IPAddr * gwList,
uint * gwMetricList, NDIS_HANDLE Handle,
PNDIS_STRING ConfigName);
extern void GetInterfaceMetric(uint * Metric, NDIS_HANDLE Handle);
extern void EnableRouter();
extern void DisableRouter();
extern uint AddIFRoutes(Interface * IF);
extern uint DelIFRoutes(Interface * IF);
extern uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE * Handle);
extern void CloseIFConfig(NDIS_HANDLE Handle);
extern void UpdateTcpParams(NDIS_HANDLE Handle, Interface *interface);
extern PDRIVER_OBJECT IPDriverObject;
void IPReset(void *Context);
void IPResetComplete(CTEEvent * Event, PVOID Context);
void LogMediaSenseEvent(CTEEvent * Event, PVOID Context);
void IPAbbreviateFriendlyName(PUNICODE_STRING DeviceName, USHORT MaxLen);
//
// local function prototypes
//
void IPNotifyClientsMediaSense(Interface * interface, IP_STATUS ipStatus);
extern void IPNotifyClientsIPEvent(Interface * interface, IP_STATUS ipStatus);
NDIS_STATUS DoPnPEvent(Interface *interface, PVOID Context);
uint GetAutoMetric(uint speed);
//* GetAutoMetric - get the corresponding metric of a speed value
//
// Called when we need to get the metric value
//
// Entry: Speed - speed of an interface
//
// Return; Metric value
//
uint GetAutoMetric(uint speed)
{
if (speed <= FOURTH_ORDER_SPEED) {
return FIFTH_ORDER_METRIC;
}
if (speed <= THIRD_ORDER_SPEED) {
return FOURTH_ORDER_METRIC;
}
if (speed <= SECOND_ORDER_SPEED) {
return THIRD_ORDER_METRIC;
}
if (speed <= FIRST_ORDER_SPEED) {
return SECOND_ORDER_METRIC;
}
return FIRST_ORDER_METRIC;
}
//** IPMapDeviceNameToIfOrder - device-name (GUID) to interface order mapping.
//
// Called to determine the interface ordering corresponding to a device-name,
// Assumes the caller is holding RouteTableLock.
//
// Entry:
// DeviceName - The device whose interface order is required.
//
// Exit:
// The order if available, MAXLONG otherwise.
uint
IPMapDeviceNameToIfOrder(PWSTR DeviceName)
{
#if !MILLEN
uint i;
PWSTR Bind;
if (IPBindList) {
for (i = 1, Bind = IPBindList; *Bind; Bind += wcslen(Bind) + 1, i++) {
Bind += sizeof(TCP_BIND_STRING_PREFIX) / sizeof(WCHAR) - 1;
if (_wcsicmp(Bind, DeviceName) == 0) {
return i;
}
}
}
#endif
return MAXLONG;
}
//* 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;
for (i = 0; i < NextPI; i++) {
if (IPProtInfo[i].pi_protocol == Protocol) {
if (IPProtInfo[i].pi_valid == PI_ENTRY_VALID) {
StatusProc = IPProtInfo[i].pi_status;
return StatusProc;
} else {
// Treat invalid entry as no maching protocol.
break;
}
}
}
if (RawPI != NULL) {
StatusProc = RawPI->pi_status;
}
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 = NULL;
if (IPProtInfo[i].pi_valid == PI_ENTRY_VALID) {
StatusProc = IPProtInfo[i].pi_status;
}
if (StatusProc != NULL)
(*StatusProc) (IP_HW_STATUS, IP_MTU_CHANGE, Dest, Src, NULL_IP_ADDR,
NewMTU, Ptr);
}
}
//* ULReConfigNotify - Notify the upper layers of an Config change.
//
// Called when we need to notify the upper layers of config changes. 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.
//
//
void
ULReConfigNotify(IP_STATUS type, ulong value)
{
ULStatusProc StatusProc;
int i;
// Now notify all UL entities about the IP re-config.
for (i = 0; i < NextPI; i++) {
StatusProc = NULL;
if (IPProtInfo[i].pi_valid == PI_ENTRY_VALID) {
StatusProc = IPProtInfo[i].pi_status;
}
if (StatusProc != NULL)
(*StatusProc) (IP_RECONFIG_STATUS, type, 0, 0, NULL_IP_ADDR,
value, NULL);
}
}
//* LogMediaSenseEvent - logs media connect/disconnect event
//
// Input: Event
// Context
//
// Returns: Nothing.
//
void
LogMediaSenseEvent(CTEEvent * Event, PVOID Context)
{
MediaSenseNotifyEvent *MediaEvent = (MediaSenseNotifyEvent *) Context;
ULONG EventCode = 0;
USHORT NumString=1;
UNREFERENCED_PARAMETER(Event);
switch (MediaEvent->Status) {
case IP_MEDIA_CONNECT:
EventCode = EVENT_TCPIP_MEDIA_CONNECT;
break;
case IP_MEDIA_DISCONNECT:
EventCode = EVENT_TCPIP_MEDIA_DISCONNECT;
break;
}
if (!MediaEvent->devname.Buffer) {
NumString = 0;
}
CTELogEvent(
IPDriverObject,
EventCode,
2,
NumString,
&MediaEvent->devname.Buffer,
0,
NULL
);
if (MediaEvent->devname.Buffer) {
CTEFreeMem(MediaEvent->devname.Buffer);
}
CTEFreeMem(MediaEvent);
}
//* 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
__stdcall
IPStatus(void *Context, uint Status, void *Buffer, uint BufferSize, void *LinkCtxt)
{
NetTableEntry *NTE = (NetTableEntry *) Context;
LLIPSpeedChange *LSC;
LLIPMTUChange *LMC;
LLIPAddrMTUChange *LAM;
uint NewMTU;
Interface *IF;
LinkEntry *Link = (LinkEntry *) LinkCtxt;
KIRQL rtlIrql;
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;
if (Link) {
ASSERT(NTE->nte_if->if_flags & IF_FLAGS_P2MP);
LMC = (LLIPMTUChange *) Buffer;
Link->link_mtu = LMC->lmc_mtu - sizeof(IPHeader);
} else {
// Walk through the NTEs on the IF, updating their MTUs.
IF = NTE->nte_if;
LMC = (LLIPMTUChange *) Buffer;
IF->if_mtu = LMC->lmc_mtu - sizeof(IPHeader);
NewMTU = IF->if_mtu;
NTE = IF->if_nte;
while (NTE != NULL) {
NTE->nte_mss = (ushort) 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;
if (!IP_ADDR_EQUAL(LAM->lam_addr,NULL_IP_ADDR)) {
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_NETMGMT, ATYPE_OVERRIDE,
GetRouteContext(LAM->lam_addr, NTE->nte_addr), 0);
}
break;
case NDIS_STATUS_MEDIA_CONNECT:{
NetTableEntry *NTE = (NetTableEntry *) Context;
Interface *IF = NTE->nte_if, *PrevIF;
BOOLEAN Notify = FALSE;
if (IF->if_resetInProgress) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ipstat: Connect while in reset progress %x\n", IF));
break;
}
if (!(IF->if_flags & IF_FLAGS_MEDIASENSE) || DisableMediaSense) {
// Just make sure that we are always in connected state
IF->if_mediastatus = 1;
break;
}
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
if (IF->if_damptimer) {
if (IF->if_mediastatus == 0) {
//cancel disconnect damping
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPStatus: Connect while Damping %x\n", IF));
IF->if_damptimer = 0;
PrevIF = STRUCT_OF(Interface, &DampingIFList, if_dampnext);
while (PrevIF->if_dampnext != IF && PrevIF->if_dampnext != NULL)
PrevIF = PrevIF->if_dampnext;
if (PrevIF->if_dampnext != NULL) {
PrevIF->if_dampnext = IF->if_dampnext;
IF->if_dampnext = NULL;
}
Notify = TRUE;
} else {
//damping for connect is already in progress
//restart the timer
IF->if_damptimer = (USHORT) (ConnectDampingInterval / 5);
if (!IF->if_damptimer)
IF->if_damptimer = 1;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPStatus: restarting connect damping %x\n", IF));
}
} else {
//need to damp this connect event
if (!(IF->if_flags & IF_FLAGS_DELETING)) {
IF->if_dampnext = DampingIFList;
DampingIFList = IF;
IF->if_damptimer = (USHORT) (ConnectDampingInterval / 5);
if (!IF->if_damptimer)
IF->if_damptimer = 1;
}
//mark the media status is disconnected
IF->if_mediastatus = 1;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ipstatus: connect on %x starting damping\n", IF));
}
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
if (Notify)
IPNotifyClientsMediaSense(IF, IP_MEDIA_CONNECT);
break;
}
case NDIS_STATUS_MEDIA_DISCONNECT:{
NetTableEntry *NTE = (NetTableEntry *) Context; // Local NTE received on
Interface *IF = NTE->nte_if, *PrevIF; // Interface corresponding to NTE.
if (IF->if_resetInProgress) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ipstat: DisConnect while in reset progress %x\n", IF));
break;
}
if (!(IF->if_flags & IF_FLAGS_MEDIASENSE) || DisableMediaSense) {
// Just make sure that we are always in connected state
IF->if_mediastatus = 1;
break;
}
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
//if damping timer is not running
//insert this IF in damping list and
// start the timer
if (IF->if_mediastatus) {
if (!IF->if_damptimer) {
if (!(IF->if_flags & IF_FLAGS_DELETING)) {
IF->if_dampnext = DampingIFList;
DampingIFList = IF;
IF->if_damptimer = (USHORT) (DampingInterval / 5);
if (!IF->if_damptimer)
IF->if_damptimer = 1;
}
//mark the media status is disconnected
IF->if_mediastatus = 0;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ipstatus: disconnect on %x starting damping\n", IF));
} else {
//this may be disconnect when connect damp is going on
//just mark this as disconnect and increase timeout.
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ipstatus: disconnect on while on connect damping %x\n", IF));
IF->if_damptimer = 0;
PrevIF = STRUCT_OF(Interface, &DampingIFList, if_dampnext);
while (PrevIF->if_dampnext != IF && PrevIF->if_dampnext != NULL)
PrevIF = PrevIF->if_dampnext;
if (PrevIF->if_dampnext != NULL) {
PrevIF->if_dampnext = IF->if_dampnext;
IF->if_dampnext = NULL;
}
}
}
//
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
//IPNotifyClientsMediaSense( IF, IP_MEDIA_DISCONNECT );
break;
}
case NDIS_STATUS_RESET_START:{
NetTableEntry *NTE = (NetTableEntry *) Context; // Local NTE received on
Interface *IF = NTE->nte_if; // Interface corresponding to NTE.
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ipstatus: Resetstart %x\n", IF));
if (IF) {
IF->if_resetInProgress = TRUE;
// inform IPSec that this interface is going away
if (IPSecNdisStatusPtr) {
(*IPSecNdisStatusPtr)(IF, NDIS_STATUS_RESET_START);
}
}
break;
}
case NDIS_STATUS_RESET_END:{
NetTableEntry *NTE = (NetTableEntry *) Context; // Local NTE received on
Interface *IF = NTE->nte_if; // Interface corresponding to NTE.
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ipstatus: Resetend %x\n", IF));
if (IF) {
IF->if_resetInProgress = FALSE;
// inform IPSec that this interface is coming back
if (IPSecNdisStatusPtr) {
(*IPSecNdisStatusPtr)(IF, NDIS_STATUS_RESET_END);
}
}
break;
}
default:
break;
}
}
void
IPReset(void *Context)
{
NetTableEntry *NTE = (NetTableEntry *) Context;
Interface *IF = NTE->nte_if;
IPResetEvent *ResetEvent;
if (IF->if_dondisreq) {
KIRQL rtlIrql;
ResetEvent = CTEAllocMemNBoot(sizeof(IPResetEvent), 'ViCT');
if (ResetEvent) {
CTEInitEvent(&ResetEvent->Event, IPResetComplete);
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
LOCKED_REFERENCE_IF(IF);
ResetEvent->IF = IF;
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
CTEScheduleDelayedEvent(&ResetEvent->Event, ResetEvent);
}
}
}
void
IPResetComplete(CTEEvent * Event, PVOID Context)
{
IPResetEvent *ResetEvent = (IPResetEvent *) Context;
Interface *IF = ResetEvent->IF;
uint MediaStatus;
NTSTATUS Status;
UNREFERENCED_PARAMETER(Event);
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPstat:resetcmplt: querying for Media connect status %x\n", IF));
if ((IF->if_flags & IF_FLAGS_MEDIASENSE) && !DisableMediaSense) {
Status = (*IF->if_dondisreq) (IF->if_lcontext,
NdisRequestQueryInformation,
OID_GEN_MEDIA_CONNECT_STATUS,
&MediaStatus,
sizeof(MediaStatus),
NULL,
TRUE);
if (Status == NDIS_STATUS_SUCCESS) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPStat: resetend: Media status %x %x\n", IF, Status));
if (MediaStatus == NdisMediaStateDisconnected && IF->if_mediastatus) {
IF->if_mediastatus = 0;
IPNotifyClientsMediaSense(IF, IP_MEDIA_DISCONNECT);
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ipstatus:resetcmplt: notifying disconnect\n"));
} else if (MediaStatus == NdisMediaStateConnected && !IF->if_mediastatus) {
IPNotifyClientsMediaSense(IF, IP_MEDIA_CONNECT);
IF->if_mediastatus = 1;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ipstatus:resetcmplt: notifying connect\n"));
}
}
}
DerefIF(IF);
CTEFreeMem(ResetEvent);
}
void
DelayedDecrInitTimeInterfaces (
IN CTEEvent * Event,
IN PVOID Context
)
/*++
Routine Description:
DelayedDecrInitTimeInterfaces could end up calling TDI's ProviderReady
function (which must be called at < DISPATCH_LEVEL) thus it is
necessary to have this routine.
Arguments:
Event - Previously allocated CTEEvent structure for this event
Context - Any parameters for this function is passed in here
Return Value:
None
--*/
{
Interface * IF;
KIRQL rtlIrql;
IF = (Interface *) Context;
DecrInitTimeInterfaces(IF);
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
LockedDerefIF(IF);
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
CTEFreeMem(Event);
}
void
DampCheck()
{
Interface *tmpIF, *PrevIF, *NotifyList = NULL;
IP_STATUS ipstat = IP_MEDIA_DISCONNECT;
KIRQL rtlIrql;
CTEEvent * Event;
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
for (tmpIF = IFList; tmpIF; ) {
if ((tmpIF->if_flags & IF_FLAGS_DELETING) ||
tmpIF->if_wlantimer == 0 ||
--tmpIF->if_wlantimer != 0) {
tmpIF = tmpIF->if_next;
} else {
LOCKED_REFERENCE_IF(tmpIF);
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
if (rtlIrql < DISPATCH_LEVEL) {
DecrInitTimeInterfaces(tmpIF);
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
PrevIF = tmpIF;
tmpIF = tmpIF->if_next;
LockedDerefIF(PrevIF);
continue;
}
//
// Queue work item for DecrInitTimeInterfaces
// because this function might be called
// at dispatch level.
//
Event = CTEAllocMemN(sizeof(CTEEvent), 'ViCT');
if (Event == NULL) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_WARNING_LEVEL,"ipstatus: DampCheck - can not allocate Event for CTEInitEvent\n"));
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
tmpIF->if_wlantimer++;
PrevIF = tmpIF;
tmpIF = tmpIF->if_next;
LockedDerefIF(PrevIF);
continue;
}
CTEInitEvent(Event, DelayedDecrInitTimeInterfaces);
CTEScheduleDelayedEvent(Event, tmpIF);
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
tmpIF = tmpIF->if_next;
}
}
tmpIF = DampingIFList;
PrevIF = STRUCT_OF(Interface, &DampingIFList, if_dampnext);
while (tmpIF) {
if (tmpIF->if_damptimer && (--tmpIF->if_damptimer <= 0)) {
tmpIF->if_damptimer = 0;
//ref this if so that it will not be deleted
//until we complete notifying dhcp
LOCKED_REFERENCE_IF(tmpIF);
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Dampcheck fired %x \n", tmpIF));
PrevIF->if_dampnext = tmpIF->if_dampnext;
tmpIF->if_dampnext = NotifyList;
NotifyList = tmpIF;
tmpIF = PrevIF->if_dampnext;
} else {
PrevIF = tmpIF;
tmpIF = tmpIF->if_dampnext;
}
}
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
//now process the notify queue
tmpIF = NotifyList;
ipstat = IP_MEDIA_DISCONNECT;
while (tmpIF) {
if (tmpIF->if_mediastatus) {
ipstat = IP_MEDIA_CONNECT;
tmpIF->if_mediastatus = 0;
}
//flush arp table entries on this interface
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"dampcheck:flushing ates on if %x\n", tmpIF));
if (tmpIF->if_arpflushallate)
(*(tmpIF->if_arpflushallate)) (tmpIF->if_lcontext);
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Dampcheck notifying %x %x\n", tmpIF, ipstat));
IPNotifyClientsMediaSense(tmpIF, ipstat);
PrevIF = tmpIF;
tmpIF = tmpIF->if_dampnext;
DerefIF(PrevIF);
}
}
//** IPNotifyClientsMediaSense - handles media-sense notification.
//
// Called to notify upper-layer clients of media-sense after damping has been
// done to filter out spurious events. Do nothing if media-sense-handling is
// disabled and, otherwise, notify the DHCP client service of the event, and
// optionally schedule a work-item to log an event.
//
// Entry:
// IF - the interface on which the media-sense event occurred.
// ipStatus - the event that occurred (connect or disconnect)
//
// Returns:
// Nothing.
//
void
IPNotifyClientsMediaSense(Interface *IF, IP_STATUS ipStatus)
{
MediaSenseNotifyEvent *MediaEvent;
USHORT MaxLen = CTE_MAX_EVENT_LOG_DATA_SIZE - 2;
if (!(IF->if_flags & IF_FLAGS_MEDIASENSE) || DisableMediaSense) {
// Just make sure that media status is always 1.
IF->if_mediastatus = 1;
return;
}
// Notify DHCP about this event, so that it can reacquire/release
// the IP address
IPNotifyClientsIPEvent(IF, ipStatus);
if (!DisableMediaSenseEventLog) {
// Log an event for the administrator's benefit.
// We attempt to log the event with a friendly-name;
// if none is available, we fall back on the device GUID.
MediaEvent = CTEAllocMemNBoot(sizeof(MediaSenseNotifyEvent), 'ViCT');
if (MediaEvent) {
MediaEvent->Status = ipStatus;
MediaEvent->devname.Buffer =
CTEAllocMemBoot((MAX_IFDESCR_LEN + 1) * sizeof(WCHAR));
if (MediaEvent->devname.Buffer) {
TDI_STATUS Status;
Status = IPGetInterfaceFriendlyName(IF->if_index,
MediaEvent->devname.Buffer,
MAX_IFDESCR_LEN);
if (Status != TDI_SUCCESS) {
RtlCopyMemory(MediaEvent->devname.Buffer,
IF->if_devname.Buffer, IF->if_devname.Length);
MediaEvent->devname.Buffer[
IF->if_devname.Length / sizeof(WCHAR)] = UNICODE_NULL;
}
MediaEvent->devname.Length = (USHORT) (wcslen(MediaEvent->devname.Buffer)*
sizeof(WCHAR));
// truncate NIC name if it is too long
if (MediaEvent->devname.Length > MaxLen) {
IPAbbreviateFriendlyName(&MediaEvent->devname, MaxLen);
}
}
CTEInitEvent(&MediaEvent->Event, LogMediaSenseEvent);
CTEScheduleDelayedEvent(&MediaEvent->Event, MediaEvent);
}
}
}
NTSTATUS
IPGetIPEventEx(
PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Processes an IPGetIPEvent request.
Arguments:
Irp - pointer to the client 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 status;
KIRQL cancelIrql, rtlIrql;
PendingIPEvent *event;
PLIST_ENTRY entry;
PIP_GET_IP_EVENT_RESPONSE responseBuf;
PIP_GET_IP_EVENT_REQUEST requestBuf;
//
// We need to grab CancelSpinLock before the RouteTableLock
// to preserve the lock order as in the cancel routine.
//
IoAcquireCancelSpinLock(&cancelIrql);
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
//
// We need to recheck that PendingIPGetIPEventRequest is
// same as Irp. What can happen is that after we set the
// cancel routine, this irp can get cancelled anytime. Here
// we check for that case to make sure that we don't complete
// a cancelled irp.
//
if (PendingIPGetIPEventRequest == Irp) {
responseBuf = Irp->AssociatedIrp.SystemBuffer;
requestBuf = Irp->AssociatedIrp.SystemBuffer;
//TCPTRACE(("IP: Received irp %lx for ip event, last seqNo %lx\n",Irp, requestBuf->SequenceNo));
//
// Find an event that is greater than the last one reported.
// i.e one with higher sequence #
//
for (entry = PendingIPEventList.Flink;
entry != &PendingIPEventList;
) {
event = CONTAINING_RECORD(entry, PendingIPEvent, Linkage);
entry = entry->Flink;
if (event->evBuf.SequenceNo > requestBuf->SequenceNo) {
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >=
(sizeof(IP_GET_IP_EVENT_RESPONSE) + event->evBuf.AdapterName.MaximumLength)) {
// reset pending irp to NULL.
PendingIPGetIPEventRequest = NULL;
IoSetCancelRoutine(Irp, NULL);
*responseBuf = event->evBuf;
// set up the buffer to store the unicode adapter name. note that this buffer will have to
// be remapped in the user space.
responseBuf->AdapterName.Buffer = (PVOID) ((uchar *) responseBuf + sizeof(IP_GET_IP_EVENT_RESPONSE));
responseBuf->AdapterName.Length = event->evBuf.AdapterName.Length;
responseBuf->AdapterName.MaximumLength = event->evBuf.AdapterName.MaximumLength;
RtlCopyMemory(responseBuf->AdapterName.Buffer,
event->evBuf.AdapterName.Buffer,
event->evBuf.AdapterName.Length);
Irp->IoStatus.Information = sizeof(IP_GET_IP_EVENT_RESPONSE) + event->evBuf.AdapterName.MaximumLength;
// once the disconnect/unbind event has been indicated
// it should be removed from the queue because the client does not
// have to be reindicated with disconnect/unbind even if the client was restarted.
if (IP_MEDIA_DISCONNECT == event->evBuf.MediaStatus ||
IP_UNBIND_ADAPTER == event->evBuf.MediaStatus) {
//TCPTRACE(("IP: Removing completed %x event\n",event->evBuf.MediaStatus));
RemoveEntryList(&event->Linkage);
CTEFreeMem(event);
}
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
IoReleaseCancelSpinLock(cancelIrql);
return STATUS_SUCCESS;
} else {
status = STATUS_INVALID_PARAMETER;
}
break;
}
}
// any entry of higher sequence # found?
if (entry == &PendingIPEventList) {
//
// Since there is no new event pending, we cannot complete
// the irp.
//
//TCPTRACE(("IP: get ip event irp %lx will pend\n",Irp));
status = STATUS_PENDING;
} else {
status = STATUS_INVALID_PARAMETER;
}
} else {
status = STATUS_CANCELLED;
}
if ((status == STATUS_INVALID_PARAMETER)) {
//makesure that we nulke this before releasing cancel spinlock
ASSERT(PendingIPGetIPEventRequest == Irp);
PendingIPGetIPEventRequest = NULL;
}
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
IoReleaseCancelSpinLock(cancelIrql);
return status;
} // IPGetMediaSenseEx
NTSTATUS
IPEnableMediaSense(BOOLEAN Enable, KIRQL *rtlIrql)
{
Interface *tmpIF, *IF;
NTSTATUS Status;
uint MediaStatus;
if (Enable) {
if ((DisableMediaSense > 0) && (--DisableMediaSense == 0)) {
// Remove if in damping list
while ( DampingIFList ) {
DampingIFList->if_damptimer = 0;
DampingIFList = DampingIFList->if_dampnext;
}
// for each interface, query media status
// and if disabled, notify clients
tmpIF = IFList;
while (tmpIF) {
if (!(tmpIF->if_flags & IF_FLAGS_DELETING) &&
!(tmpIF->if_flags & IF_FLAGS_NOIPADDR) &&
(tmpIF->if_flags & IF_FLAGS_MEDIASENSE) &&
(tmpIF->if_dondisreq) &&
(tmpIF != &LoopInterface)) {
// query ndis
LOCKED_REFERENCE_IF(tmpIF);
CTEFreeLock(&RouteTableLock.Lock, *rtlIrql);
Status =
(*tmpIF->if_dondisreq)(tmpIF->if_lcontext,
NdisRequestQueryInformation,
OID_GEN_MEDIA_CONNECT_STATUS,
&MediaStatus,
sizeof(MediaStatus),
NULL,
TRUE);
if (Status == NDIS_STATUS_SUCCESS) {
if (MediaStatus == NdisMediaStateDisconnected &&
tmpIF->if_mediastatus) {
tmpIF->if_mediastatus = 0;
IPNotifyClientsIPEvent(tmpIF, IP_MEDIA_DISCONNECT);
} else if (MediaStatus == NdisMediaStateConnected &&
!tmpIF->if_mediastatus) {
IPNotifyClientsIPEvent(tmpIF, IP_MEDIA_CONNECT);
tmpIF->if_mediastatus = 1;
}
}
CTEGetLock(&RouteTableLock.Lock, rtlIrql);
IF = tmpIF->if_next;
LockedDerefIF(tmpIF);
} else {
IF = tmpIF->if_next;
}
tmpIF = IF;
}
}
Status = STATUS_SUCCESS;
} else {
if (DisableMediaSense++ == 0) {
// remove if in damping list
while (DampingIFList) {
DampingIFList->if_damptimer = 0;
DampingIFList = DampingIFList->if_dampnext;
}
// if there is a disconnected media, fake a connect request
tmpIF = IFList;
while (tmpIF) {
if (!(tmpIF->if_flags & IF_FLAGS_DELETING) &&
!(tmpIF->if_flags & IF_FLAGS_NOIPADDR) &&
(tmpIF->if_flags & IF_FLAGS_MEDIASENSE) &&
(tmpIF->if_dondisreq) &&
(tmpIF->if_mediastatus == 0) &&
(tmpIF != &LoopInterface)) {
LOCKED_REFERENCE_IF(tmpIF);
CTEFreeLock(&RouteTableLock.Lock, *rtlIrql);
IPNotifyClientsIPEvent(tmpIF, IP_MEDIA_CONNECT);
tmpIF->if_mediastatus = 1;
CTEGetLock(&RouteTableLock.Lock, rtlIrql);
IF = tmpIF->if_next;
LockedDerefIF(tmpIF);
} else {
IF = tmpIF->if_next;
}
tmpIF = IF;
}
}
Status = STATUS_PENDING;
}
return Status;
}
void
IPNotifyClientsIPEvent(
Interface * interface,
IP_STATUS ipStatus
)
/*++
Routine Description:
Notifies the clients about media sense event.
Arguments:
interface - IP interface on which this event arrived.
ipStatus - the status of the event
Return Value:
none.
--*/
{
PIRP pendingIrp;
KIRQL rtlIrql;
NDIS_STRING adapterName;
uint seqNo;
PendingIPEvent *event;
PLIST_ENTRY p;
BOOLEAN EventIndicated;
AddStaticAddrEvent *AddrEvent;
KIRQL oldIrql;
EventIndicated = FALSE;
if (interface->if_flags & IF_FLAGS_MEDIASENSE) {
if (ipStatus == IP_MEDIA_CONNECT) {
if (interface->if_mediastatus == 0) {
//
// First mark the interface UP
//
interface->if_mediastatus = 1;
} else {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Connect media event when already connected!\n"));
// return;
}
//schedule an event to replumb static addr
AddrEvent = CTEAllocMemNBoot(sizeof(AddStaticAddrEvent), 'ViCT');
if (AddrEvent) {
AddrEvent->ConfigName = interface->if_configname;
// If we fail to alloc Configname buffer, do not schedule
// ReplumbStaticAddr, as OpenIFConfig anyway will fail.
AddrEvent->ConfigName.Buffer =
CTEAllocMemBoot(interface->if_configname.MaximumLength);
if (AddrEvent->ConfigName.Buffer) {
NdisZeroMemory(AddrEvent->ConfigName.Buffer, interface->if_configname.MaximumLength);
RtlCopyMemory(AddrEvent->ConfigName.Buffer, interface->if_configname.Buffer, interface->if_configname.Length);
AddrEvent->IF = interface;
// Reference this interface so that it will not
// go away until RePlumbStaticAddr is scheduled.
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
LOCKED_REFERENCE_IF(interface);
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
CTEInitEvent(&AddrEvent->Event, RePlumbStaticAddr);
CTEScheduleDelayedEvent(&AddrEvent->Event, AddrEvent);
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"media connect: scheduled replumbstaticaddr %xd!\n", interface));
} else {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Failed to allocate config name buffer for RePlumbStaticAddr!\n"));
}
} else {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Failed to allocate event for RePlumbStaticAddr!\n"));
return;
}
} else if (ipStatus == IP_MEDIA_DISCONNECT) {
//
// Mark the interface DOWN
//
interface->if_mediastatus = 0;
AddrEvent = CTEAllocMemNBoot(sizeof(AddStaticAddrEvent), 'ViCT');
if (AddrEvent) {
AddrEvent->ConfigName = interface->if_configname;
AddrEvent->ConfigName.Buffer =
CTEAllocMemBoot(interface->if_configname.MaximumLength);
if (AddrEvent) {
NdisZeroMemory(AddrEvent->ConfigName.Buffer, interface->if_configname.MaximumLength);
RtlCopyMemory(AddrEvent->ConfigName.Buffer, interface->if_configname.Buffer, interface->if_configname.Length);
}
AddrEvent->IF = interface;
// Reference this interface so that it will not
// go away until RemoveStaticAddr is scheduled.
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
LOCKED_REFERENCE_IF(interface);
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
CTEInitEvent(&AddrEvent->Event, RemoveStaticAddr);
CTEScheduleDelayedEvent(&AddrEvent->Event, AddrEvent);
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"media disconnect: scheduled removestaticaddr %xd!\n", interface));
} else {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Failed to allocate event for RemoveStaticAddr!\n"));
return;
}
}
}
//
// strip off \Device\ from the interface name to get the adapter name.
// This is what we pass to our clients.
//
#if MILLEN
adapterName.Length = interface->if_devname.Length;
adapterName.MaximumLength = interface->if_devname.MaximumLength;
adapterName.Buffer = interface->if_devname.Buffer;
#else // MILLEN
adapterName.Length = interface->if_devname.Length -
(USHORT) (wcslen(TCP_EXPORT_STRING_PREFIX) * sizeof(WCHAR));
adapterName.MaximumLength = interface->if_devname.MaximumLength -
(USHORT) (wcslen(TCP_EXPORT_STRING_PREFIX) * sizeof(WCHAR));
adapterName.Buffer = interface->if_devname.Buffer + wcslen(TCP_EXPORT_STRING_PREFIX);
#endif // !MILLEN
seqNo = InterlockedIncrement( (PLONG) &gIPEventSequenceNo);
// TCPTRACE(("IP: Received ip event %lx for interface %lx context %lx, seq %lx\n",
// ipStatus, interface, interface->if_nte->nte_context,seqNo));
IoAcquireCancelSpinLock(&oldIrql);
if (PendingIPGetIPEventRequest) {
PIP_GET_IP_EVENT_RESPONSE responseBuf;
IN PIO_STACK_LOCATION IrpSp;
pendingIrp = PendingIPGetIPEventRequest;
PendingIPGetIPEventRequest = NULL;
IoSetCancelRoutine(pendingIrp, NULL);
IoReleaseCancelSpinLock(oldIrql);
IrpSp = IoGetCurrentIrpStackLocation(pendingIrp);
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >=
(sizeof(IP_GET_IP_EVENT_RESPONSE) + adapterName.MaximumLength)) {
responseBuf = pendingIrp->AssociatedIrp.SystemBuffer;
responseBuf->ContextStart = interface->if_nte->nte_context;
responseBuf->ContextEnd = (USHORT) (responseBuf->ContextStart +
interface->if_ntecount);
responseBuf->MediaStatus = ipStatus;
responseBuf->SequenceNo = seqNo;
// set up the buffer to store the unicode adapter name. note that this buffer will have to
// be remapped in the user space.
responseBuf->AdapterName.Buffer = (PVOID) ((uchar *) responseBuf + sizeof(IP_GET_IP_EVENT_RESPONSE));
responseBuf->AdapterName.Length = adapterName.Length;
responseBuf->AdapterName.MaximumLength = adapterName.MaximumLength;
RtlCopyMemory(responseBuf->AdapterName.Buffer,
adapterName.Buffer,
adapterName.Length);
pendingIrp->IoStatus.Information = sizeof(IP_GET_IP_EVENT_RESPONSE) + adapterName.MaximumLength;
pendingIrp->IoStatus.Status = STATUS_SUCCESS;
EventIndicated = TRUE;
} else {
pendingIrp->IoStatus.Information = 0;
pendingIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;
}
IoCompleteRequest(pendingIrp, IO_NETWORK_INCREMENT);
} else {
IoReleaseCancelSpinLock(oldIrql);
}
//
// Make sure there aren't any outdated events which we dont
// need to keep in the queue any longer.
// if this is a DISCONNECT request or UNBIND request:
// remove all the previous events since they are of
// no meaning once we get a new disconnect/unbind request.
// if this is a CONNECT request.
// remove previous duplicate CONNECT requests if any.
// if this is a BIND request:
// there cant be anything other than UNBIND request in the queue.
//
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
for (p = PendingIPEventList.Flink;
p != &PendingIPEventList;) {
BOOLEAN removeOldEvent = FALSE;
PUNICODE_STRING evAdapterName;
event = CONTAINING_RECORD(p, PendingIPEvent, Linkage);
p = p->Flink;
evAdapterName = &event->evBuf.AdapterName;
if ((evAdapterName->Length == adapterName.Length) &&
RtlEqualMemory(evAdapterName->Buffer,
adapterName.Buffer,
evAdapterName->Length)) {
switch (ipStatus) {
case IP_MEDIA_DISCONNECT:
case IP_UNBIND_ADAPTER:
removeOldEvent = TRUE;
break;
case IP_MEDIA_CONNECT:
if (event->evBuf.MediaStatus == IP_MEDIA_CONNECT) {
removeOldEvent = TRUE;
}
break;
case IP_BIND_ADAPTER:
break;
default:
break;
}
if (removeOldEvent == TRUE) {
//TCPTRACE(("IP: Removing old ip event %lx, status %lx, seqNo %lx\n",
// event,event->evBuf.MediaStatus,event->evBuf.SequenceNo));
RemoveEntryList(&event->Linkage);
CTEFreeMem(event);
}
}
}
// At the same time, once the disconnect/unbind event has been indicated
// it should be removed from the queue because the client does not
// have to be reindicated with disconnect/unbind even if the client was restarted.
if (EventIndicated &&
(IP_MEDIA_DISCONNECT == ipStatus || IP_UNBIND_ADAPTER == ipStatus)) {
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
return;
}
//
// Allocate an event.
//
event = CTEAllocMem(sizeof(PendingIPEvent) + adapterName.MaximumLength);
if (NULL == event) {
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
return;
}
event->evBuf.ContextStart = interface->if_nte->nte_context;
event->evBuf.ContextEnd = (USHORT) (event->evBuf.ContextStart +
interface->if_ntecount - 1);
event->evBuf.MediaStatus = ipStatus;
event->evBuf.SequenceNo = seqNo;
// set up the buffer to store the unicode adapter name. note that this buffer will have to
// be remapped in the user space.
event->evBuf.AdapterName.Buffer = (PVOID) ((uchar *) event + sizeof(PendingIPEvent));
event->evBuf.AdapterName.Length = adapterName.Length;
event->evBuf.AdapterName.MaximumLength = adapterName.MaximumLength;
RtlCopyMemory(event->evBuf.AdapterName.Buffer,
adapterName.Buffer,
adapterName.Length);
//
// There is no client request pending, so we queue this event on the
// pending event list. When the client comes back with an irp we will
// complete the irp with the event.
//
//TCPTRACE(("Queuing ip event %lx for adapter %lx seq %lx\n", ipStatus,interface,seqNo));
InsertTailList(&PendingIPEventList, &event->Linkage);
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
} // IPNotifyClientsIPEvent
NTSTATUS
NotifyPnPInternalClients(Interface * interface, PNET_PNP_EVENT netPnPEvent)
{
NTSTATUS Status, retStatus;
int i;
NetTableEntry *NTE;
NDIS_HANDLE handle = NULL;
retStatus = Status = STATUS_SUCCESS;
if (interface && !OpenIFConfig(&interface->if_configname, &handle)) {
return NDIS_STATUS_FAILURE;
}
for (i = 0; (i < NextPI) && (STATUS_SUCCESS == Status); i++) {
if (IPProtInfo[i].pi_pnppower &&
(IPProtInfo[i].pi_valid == PI_ENTRY_VALID)) {
if (interface) {
NTE = interface->if_nte;
while (NTE != NULL) {
if (NTE->nte_flags & NTE_VALID) {
Status = (*IPProtInfo[i].pi_pnppower) (interface, NTE->nte_addr, handle, netPnPEvent);
if (STATUS_SUCCESS != Status) {
retStatus = Status;
}
}
NTE = NTE->nte_ifnext;
}
} else {
Status = (*IPProtInfo[i].pi_pnppower) (NULL, 0, NULL, netPnPEvent);
if (STATUS_SUCCESS != Status) {
retStatus = Status;
}
}
}
}
if (handle) {
CloseIFConfig(handle);
}
return retStatus;
}
NTSTATUS
IPPnPReconfigure(Interface * interface, PNET_PNP_EVENT netPnPEvent)
{
NetTableEntry *NTE;
uint i;
NDIS_HANDLE handle = NULL;
PIP_PNP_RECONFIG_REQUEST reconfigBuffer = (PIP_PNP_RECONFIG_REQUEST) netPnPEvent->Buffer;
CTELockHandle Handle;
uint NextEntryOffset;
PIP_PNP_RECONFIG_HEADER Header;
BOOLEAN InitComplete = FALSE;
if (!reconfigBuffer)
return STATUS_SUCCESS;
if (IP_PNP_RECONFIG_VERSION != reconfigBuffer->version) {
return NDIS_STATUS_BAD_VERSION;
} else if (netPnPEvent->BufferLength < sizeof(*reconfigBuffer)) {
return NDIS_STATUS_INVALID_LENGTH;
} else {
NextEntryOffset = reconfigBuffer->NextEntryOffset;
if (NextEntryOffset) {
// validate the chain of reconfig entries
for (;;) {
if ((NextEntryOffset + sizeof(IP_PNP_RECONFIG_HEADER)) >
netPnPEvent->BufferLength) {
return NDIS_STATUS_INVALID_LENGTH;
} else {
Header =
(PIP_PNP_RECONFIG_HEADER)
((PUCHAR) reconfigBuffer + NextEntryOffset);
if (Header->EntryType == IPPnPInitCompleteEntryType) {
InitComplete = TRUE;
}
if (!Header->NextEntryOffset) {
break;
} else {
NextEntryOffset += Header->NextEntryOffset;
}
}
}
}
}
if (interface && InitComplete) {
DecrInitTimeInterfaces(interface);
}
if (interface && !OpenIFConfig(&interface->if_configname, &handle)) {
return NDIS_STATUS_FAILURE;
}
// if there is gateway list update, delete the old gateways
// and add the new ones.
if ((reconfigBuffer->Flags & IP_PNP_FLAG_GATEWAY_LIST_UPDATE) &&
interface && reconfigBuffer->gatewayListUpdate) {
for (i = 0; i < interface->if_numgws; i++) {
NTE = interface->if_nte;
while (NTE != NULL) {
if (NTE->nte_flags & NTE_VALID) {
DeleteRoute(NULL_IP_ADDR,
DEFAULT_MASK,
IPADDR_LOCAL,
interface,
0);
DeleteRoute(NULL_IP_ADDR,
DEFAULT_MASK,
net_long(interface->if_gw[i]),
interface,
0);
}
NTE = NTE->nte_ifnext;
}
}
RtlZeroMemory(interface->if_gw, interface->if_numgws);
if (!GetDefaultGWList(&interface->if_numgws,
interface->if_gw,
interface->if_gwmetric,
handle,
&interface->if_configname)) {
CloseIFConfig(handle);
return NDIS_STATUS_FAILURE;
}
for (i = 0; i < interface->if_numgws; i++) {
NTE = interface->if_nte;
while (NTE != NULL) {
if (NTE->nte_flags & NTE_VALID) {
IPAddr GWAddr = net_long(interface->if_gw[i]);
if (IP_ADDR_EQUAL(GWAddr, NTE->nte_addr)) {
GWAddr = IPADDR_LOCAL;
}
AddRoute(NULL_IP_ADDR, DEFAULT_MASK,
GWAddr, interface,
NTE->nte_mss,
interface->if_gwmetric[i]
? interface->if_gwmetric[i] : interface->if_metric,
IRE_PROTO_NETMGMT, ATYPE_OVERRIDE, 0, 0);
}
NTE = NTE->nte_ifnext;
}
}
}
// Update the interface metric if necessary.
if ((reconfigBuffer->Flags & IP_PNP_FLAG_INTERFACE_METRIC_UPDATE) &&
interface && reconfigBuffer->InterfaceMetricUpdate) {
uint Metric, NewMetric;
GetInterfaceMetric(&Metric, handle);
if (!Metric && !interface->if_auto_metric) {
//from non auto mode change to auto mode
interface->if_auto_metric = 1;
NewMetric = 0;
} else {
if (Metric && interface->if_auto_metric) {
//from auto mode change to non auto mode
interface->if_auto_metric = 0;
NewMetric = Metric;
} else {
NewMetric = Metric;
}
}
if (!NewMetric) {
//set the metric according to the speed
NewMetric = GetAutoMetric(interface->if_speed);
}
if (NewMetric != interface->if_metric) {
interface->if_metric = NewMetric;
AddIFRoutes(interface);
// Also need to change default route metric when metric of static DG is auto
for (i = 0; i < interface->if_numgws; i++) {
if (interface->if_gwmetric[i] != 0) {
continue;
}
NTE = interface->if_nte;
while (NTE != NULL) {
if (NTE->nte_flags & NTE_VALID) {
IPAddr GWAddr = net_long(interface->if_gw[i]);
if (IP_ADDR_EQUAL(GWAddr, NTE->nte_addr)) {
GWAddr = IPADDR_LOCAL;
}
AddRoute(NULL_IP_ADDR, DEFAULT_MASK,
GWAddr, interface,
NTE->nte_mss,
interface->if_metric,
IRE_PROTO_NETMGMT, ATYPE_OVERRIDE, 0, 0);
}
NTE = NTE->nte_ifnext;
}
}
IPNotifyClientsIPEvent(interface, IP_INTERFACE_METRIC_CHANGE);
}
}
// Check for per-interface tcp parameters updation
if ((reconfigBuffer->Flags & IP_PNP_FLAG_INTERFACE_TCP_PARAMETER_UPDATE) &&
interface) {
UpdateTcpParams(handle, interface);
}
if (interface) {
CloseIFConfig(handle);
}
// Enable or disable forwarding if necessary.
CTEGetLock(&RouteTableLock.Lock, &Handle);
if (reconfigBuffer->Flags & IP_PNP_FLAG_IP_ENABLE_ROUTER) {
if (reconfigBuffer->IPEnableRouter) {
// configure ourself a router..
if (!RouterConfigured) {
EnableRouter();
}
} else {
// if we were config as router, disable it.
if (RouterConfigured) {
DisableRouter();
}
}
}
// Handle a change to the router-discovery setting on the interface.
// The static setting is in 'PerformRouterDiscovery' (see IP_IRDP_*),
// and the DHCP setting is the BOOLEAN 'DhcpPerformRouterDiscovery'.
if (interface &&
(((reconfigBuffer->Flags & IP_PNP_FLAG_PERFORM_ROUTER_DISCOVERY) &&
reconfigBuffer->PerformRouterDiscovery !=
interface->if_rtrdiscovery) ||
((reconfigBuffer->Flags & IP_PNP_FLAG_DHCP_PERFORM_ROUTER_DISCOVERY) &&
!!reconfigBuffer->DhcpPerformRouterDiscovery !=
!!interface->if_dhcprtrdiscovery))) {
if (reconfigBuffer->Flags & IP_PNP_FLAG_PERFORM_ROUTER_DISCOVERY) {
interface->if_rtrdiscovery =
reconfigBuffer->PerformRouterDiscovery;
}
if (reconfigBuffer->Flags & IP_PNP_FLAG_DHCP_PERFORM_ROUTER_DISCOVERY) {
interface->if_dhcprtrdiscovery =
(USHORT) (!!reconfigBuffer->DhcpPerformRouterDiscovery);
}
// Propagate the interface's router-discovery setting to its NTEs.
// Note that the 'if_dhcprtrdiscovery' setting takes effect only
// if the interface's setting is 'IP_IRDP_DISABLED_USE_DHCP'.
NTE = interface->if_nte;
while ((NTE != NULL) && (NTE->nte_flags & NTE_VALID)) {
if (interface->if_rtrdiscovery == IP_IRDP_ENABLED) {
NTE->nte_rtrdiscovery = IP_IRDP_ENABLED;
NTE->nte_rtrdisccount = MAX_SOLICITATION_DELAY;
NTE->nte_rtrdiscstate = NTE_RTRDISC_DELAYING;
} else if (interface->if_rtrdiscovery == IP_IRDP_DISABLED) {
NTE->nte_rtrdiscovery = IP_IRDP_DISABLED;
} else if (interface->if_rtrdiscovery ==
IP_IRDP_DISABLED_USE_DHCP &&
interface->if_dhcprtrdiscovery) {
NTE->nte_rtrdiscovery = IP_IRDP_ENABLED;
NTE->nte_rtrdisccount = MAX_SOLICITATION_DELAY;
NTE->nte_rtrdiscstate = NTE_RTRDISC_DELAYING;
} else {
NTE->nte_rtrdiscovery = IP_IRDP_DISABLED;
}
NTE = NTE->nte_ifnext;
}
}
CTEFreeLock(&RouteTableLock.Lock, Handle);
if (reconfigBuffer->Flags & IP_PNP_FLAG_ENABLE_SECURITY_FILTER) {
ULReConfigNotify(IP_RECONFIG_SECFLTR,
(ulong) reconfigBuffer->EnableSecurityFilter);
}
return STATUS_SUCCESS;
}
#if MILLEN
extern Interface *IFList;
//
// Millennium doesn't have the same PnP reconfigure support via NDIS as
// Win2000, so IPReconfigIRDP is
//
NTSTATUS
IPReconfigIRDP(uint IfIndex, PIP_PNP_RECONFIG_REQUEST pReconfigRequest)
{
NET_PNP_EVENT PnpEvent;
Interface *IF = NULL;
NTSTATUS NtStatus = STATUS_INVALID_PARAMETER;
CTELockHandle Handle;
//
// Only allow IRDP reconfigs.
//
if ((pReconfigRequest->Flags & IP_PNP_FLAG_PERFORM_ROUTER_DISCOVERY) == 0 &&
(pReconfigRequest->Flags & IP_PNP_FLAG_DHCP_PERFORM_ROUTER_DISCOVERY) == 0) {
goto done;
}
//
// Search for the interface. Hold the route table lock and grab a
// reference while in use.
//
CTEGetLock(&RouteTableLock.Lock, &Handle);
for (IF = IFList; IF != NULL; IF = IF->if_next) {
if ((IF->if_refcount != 0) && (IF->if_index == IfIndex)) {
break;
}
}
if (IF == NULL) {
CTEFreeLock(&RouteTableLock.Lock, Handle);
goto done;
} else {
LOCKED_REFERENCE_IF(IF);
CTEFreeLock(&RouteTableLock.Lock, Handle);
}
//
// Set up our PnP event buffer to make it look like it came from NDIS --
// NetEventReconfigure.
//
NdisZeroMemory(&PnpEvent, sizeof(NET_PNP_EVENT));
PnpEvent.NetEvent = NetEventReconfigure;
PnpEvent.Buffer = (PVOID) pReconfigRequest;
PnpEvent.BufferLength = sizeof(IP_PNP_RECONFIG_REQUEST);
NtStatus = IPPnPReconfigure(IF, &PnpEvent);
done:
if (IF) {
DerefIF(IF);
}
return (NtStatus);
}
#endif // MILLEN
NTSTATUS
IPPnPCancelRemoveDevice(Interface * interface, PNET_PNP_EVENT netPnPEvent)
{
UNREFERENCED_PARAMETER(netPnPEvent);
interface->if_flags &= ~IF_FLAGS_REMOVING_DEVICE;
return STATUS_SUCCESS;
}
NTSTATUS
IPPnPQueryRemoveDevice(Interface * interface, PNET_PNP_EVENT netPnPEvent)
{
NTSTATUS status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(netPnPEvent);
//
// CAVEAT: PnP generates this event even on adapters that are not being
// disabled (Bug # 618052)! Hence this flag should not be used to disable
// communication over the adapter.
//
interface->if_flags |= IF_FLAGS_REMOVING_DEVICE;
return status;
}
NTSTATUS
IPPnPQueryPower(Interface * interface, PNET_PNP_EVENT netPnPEvent)
{
PNET_DEVICE_POWER_STATE powState = (PNET_DEVICE_POWER_STATE) netPnPEvent->Buffer;
NTSTATUS status = STATUS_SUCCESS;
//TCPTRACE(("Received query power (%x) event for interface %lx\n",*powState,interface));
switch (*powState) {
case NetDeviceStateD0:
break;
case NetDeviceStateD1:
case NetDeviceStateD2:
case NetDeviceStateD3:
//
// Change the state to removing power anyways, because power may get
// removed even if we reject the query power.
//
interface->if_flags |= IF_FLAGS_REMOVING_POWER;
break;
default:
ASSERT(FALSE);
}
return status;
}
NTSTATUS
IPPnPSetPower(Interface * interface, PNET_PNP_EVENT netPnPEvent)
{
PNET_DEVICE_POWER_STATE powState = (PNET_DEVICE_POWER_STATE) netPnPEvent->Buffer;
// TCPTRACE(("Received set power (%x) event for interface %lx\n",*powState,interface));
switch (*powState) {
case NetDeviceStateD0:
interface->if_flags &= ~(IF_FLAGS_REMOVING_POWER | IF_FLAGS_POWER_DOWN);
//Force connect event
if ((interface->if_flags & IF_FLAGS_MEDIASENSE) && !DisableMediaSense) {
//query for mediastatus
interface->if_mediastatus = 1;
if (interface->if_dondisreq) {
uint MediaStatus;
NTSTATUS Status;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPstat: querying for Media connect status %x\n", interface));
Status = (*interface->if_dondisreq) (interface->if_lcontext,
NdisRequestQueryInformation,
OID_GEN_MEDIA_CONNECT_STATUS,
&MediaStatus,
sizeof(MediaStatus),
NULL,
TRUE);
if (Status == NDIS_STATUS_SUCCESS) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPStat: Media status %x\n", Status));
if (MediaStatus == NdisMediaStateDisconnected) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Disconnected? %x\n", MediaStatus));
interface->if_mediastatus = 0;
}
}
}
if (interface->if_mediastatus) {
IPNotifyClientsIPEvent(
interface,
IP_MEDIA_CONNECT);
} else {
IPNotifyClientsIPEvent(
interface,
IP_MEDIA_DISCONNECT);
}
}
// Update offload capabilities, and notify IPSec of the changes.
if (!DisableTaskOffload) {
IFOffloadCapability IFOC;
TDI_STATUS Status;
Status = IPQuerySetOffload(interface, &IFOC);
if (Status == TDI_SUCCESS) {
interface->if_OffloadFlags = IFOC.ifoc_OffloadFlags;
interface->if_IPSecOffloadFlags = IFOC.ifoc_IPSecOffloadFlags;
} else {
interface->if_OffloadFlags = 0;
interface->if_IPSecOffloadFlags = 0;
}
if (IPSecNdisStatusPtr) {
(*IPSecNdisStatusPtr)(interface, NDIS_STATUS_INTERFACE_UP);
}
}
break;
case NetDeviceStateD1:
case NetDeviceStateD2:
case NetDeviceStateD3:
interface->if_flags |= IF_FLAGS_POWER_DOWN;
break;
default:
ASSERT(FALSE);
}
return STATUS_SUCCESS;
}
void
IPPnPPowerComplete(PNET_PNP_EVENT NetPnPEvent, NTSTATUS Status)
{
Interface *interface;
NDIS_STATUS retStatus;
PNetPnPEventReserved Reserved = (PNetPnPEventReserved) NetPnPEvent->TransportReserved;
interface = Reserved->Interface;
retStatus = Reserved->PnPStatus;
if (STATUS_SUCCESS == Status) {
retStatus = Status;
}
if (interface) {
(*interface->if_pnpcomplete) (interface->if_lcontext, retStatus, NetPnPEvent);
} else {
NdisCompletePnPEvent(retStatus, NULL, NetPnPEvent);
}
}
//** DoPnPEvent - Handles PNP/PM events.
//
// Called from the worker thread event scheduled by IPPnPEvent
// We take action depending on the type of the event.
//
// Entry:
// Context - This is a pointer to a NET_PNP_EVENT that describes
// the PnP indication.
//
// Exit:
// None.
//
NDIS_STATUS
DoPnPEvent(Interface * interface, PVOID Context)
{
PNET_PNP_EVENT NetPnPEvent = (PNET_PNP_EVENT) Context;
NDIS_STATUS Status, retStatus;
PTDI_PNP_CONTEXT tdiPnPContext2, tdiPnPContext1;
USHORT context1Size, context2Size;
USHORT context1ntes;
tdiPnPContext2 = tdiPnPContext1 = NULL;
// this will contain the cummulative status.
Status = retStatus = STATUS_SUCCESS;
if (interface == NULL) {
// if its not NetEventReconfigure || NetEventBindsComplete
// fail the request
if ((NetPnPEvent->NetEvent != NetEventReconfigure) &&
(NetPnPEvent->NetEvent != NetEventBindsComplete) &&
(NetPnPEvent->NetEvent != NetEventBindList)) {
retStatus = STATUS_UNSUCCESSFUL;
goto pnp_complete;
}
}
//
// First handle it in IP.
//
switch (NetPnPEvent->NetEvent) {
case NetEventReconfigure:
Status = IPPnPReconfigure(interface, NetPnPEvent);
break;
case NetEventCancelRemoveDevice:
Status = IPPnPCancelRemoveDevice(interface, NetPnPEvent);
break;
case NetEventQueryRemoveDevice:
Status = IPPnPQueryRemoveDevice(interface, NetPnPEvent);
break;
case NetEventQueryPower:
Status = IPPnPQueryPower(interface, NetPnPEvent);
break;
case NetEventSetPower:
Status = IPPnPSetPower(interface, NetPnPEvent);
break;
case NetEventBindsComplete:
DecrInitTimeInterfaces(NULL);
goto pnp_complete;
case NetEventPnPCapabilities:
if (interface) {
PNDIS_PNP_CAPABILITIES PnpCap = (PNDIS_PNP_CAPABILITIES) NetPnPEvent->Buffer;
interface->if_pnpcap = PnpCap->Flags;
IPNotifyClientsIPEvent(interface, IP_INTERFACE_WOL_CAPABILITY_CHANGE);
}
break;
case NetEventBindList: {
#if !MILLEN
PWSTR BindList;
PWSTR DeviceName;
CTELockHandle Handle;
DestinationEntry* Dest;
uint IsDataLeft;
uchar IteratorContext[CONTEXT_SIZE];
Interface* CurrIF;
if (NetPnPEvent->BufferLength) {
BindList = CTEAllocMem(NetPnPEvent->BufferLength);
if (BindList) {
RtlCopyMemory(BindList, NetPnPEvent->Buffer,
NetPnPEvent->BufferLength);
}
} else {
BindList = NULL;
}
CTEGetLock(&RouteTableLock.Lock, &Handle);
// Update the bind list
if (IPBindList) {
CTEFreeMem(IPBindList);
}
IPBindList = BindList;
// Recompute interface orderings
for (CurrIF = IFList; CurrIF; CurrIF = CurrIF->if_next) {
if (CurrIF->if_devname.Buffer) {
DeviceName =
CurrIF->if_devname.Buffer +
sizeof(TCP_EXPORT_STRING_PREFIX) / sizeof(WCHAR) - 1;
CurrIF->if_order = IPMapDeviceNameToIfOrder(DeviceName);
}
}
// Reorder route-lists for all existing destinations
RtlZeroMemory(IteratorContext, sizeof(IteratorContext));
IsDataLeft = GetNextDest(IteratorContext, &Dest);
while (IsDataLeft) {
if (Dest) {
SortRoutesInDest(Dest);
}
IsDataLeft = GetNextDest(IteratorContext, &Dest);
}
CTEFreeLock(&RouteTableLock.Lock, Handle);
#endif // MILLEN
retStatus = NDIS_STATUS_SUCCESS;
goto pnp_complete;
}
default:
retStatus = NDIS_STATUS_FAILURE;
goto pnp_complete;
}
if (STATUS_SUCCESS != Status) {
retStatus = Status;
}
//
// next notify internal clients.
// If we have any open connections, return STATUS_DEVICE_BUSY
//
Status = NotifyPnPInternalClients(interface, NetPnPEvent);
PAGED_CODE();
if (STATUS_SUCCESS != Status) {
retStatus = Status;
}
if (NetPnPEvent->NetEvent == NetEventReconfigure) {
goto pnp_complete;
}
//
// and finally notify tdi clients.
//
//
// context1 contains the list of ip addresses on this interface.
// but dont create a long list if we have too many addresses.
//
context1ntes = (USHORT) (interface->if_ntecount > 32 ? 32 : interface->if_ntecount);
if (context1ntes) {
context1Size = sizeof(TRANSPORT_ADDRESS) +
(sizeof(TA_ADDRESS) + sizeof(TDI_ADDRESS_IP)) * (context1ntes);
tdiPnPContext1 = CTEAllocMem(sizeof(TDI_PNP_CONTEXT) - 1 + context1Size);
if (!tdiPnPContext1) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto pnp_complete;
} else {
PTRANSPORT_ADDRESS pAddrList;
PTA_ADDRESS pAddr;
PTDI_ADDRESS_IP pIPAddr;
int i;
NetTableEntry *nextNTE;
RtlZeroMemory(tdiPnPContext1, context1Size);
tdiPnPContext1->ContextSize = context1Size;
tdiPnPContext1->ContextType = TDI_PNP_CONTEXT_TYPE_IF_ADDR;
pAddrList = (PTRANSPORT_ADDRESS) tdiPnPContext1->ContextData;
pAddr = (PTA_ADDRESS) pAddrList->Address;
//
// copy all the nte addresses
//
for (i = context1ntes, nextNTE = interface->if_nte;
i && nextNTE;
nextNTE = nextNTE->nte_ifnext) {
if (nextNTE->nte_flags & NTE_VALID) {
pAddr->AddressLength = sizeof(TDI_ADDRESS_IP);
pAddr->AddressType = TDI_ADDRESS_TYPE_IP;
pIPAddr = (PTDI_ADDRESS_IP) pAddr->Address;
pIPAddr->in_addr = nextNTE->nte_addr;
pAddr = (PTA_ADDRESS) ((PCHAR) pAddr + sizeof(TA_ADDRESS) +
sizeof(TDI_ADDRESS_IP));
pAddrList->TAAddressCount++;
i--;
}
}
}
}
//
// context2 contains a PDO.
//
context2Size = sizeof(PVOID);
tdiPnPContext2 = CTEAllocMem(sizeof(TDI_PNP_CONTEXT) - 1 + context2Size);
if (tdiPnPContext2) {
PNetPnPEventReserved Reserved = (PNetPnPEventReserved) NetPnPEvent->TransportReserved;
Reserved->Interface = interface;
Reserved->PnPStatus = retStatus;
tdiPnPContext2->ContextSize = sizeof(PVOID);
tdiPnPContext2->ContextType = TDI_PNP_CONTEXT_TYPE_PDO;
*(ULONG_PTR UNALIGNED *) tdiPnPContext2->ContextData =
(ULONG_PTR) interface->if_pnpcontext;
//
// Notify our TDI clients about this PNP event.
//
retStatus = TdiPnPPowerRequest(
&interface->if_devname,
NetPnPEvent,
tdiPnPContext1,
tdiPnPContext2,
IPPnPPowerComplete);
} else {
retStatus = STATUS_INSUFFICIENT_RESOURCES;
}
pnp_complete:
PAGED_CODE();
if (tdiPnPContext1) {
CTEFreeMem(tdiPnPContext1);
}
if (tdiPnPContext2) {
CTEFreeMem(tdiPnPContext2);
}
return retStatus;
}
TDI_STATUS
IPGetDeviceRelation(RouteCacheEntry * rce, PVOID * pnpDeviceContext)
{
RouteTableEntry *rte;
CTELockHandle LockHandle;
CTEGetLock(&rce->rce_lock, &LockHandle);
if (rce->rce_flags == RCE_ALL_VALID) {
rte = rce->rce_rte;
if (rte->rte_if->if_pnpcontext) {
*pnpDeviceContext = rte->rte_if->if_pnpcontext;
CTEFreeLock(&rce->rce_lock, LockHandle);
return TDI_SUCCESS;
} else {
CTEFreeLock(&rce->rce_lock, LockHandle);
return TDI_INVALID_STATE;
}
} else {
CTEFreeLock(&rce->rce_lock, LockHandle);
return TDI_INVALID_STATE;
}
}
//** IPPnPEvent - ARP PnPEvent handler.
//
// Called by the ARP when PnP or PM events occurs.
//
// Entry:
// Context - The context that we gave to ARP.
// NetPnPEvent - This is a pointer to a NET_PNP_EVENT that describes
// the PnP indication.
//
// Exit:
// STATUS_PENDING if this event is queued on a worker thread, otherwise
// proper error code.
//
NDIS_STATUS
__stdcall
IPPnPEvent(void *Context, PNET_PNP_EVENT NetPnPEvent)
{
NetTableEntry *nte;
Interface *interface = NULL;
PAGED_CODE();
if (Context) {
nte = (NetTableEntry *) Context;
if (!(nte->nte_flags & NTE_IF_DELETING)) {
interface = nte->nte_if;
}
}
return DoPnPEvent(interface, NetPnPEvent);
}
//** IPAbbreviateFriendlyName - Abbreviates NIC's friendly name by
// truncating the name string
//
// Called vy IPNotifyClientsMediaSense if NIC's name is too long
//
// Entry:
// UNICODE_STRING DeviceName - the name to be truncated
// USHORT MaxLen - length to truncate to (in bytes)
//
// Exit:
// Truncated name is returned in DeviceName
//
void IPAbbreviateFriendlyName(PUNICODE_STRING DeviceName, USHORT MaxLen) {
PWCHAR Str;
PWCHAR CpyFromPos, CpyToPos;
CONST WCHAR Ellipses[] = L"...";
USHORT EllipsesLen = (USHORT) wcslen(Ellipses)*sizeof(WCHAR);
if (DeviceName->Length <= MaxLen) {
return;
}
//
// we want to keep 1st word and truncate after it
//
CpyToPos = wcschr(DeviceName->Buffer, L' ');
if ( CpyToPos == NULL ||
( CpyToPos + EllipsesLen / sizeof(WCHAR) >=
DeviceName->Buffer + MaxLen / sizeof(WCHAR) )) {
DeviceName->Buffer[MaxLen / sizeof(WCHAR)] = UNICODE_NULL;
DeviceName->Length = MaxLen;
wcscpy(DeviceName->Buffer + (MaxLen - EllipsesLen) / sizeof(WCHAR),
Ellipses);
return;
}
// add ellipses
wcsncpy (CpyToPos, Ellipses, EllipsesLen / sizeof(WCHAR));
CpyToPos += EllipsesLen / sizeof(WCHAR);
//
// skip to section in string that will fit in buffer
// look for a good cutoff point
//
CpyFromPos = CpyToPos + (DeviceName->Length - MaxLen) / sizeof(WCHAR);
Str = wcschr(CpyFromPos, L' ');
if (Str != NULL) {
CpyFromPos = Str + 1;
}
// copy the string
wcscpy (CpyToPos, CpyFromPos);
DeviceName->Length = (USHORT) wcslen (DeviceName->Buffer) * sizeof(WCHAR);
}