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
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);
|
|
}
|