mirror of https://github.com/tongzx/nt5src
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.
2790 lines
70 KiB
2790 lines
70 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mcmisc.c
|
|
|
|
Abstract:
|
|
|
|
This module implements routines associated with mrinfo and mtrace
|
|
functionality.
|
|
|
|
Author:
|
|
|
|
[email protected] 2-9-98
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "allinc.h"
|
|
#include <iptypes.h>
|
|
#include <dsrole.h>
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// Undefine this if we can't bind/set oif by IfIndex.
|
|
// This can be turned on if Bug #208359 gets fixed.
|
|
//
|
|
|
|
#define RAW_UNNUMBERED_SUPPORT
|
|
|
|
#undef UDP_UNNUMBERED_SUPPORT
|
|
|
|
// Miscellaneous IGMP socket used for mrinfo, mtrace, etc.
|
|
|
|
SOCKET McMiscSocket = INVALID_SOCKET;
|
|
|
|
// Miscellaneous UDP socket used for RAS advertisements, etc.
|
|
// Note that no event is currently associated with this socket,
|
|
// since it's currently only used for sending.
|
|
|
|
SOCKET g_UDPMiscSocket = INVALID_SOCKET;
|
|
|
|
//
|
|
// Set this to >0 to generate extra logging information
|
|
//
|
|
|
|
DWORD g_mcastDebugLevel = 0;
|
|
|
|
//
|
|
// This is an array mapping an error code in priority order
|
|
// (MFE_...) to the actual value which goes in a packet.
|
|
//
|
|
|
|
|
|
//
|
|
// MFE_NO_ERROR 0x00
|
|
// MFE_REACHED_CORE 0x08
|
|
// MFE_NOT_FORWARDING 0x07
|
|
// MFE_WRONG_IF 0x01
|
|
// MFE_PRUNED_UPSTREAM 0x02
|
|
// MFE_OIF_PRUNED 0x03
|
|
// MFE_BOUNDARY_REACHED 0x04
|
|
// MFE_NO_MULTICAST 0x0A
|
|
// MFE_IIF 0x09
|
|
// MFE_NO_ROUTE 0x05 - set by rtrmgr
|
|
// MFE_NOT_LAST_HOP 0x06 - set by rtrmgr
|
|
// MFE_OLD_ROUTER 0x82
|
|
// MFE_PROHIBITED 0x83
|
|
// MFE_NO_SPACE 0x81
|
|
//
|
|
|
|
static int mtraceErrCode[MFE_NO_SPACE+1] =
|
|
{
|
|
0x00,
|
|
0x08,
|
|
0x07,
|
|
0x01,
|
|
0x02,
|
|
0x03,
|
|
0x04,
|
|
0x0A,
|
|
0x09,
|
|
0x05,
|
|
0x06,
|
|
0x82,
|
|
0x83,
|
|
0x81
|
|
};
|
|
|
|
DWORD
|
|
MulticastOwner(
|
|
PICB picb,
|
|
PPROTO_CB *pcbOwner,
|
|
PPROTO_CB *pcbQuerier
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks up which protocol instance "owns" a given interface, and which
|
|
is the IGMP querying instance.
|
|
|
|
Locks:
|
|
|
|
Assumes caller holds read lock on ICB list
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY pleNode;
|
|
PPROTO_CB pOwner = NULL,
|
|
pQuerier = NULL;
|
|
|
|
if (g_mcastDebugLevel > 0) {
|
|
|
|
Trace1(MCAST, "MulticastOwner: Looking for owner of %x", picb);
|
|
|
|
if ( picb->leProtocolList.Flink == &(picb->leProtocolList))
|
|
{
|
|
Trace0(MCAST, "MulticastOwner: Protocol list is empty.");
|
|
}
|
|
}
|
|
|
|
for (pleNode = picb->leProtocolList.Flink;
|
|
pleNode isnot &(picb->leProtocolList);
|
|
pleNode = pleNode->Flink)
|
|
{
|
|
PIF_PROTO pProto;
|
|
|
|
pProto = CONTAINING_RECORD(pleNode,
|
|
IF_PROTO,
|
|
leIfProtoLink);
|
|
|
|
if (!(pProto->pActiveProto->fSupportedFunctionality & RF_MULTICAST)
|
|
//|| pProto->bPromiscuous
|
|
|| !(pProto->pActiveProto->pfnGetNeighbors))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!pOwner || pOwner->dwProtocolId==MS_IP_IGMP)
|
|
{
|
|
pOwner = pProto->pActiveProto;
|
|
}
|
|
|
|
if (pProto->pActiveProto->dwProtocolId==MS_IP_IGMP)
|
|
{
|
|
pQuerier = pProto->pActiveProto;
|
|
}
|
|
}
|
|
|
|
if (pcbOwner)
|
|
{
|
|
(*pcbOwner) = pOwner;
|
|
}
|
|
|
|
if (pcbQuerier)
|
|
{
|
|
(*pcbQuerier) = pQuerier;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
IPV4_ADDRESS
|
|
defaultSourceAddress(
|
|
PICB picb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Look up the default source address for an interface
|
|
For now, we need to special case IP-in-IP since at least
|
|
the local address is available SOMEWHERE, unlike other
|
|
unnumbered interfaces!
|
|
|
|
Locks:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
if (picb->dwNumAddresses > 0)
|
|
{
|
|
//
|
|
// report 1st binding
|
|
//
|
|
|
|
return picb->pibBindings[0].dwAddress;
|
|
}
|
|
else
|
|
{
|
|
if ((picb->ritType is ROUTER_IF_TYPE_TUNNEL1) &&
|
|
(picb->pIpIpInfo->dwLocalAddress != 0))
|
|
{
|
|
return picb->pIpIpInfo->dwLocalAddress;
|
|
}
|
|
else
|
|
{
|
|
// XXX fill in 0.0.0.0 until this is fixed
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
McIsMyAddress(
|
|
IPV4_ADDRESS dwAddr
|
|
)
|
|
{
|
|
// XXX test whether dwAddr is bound to any interface.
|
|
// If we return FALSE, then an mtrace with this destination address
|
|
// will be reinjected to be forwarded.
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD
|
|
McSetRouterAlert(
|
|
SOCKET s,
|
|
BOOL bEnabled
|
|
)
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
int StartSnooping = bEnabled;
|
|
int cbReturnedBytes;
|
|
|
|
if ( WSAIoctl( s,
|
|
SIO_ABSORB_RTRALERT,
|
|
(char *)&StartSnooping,
|
|
sizeof(StartSnooping),
|
|
NULL,
|
|
0,
|
|
&cbReturnedBytes,
|
|
NULL,
|
|
NULL) )
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
StartMcMisc(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD dwErr = NO_ERROR, dwRetval;
|
|
SOCKADDR_IN saLocalIf;
|
|
|
|
Trace1(MCAST,
|
|
"StartMcMisc() initiated with filever=%d",
|
|
VER_PRODUCTBUILD);
|
|
|
|
InitializeBoundaryTable();
|
|
|
|
do
|
|
{
|
|
//
|
|
// create input socket
|
|
//
|
|
|
|
McMiscSocket = WSASocket(AF_INET,
|
|
SOCK_RAW,
|
|
IPPROTO_IGMP,
|
|
NULL,
|
|
0,
|
|
0);
|
|
|
|
if (McMiscSocket == INVALID_SOCKET)
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
|
|
Trace1(MCAST,
|
|
"error %d creating mrinfo/mtrace socket",
|
|
dwErr);
|
|
|
|
// LogErr1(CREATE_SOCKET_FAILED_2, lpszAddr, dwErr);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// bind socket to any interface and port 0 (0 => doesnt matter)
|
|
//
|
|
|
|
saLocalIf.sin_family = PF_INET;
|
|
saLocalIf.sin_addr.s_addr = INADDR_ANY;
|
|
saLocalIf.sin_port = 0;
|
|
|
|
//
|
|
// bind the input socket
|
|
//
|
|
|
|
dwErr = bind(McMiscSocket,
|
|
(SOCKADDR FAR *)&saLocalIf,
|
|
sizeof(SOCKADDR));
|
|
|
|
if (dwErr == SOCKET_ERROR)
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
|
|
Trace1(MCAST,
|
|
"error %d binding on mrinfo/mtrace socket",
|
|
dwErr);
|
|
|
|
// LogErr1(BIND_FAILED, lpszAddr, dwErr);
|
|
|
|
break;
|
|
}
|
|
|
|
Trace0(MCAST, "StartMcMisc: bind succeeded");
|
|
|
|
//
|
|
// to respond to mrinfo, and unicast mtraces, we don't need the
|
|
// following.
|
|
// To respond to mtrace queries which are multicast
|
|
// (to the group being traced, to ALL-<proto>-ROUTERS, or
|
|
// to ALL-ROUTERS), we do need this.
|
|
//
|
|
|
|
|
|
#if 0
|
|
#ifdef SIO_RCVALL_HOST
|
|
{
|
|
//
|
|
// put the socket in promiscuous igmp mode.
|
|
// (no need to specify which protocol we want, as it's taken
|
|
// from the protocol we used in the WSASocket() call above)
|
|
//
|
|
{
|
|
DWORD dwEnable = 1;
|
|
DWORD dwNum;
|
|
|
|
dwRetval = WSAIoctl(McMiscSocket, SIO_RCVALL_HOST,
|
|
(char *)&dwEnable, sizeof(dwEnable), NULL, 0, &dwNum,
|
|
NULL, NULL);
|
|
|
|
if (dwRetval !=0) {
|
|
// LPSTR lpszAddr = "ANY";
|
|
dwRetval = WSAGetLastError();
|
|
Trace1(MCAST,
|
|
"error %d setting mrinfo/mtrace socket as host-promiscuous IGMP",
|
|
dwRetval);
|
|
// LogErr1(SET_MCAST_IF_FAILED, lpszAddr, dwRetval);
|
|
|
|
// Don't set dwErr in this case, since we can still
|
|
// respond to unicast queries.
|
|
break;
|
|
} else {
|
|
Trace0(MCAST, "host-promiscuous IGMP enabled on mrinfo/mtrace socket");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Tell the kernel to hand us IGMP packets with the RouterAlert
|
|
// option, even if they're not destined to us
|
|
|
|
McSetRouterAlert( McMiscSocket, TRUE );
|
|
|
|
//
|
|
// Associate an event with the socket
|
|
//
|
|
|
|
if (WSAEventSelect(McMiscSocket,
|
|
g_hMcMiscSocketEvent,
|
|
FD_READ | FD_ADDRESS_LIST_CHANGE) == SOCKET_ERROR)
|
|
{
|
|
Trace1(MCAST,
|
|
"StartMcMisc: WSAEventSelect() failed. Error %d",
|
|
WSAGetLastError());
|
|
|
|
closesocket(McMiscSocket);
|
|
|
|
McMiscSocket = INVALID_SOCKET;
|
|
|
|
continue;
|
|
}
|
|
|
|
} while(0);
|
|
|
|
if (dwErr!=NO_ERROR)
|
|
{
|
|
StopMcMisc();
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
VOID
|
|
StopMcMisc(
|
|
VOID
|
|
)
|
|
{
|
|
Trace0(MCAST,
|
|
"StopMcMisc() initiated");
|
|
|
|
//
|
|
// close input socket
|
|
//
|
|
|
|
if (McMiscSocket!=INVALID_SOCKET)
|
|
{
|
|
if (closesocket(McMiscSocket) == SOCKET_ERROR) {
|
|
|
|
Trace1(MCAST,
|
|
"error %d closing socket",
|
|
WSAGetLastError());
|
|
}
|
|
|
|
McMiscSocket = INVALID_SOCKET;
|
|
}
|
|
|
|
Trace0(MCAST, "StopMcMisc() complete");
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
HandleMcMiscMessages(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Accepts mrinfo and mtrace messages and hands them off to the appropriate
|
|
routine.
|
|
Also called to handle address change notification
|
|
|
|
Locks:
|
|
|
|
Acquires the ICB lock as reader if processing Mc messages
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwErr, dwNumBytes, dwFlags, dwAddrLen, dwSizeOfHeader;
|
|
DWORD dwDataLen;
|
|
SOCKADDR_IN sinFrom;
|
|
PIGMP_HEADER pIgmpMsg;
|
|
PIP_HEADER pIpHeader;
|
|
BOOL bSetIoctl, bUnlock;
|
|
|
|
WSANETWORKEVENTS NetworkEvents;
|
|
|
|
bSetIoctl = FALSE;
|
|
bUnlock = FALSE;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Figure out if its an address change or read
|
|
//
|
|
|
|
dwErr = WSAEnumNetworkEvents(McMiscSocket,
|
|
g_hMcMiscSocketEvent,
|
|
&NetworkEvents);
|
|
|
|
if(dwErr isnot NO_ERROR)
|
|
{
|
|
bSetIoctl = TRUE;
|
|
|
|
Trace1(ERR,
|
|
"HandleMcMiscMessages: Error %d from WSAEnumNetworkEvents",
|
|
WSAGetLastError());
|
|
|
|
break;
|
|
}
|
|
|
|
if(NetworkEvents.lNetworkEvents & FD_ADDRESS_LIST_CHANGE)
|
|
{
|
|
bSetIoctl = TRUE;
|
|
|
|
dwErr = NetworkEvents.iErrorCode[FD_ADDRESS_LIST_CHANGE_BIT];
|
|
|
|
Trace0(GLOBAL,
|
|
"HandleMcMiscMessages: Received Address change notification");
|
|
|
|
if(dwErr isnot NO_ERROR)
|
|
{
|
|
Trace1(ERR,
|
|
"HandleMcMiscMessages: ErrorCode %d",
|
|
dwErr);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// All's good, handle the binding change
|
|
//
|
|
|
|
HandleAddressChangeNotification();
|
|
|
|
break;
|
|
}
|
|
|
|
ENTER_READER(ICB_LIST);
|
|
|
|
bUnlock = TRUE;
|
|
|
|
//
|
|
// read the incoming packet
|
|
//
|
|
|
|
dwAddrLen = sizeof(sinFrom);
|
|
dwFlags = 0;
|
|
|
|
dwErr = WSARecvFrom(McMiscSocket,
|
|
&g_wsaMcRcvBuf,
|
|
1,
|
|
&dwNumBytes,
|
|
&dwFlags,
|
|
(SOCKADDR FAR *)&sinFrom,
|
|
&dwAddrLen,
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// check if any error in reading packet
|
|
//
|
|
|
|
if ((dwErr!=0) || (dwNumBytes==0))
|
|
{
|
|
// LPSTR lpszAddr = "ANY";
|
|
|
|
dwErr = WSAGetLastError();
|
|
|
|
Trace1(MCAST,
|
|
"HandleMcMiscMessages: Error %d receiving IGMP packet",
|
|
dwErr);
|
|
|
|
// LogErr1(RECVFROM_FAILED, lpszAddr, dwErr);
|
|
|
|
break;
|
|
}
|
|
|
|
pIpHeader = (PIP_HEADER)g_wsaMcRcvBuf.buf;
|
|
dwSizeOfHeader = ((pIpHeader->byVerLen)&0x0f)<<2;
|
|
|
|
pIgmpMsg = (PIGMP_HEADER)(((PBYTE)pIpHeader) + dwSizeOfHeader);
|
|
|
|
dwDataLen = ntohs(pIpHeader->wLength) - dwSizeOfHeader;
|
|
|
|
if (g_mcastDebugLevel > 0)
|
|
{
|
|
Trace4(MCAST,
|
|
"HandleMcMiscMessages: Type is %d (0x%x), code %d (0x%x).",
|
|
(DWORD)pIgmpMsg->byType,
|
|
(DWORD)pIgmpMsg->byType,
|
|
(DWORD)pIgmpMsg->byCode,
|
|
(DWORD)pIgmpMsg->byCode);
|
|
|
|
Trace2(MCAST,
|
|
"HandleMcMiscMessages: IP Length is %d. Header Length %d",
|
|
ntohs(pIpHeader->wLength),
|
|
dwSizeOfHeader);
|
|
|
|
Trace2(MCAST,
|
|
"HandleMcMiscMessages: Src: %d.%d.%d.%d dest: %d.%d.%d.%d",
|
|
PRINT_IPADDR(pIpHeader->dwSrc),
|
|
PRINT_IPADDR(pIpHeader->dwDest));
|
|
|
|
TraceDump(TRACEID,(PBYTE)pIpHeader,dwNumBytes,2,FALSE,NULL);
|
|
}
|
|
|
|
//
|
|
// Verify minimum length
|
|
//
|
|
|
|
if (dwNumBytes < MIN_IGMP_PACKET_SIZE)
|
|
{
|
|
Trace2(MCAST,
|
|
"%d-byte packet from %d.%d.%d.%d is too small",
|
|
dwNumBytes,
|
|
PRINT_IPADDR(pIpHeader->dwSrc));
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Check for mal-formed packets that might report bad lengths
|
|
//
|
|
|
|
if (dwDataLen > (dwNumBytes - dwSizeOfHeader))
|
|
{
|
|
Trace3(MCAST,
|
|
"%d-byte packet from %d.%d.%d.%d is smaller than "
|
|
"indicated length %d", dwNumBytes,
|
|
PRINT_IPADDR(pIpHeader->dwSrc),
|
|
dwDataLen);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Verify IGMP checksum
|
|
//
|
|
|
|
if (Compute16BitXSum((PVOID)pIgmpMsg, dwDataLen) != 0)
|
|
{
|
|
Trace4( MCAST,
|
|
"Wrong IGMP checksum %d-byte packet received from %d.%d.%d.%d, type %d.%d",
|
|
dwDataLen,
|
|
PRINT_IPADDR(pIpHeader->dwSrc),
|
|
pIgmpMsg->byType, pIgmpMsg->byCode );
|
|
|
|
break;
|
|
}
|
|
|
|
if (pIgmpMsg->byType is IGMP_DVMRP
|
|
&& pIgmpMsg->byCode is DVMRP_ASK_NEIGHBORS2)
|
|
{
|
|
SOCKADDR_IN sinDestAddr;
|
|
|
|
sinDestAddr.sin_family = PF_INET;
|
|
sinDestAddr.sin_addr.s_addr = pIpHeader->dwSrc;
|
|
sinDestAddr.sin_port = 0;
|
|
|
|
HandleMrinfoRequest((IPV4_ADDRESS)pIpHeader->dwDest,
|
|
&sinDestAddr
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
if (pIgmpMsg->byType is IGMP_MTRACE_REQUEST)
|
|
{
|
|
HandleMtraceRequest(&g_wsaMcRcvBuf);
|
|
}
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
if(bSetIoctl)
|
|
{
|
|
dwErr = WSAIoctl(McMiscSocket,
|
|
SIO_ADDRESS_LIST_CHANGE,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&dwNumBytes,
|
|
NULL,
|
|
NULL);
|
|
|
|
if(dwErr is SOCKET_ERROR)
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
|
|
if((dwErr isnot WSAEWOULDBLOCK) and
|
|
(dwErr isnot WSA_IO_PENDING) and
|
|
(dwErr isnot NO_ERROR))
|
|
{
|
|
Trace1(ERR,
|
|
"HandleMcMiscMessages: Error %d from SIO_ADDRESS_LIST_CHANGE",
|
|
dwErr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bUnlock)
|
|
{
|
|
EXIT_LOCK(ICB_LIST);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
FindBindingWithLocalAddress(
|
|
OUT PICB *ppicb,
|
|
OUT PIPV4_ADDRESS pdwIfAddress,
|
|
IN IPV4_ADDRESS dwAddress
|
|
)
|
|
{
|
|
BOOL bFound = FALSE;
|
|
PLIST_ENTRY pleNode;
|
|
IPV4_ADDRESS ipFoundMask;
|
|
|
|
//
|
|
// Lock the ICBList for reading
|
|
//
|
|
|
|
ENTER_READER(ICB_LIST);
|
|
|
|
for (pleNode = ICBList.Flink;
|
|
pleNode isnot &ICBList && !bFound;
|
|
pleNode = pleNode->Flink)
|
|
{
|
|
DWORD dwIndex;
|
|
PICB picb;
|
|
|
|
picb = CONTAINING_RECORD(pleNode,
|
|
ICB,
|
|
leIfLink);
|
|
|
|
for (dwIndex=0;
|
|
dwIndex<picb->dwNumAddresses && !bFound;
|
|
dwIndex++)
|
|
{
|
|
PICB_BINDING pb = &picb->pibBindings[dwIndex];
|
|
|
|
if (dwAddress == pb->dwAddress)
|
|
{
|
|
*pdwIfAddress = pb->dwAddress;
|
|
|
|
*ppicb = picb;
|
|
|
|
bFound = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXIT_LOCK(ICB_LIST);
|
|
|
|
if (bFound)
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
*ppicb = NULL;
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
BOOL
|
|
IsConnectedTo(
|
|
IN PICB picb,
|
|
IN IPV4_ADDRESS ipAddress,
|
|
OUT PIPV4_ADDRESS pipLocalAddress OPTIONAL,
|
|
OUT PIPV4_ADDRESS pipMask OPTIONAL
|
|
)
|
|
{
|
|
DWORD dwIndex;
|
|
BOOL bFound = FALSE;
|
|
IPV4_ADDRESS ipFoundMask = 0;
|
|
|
|
if (picb->dwRemoteAddress is ipAddress)
|
|
{
|
|
if (pipLocalAddress)
|
|
{
|
|
*pipLocalAddress = defaultSourceAddress(picb);
|
|
}
|
|
if (pipMask)
|
|
{
|
|
*pipMask = ALL_ONES_MASK;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Find interface with longest match
|
|
|
|
for (dwIndex=0;
|
|
dwIndex<picb->dwNumAddresses && !bFound;
|
|
dwIndex++)
|
|
{
|
|
PICB_BINDING pb = &picb->pibBindings[dwIndex];
|
|
|
|
if (((ipAddress & pb->dwMask) is (pb->dwAddress & pb->dwMask))
|
|
&& (!bFound || (pb->dwMask > ipFoundMask)))
|
|
{
|
|
if (pipLocalAddress)
|
|
{
|
|
*pipLocalAddress = pb->dwAddress;
|
|
}
|
|
|
|
bFound = TRUE;
|
|
|
|
ipFoundMask = pb->dwMask;
|
|
}
|
|
}
|
|
|
|
if (pipMask)
|
|
{
|
|
*pipMask = ipFoundMask;
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
DWORD
|
|
FindBindingWithRemoteAddress(
|
|
OUT PICB *ppicb,
|
|
OUT PIPV4_ADDRESS pdwIfAddress,
|
|
IN IPV4_ADDRESS dwAddress
|
|
)
|
|
{
|
|
BOOL bFound = FALSE;
|
|
PLIST_ENTRY pleNode;
|
|
IPV4_ADDRESS ipFoundMask, ipMask, ipLocalAddress;
|
|
|
|
//
|
|
// Lock the ICBList for reading
|
|
//
|
|
|
|
ENTER_READER(ICB_LIST);
|
|
|
|
for (pleNode = ICBList.Flink;
|
|
pleNode isnot &ICBList;
|
|
pleNode = pleNode->Flink)
|
|
{
|
|
DWORD dwIndex;
|
|
PICB picb;
|
|
|
|
picb = CONTAINING_RECORD(pleNode,
|
|
ICB,
|
|
leIfLink);
|
|
|
|
if (IsConnectedTo(picb, dwAddress, &ipLocalAddress, &ipMask)
|
|
&& (!bFound || (ipMask > ipFoundMask)))
|
|
{
|
|
*pdwIfAddress = ipLocalAddress;
|
|
*ppicb = picb;
|
|
bFound = TRUE;
|
|
ipFoundMask = ipMask;
|
|
}
|
|
}
|
|
|
|
EXIT_LOCK(ICB_LIST);
|
|
|
|
if (bFound)
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
*ppicb = NULL;
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
DWORD
|
|
FindBindingForPacket(
|
|
IN PIP_HEADER pIpHeader,
|
|
OUT PICB *ppicb,
|
|
OUT IPV4_ADDRESS *pdwIfAddr
|
|
)
|
|
{
|
|
DWORD dwResult;
|
|
|
|
dwResult = FindBindingWithRemoteAddress(ppicb,
|
|
pdwIfAddr,
|
|
pIpHeader->dwSrc);
|
|
|
|
if (dwResult == NO_ERROR)
|
|
{
|
|
return dwResult;
|
|
}
|
|
|
|
dwResult = FindBindingWithRemoteAddress(ppicb,
|
|
pdwIfAddr,
|
|
pIpHeader->dwDest);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
VOID
|
|
HandleMrinfoRequest(
|
|
IPV4_ADDRESS dwLocalAddr,
|
|
SOCKADDR_IN *sinDestAddr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Accepts an mrinfo request and sends a reply.
|
|
|
|
Locks:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwNumBytesSent, dwResult, dwSize = sizeof(MRINFO_HEADER);
|
|
WSABUF wsMrinfoBuffer;
|
|
MRINFO_HEADER *mriHeader;
|
|
DWORD dwBufSize;
|
|
IPV4_ADDRESS dwIfAddr;
|
|
PLIST_ENTRY pleNode, pleNode2;
|
|
PICB picb;
|
|
PBYTE pb;
|
|
BYTE byIfFlags;
|
|
BOOL bForMe;
|
|
|
|
//
|
|
// If the query was not destined to me, drop it.
|
|
//
|
|
|
|
dwResult = FindBindingWithLocalAddress(&picb,
|
|
&dwIfAddr,
|
|
dwLocalAddr);
|
|
|
|
if (dwResult != NO_ERROR)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Lock the ICBList for reading
|
|
//
|
|
|
|
ENTER_READER(ICB_LIST);
|
|
|
|
do
|
|
{
|
|
|
|
//
|
|
// Calculate required size of response packet
|
|
//
|
|
|
|
for (pleNode = ICBList.Flink;
|
|
pleNode isnot &ICBList;
|
|
pleNode = pleNode->Flink)
|
|
{
|
|
PPROTO_CB pOwner, pQuerier;
|
|
|
|
picb = CONTAINING_RECORD(pleNode,
|
|
ICB,
|
|
leIfLink);
|
|
|
|
dwResult = MulticastOwner(picb,
|
|
&pOwner,
|
|
&pQuerier);
|
|
|
|
//
|
|
// If we didn't find an owner, then we can skip this
|
|
// interface, since we're not doing multicast routing on it.
|
|
//
|
|
|
|
if (!pOwner)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (picb->dwNumAddresses > 0)
|
|
{
|
|
//
|
|
// add iface size per address
|
|
//
|
|
|
|
dwSize += 8+4*picb->dwNumAddresses;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// add single address size for unnumbered iface
|
|
//
|
|
|
|
dwSize += 12;
|
|
}
|
|
|
|
//
|
|
// Call the owner's GetNeighbors() entrypoint
|
|
// with a NULL buffer. This will cause it to tell us the size of
|
|
// its neighbor set
|
|
//
|
|
|
|
dwBufSize = 0;
|
|
byIfFlags = 0;
|
|
|
|
//
|
|
// mrouted doesn't report multiple subnets,
|
|
// so neither do we. Just group all neighbors
|
|
// together on an interface.
|
|
//
|
|
|
|
dwResult = (pOwner->pfnGetNeighbors)(picb->dwIfIndex,
|
|
NULL,
|
|
&dwBufSize,
|
|
&byIfFlags);
|
|
|
|
if ((dwResult isnot NO_ERROR) and
|
|
(dwResult isnot ERROR_INSUFFICIENT_BUFFER))
|
|
{
|
|
//
|
|
// The only errors which will tell us the size needed are
|
|
// NO_ERROR and ERROR_INSUFFICIENT_BUFFER. Anything else
|
|
// means we didn't get the right size
|
|
//
|
|
|
|
Trace2(MCAST,
|
|
"HandleMrinfoRequest: Error %d in GetNeighbours for %S",
|
|
dwResult,
|
|
pOwner->pwszDisplayName);
|
|
|
|
continue;
|
|
}
|
|
|
|
dwSize += dwBufSize;
|
|
}
|
|
|
|
//
|
|
// We can now malloc a buffer and fill in the info
|
|
//
|
|
|
|
wsMrinfoBuffer.len = dwSize;
|
|
|
|
wsMrinfoBuffer.buf = HeapAlloc(IPRouterHeap,
|
|
0,
|
|
dwSize);
|
|
|
|
if(wsMrinfoBuffer.buf is NULL)
|
|
{
|
|
EXIT_LOCK(ICB_LIST);
|
|
|
|
return;
|
|
}
|
|
|
|
mriHeader = (PMRINFO_HEADER)wsMrinfoBuffer.buf;
|
|
|
|
mriHeader->byType = IGMP_DVMRP;
|
|
mriHeader->byCode = DVMRP_NEIGHBORS2;
|
|
mriHeader->wChecksum = 0;
|
|
mriHeader->byReserved = 0;
|
|
|
|
//
|
|
// MRINFO_CAP_MTRACE - set if mtrace handler is available
|
|
// MRINFO_CAP_SNMP - set if public IP Multicast MIB is available
|
|
// MRINFO_CAP_GENID - set if DVMRP 3.255 is available
|
|
// MRINFO_CAP_PRUNE - set if DVMRP 3.255 is available
|
|
//
|
|
|
|
mriHeader->byCapabilities = MRINFO_CAP_MTRACE | MRINFO_CAP_SNMP;
|
|
mriHeader->byMinor = VER_PRODUCTBUILD % 100;
|
|
mriHeader->byMajor = VER_PRODUCTBUILD / 100;
|
|
|
|
//
|
|
// Need to get a list of interfaces, and a list of neighbors
|
|
// (and their info) per interface, updating dwSize as we go.
|
|
//
|
|
|
|
pb = ((PBYTE) wsMrinfoBuffer.buf) + sizeof(MRINFO_HEADER);
|
|
|
|
for (pleNode = ICBList.Flink;
|
|
pleNode isnot &ICBList;
|
|
pleNode = pleNode->Flink)
|
|
{
|
|
PBYTE pbNbrCount, pfIfFlags;
|
|
PPROTO_CB pOwner, pQuerier;
|
|
|
|
picb = CONTAINING_RECORD(pleNode,
|
|
ICB,
|
|
leIfLink);
|
|
|
|
dwResult = MulticastOwner(picb,
|
|
&pOwner,
|
|
&pQuerier);
|
|
|
|
//
|
|
// If we didn't find an owner, then we can skip this
|
|
// interface, since we're not doing multicast routing on it.
|
|
//
|
|
|
|
if (!pOwner)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Fill in interface info
|
|
//
|
|
|
|
*(PIPV4_ADDRESS)pb = defaultSourceAddress(picb);
|
|
|
|
pb += 4;
|
|
*pb++ = 1; // currently metric must be 1
|
|
*pb++ = (BYTE)picb->dwMcastTtl; // threshold
|
|
*pb = 0;
|
|
|
|
//
|
|
// Right now, we only report IP-in-IP tunnels with the tunnel flag
|
|
// In the future, a tunnel should have its own MIB-II ifType
|
|
// value, which should be stored in the ICB structure so we can
|
|
// get at it.
|
|
//
|
|
|
|
if (picb->ritType is ROUTER_IF_TYPE_TUNNEL1)
|
|
{
|
|
//
|
|
// neighbor reached via tunnel
|
|
//
|
|
|
|
*pb |= MRINFO_TUNNEL_FLAG;
|
|
}
|
|
|
|
if (picb->dwOperationalState < IF_OPER_STATUS_CONNECTED)
|
|
{
|
|
//
|
|
// operational status down
|
|
//
|
|
|
|
*pb |= MRINFO_DOWN_FLAG;
|
|
}
|
|
|
|
if (picb->dwAdminState is IF_ADMIN_STATUS_DOWN)
|
|
{
|
|
//
|
|
// administrative status down
|
|
//
|
|
|
|
*pb |= MRINFO_DISABLED_FLAG;
|
|
}
|
|
|
|
pfIfFlags = pb++; // save pointer for later updating
|
|
pbNbrCount = pb++; // save pointer to neighbor count location
|
|
*pbNbrCount = 0;
|
|
|
|
//
|
|
// Call the routing protocol's GetNeighbors() entrypoint
|
|
// with a pointer into the middle of the current packet buffer.
|
|
//
|
|
|
|
dwBufSize = dwSize - (DWORD)(pb-(PBYTE)wsMrinfoBuffer.buf);
|
|
|
|
byIfFlags = 0;
|
|
|
|
dwResult = (pOwner->pfnGetNeighbors)(picb->dwIfIndex,
|
|
(PDWORD)pb,
|
|
&dwBufSize,
|
|
&byIfFlags);
|
|
|
|
if (dwBufSize>0)
|
|
{
|
|
pb += dwBufSize;
|
|
(*pbNbrCount)+= (BYTE)(dwBufSize / sizeof(DWORD));
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If the protocol has no neighbors, we fill in 0.0.0.0
|
|
// because the mrinfo client most people use
|
|
// won't display the flags, metric, and threshold
|
|
// unless the neighbors count is non-zero. 0.0.0.0
|
|
// is legal according to the spec.
|
|
//
|
|
|
|
*(PDWORD)pb = 0;
|
|
|
|
pb += sizeof(DWORD);
|
|
|
|
(*pbNbrCount)++;
|
|
}
|
|
|
|
//
|
|
// set pim/querier/whatever bits
|
|
//
|
|
|
|
*pfIfFlags |= byIfFlags;
|
|
|
|
//
|
|
// Get querier flag
|
|
//
|
|
|
|
if (pQuerier isnot NULL && pQuerier isnot pOwner)
|
|
{
|
|
byIfFlags = 0;
|
|
dwBufSize = 0;
|
|
|
|
dwResult = (pQuerier->pfnGetNeighbors)(picb->dwIfIndex,
|
|
NULL,
|
|
&dwBufSize,
|
|
&byIfFlags);
|
|
|
|
*pfIfFlags |= byIfFlags;
|
|
}
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
EXIT_LOCK(ICB_LIST);
|
|
|
|
//
|
|
// Fill in Checksum
|
|
//
|
|
|
|
mriHeader->wChecksum = Compute16BitXSum(wsMrinfoBuffer.buf,
|
|
dwSize);
|
|
|
|
if (g_mcastDebugLevel > 0)
|
|
{
|
|
Trace2(MCAST,
|
|
"HandleMrinfoRequest: sending reply to %d.%d.%d.%d. Len %d",
|
|
PRINT_IPADDR(sinDestAddr->sin_addr.s_addr),
|
|
wsMrinfoBuffer.len);
|
|
}
|
|
|
|
//
|
|
// Send it off
|
|
//
|
|
|
|
if(WSASendTo(McMiscSocket,
|
|
&wsMrinfoBuffer,
|
|
1,
|
|
&dwNumBytesSent,
|
|
0,
|
|
(const struct sockaddr *)sinDestAddr,
|
|
sizeof(SOCKADDR_IN),
|
|
NULL,
|
|
NULL) == SOCKET_ERROR)
|
|
{
|
|
dwResult = WSAGetLastError();
|
|
|
|
Trace2(MCAST,
|
|
"HandleMrinfoRequest: Err %d sending reply to %d.%d.%d.%d",
|
|
dwResult,
|
|
PRINT_IPADDR(sinDestAddr->sin_addr.s_addr));
|
|
}
|
|
|
|
//
|
|
// Free the buffer
|
|
//
|
|
|
|
HeapFree(IPRouterHeap,
|
|
0,
|
|
wsMrinfoBuffer.buf);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// This function is derived from NTTimeToNTPTime() in
|
|
// src\sockets\tcpcmd\iphlpapi\mscapis.cxx
|
|
//
|
|
|
|
DWORD
|
|
GetCurrentNTP32Time(
|
|
VOID
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get current 32-bit NTP timestamp. The 32-bit form of an NTP timestamp
|
|
consists of the middle 32 bits of the full 64-bit form; that is, the low
|
|
16 bits of the integer part and the high 16 bits of the fractional part.
|
|
|
|
Locks:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
static LARGE_INTEGER li1900 = {0xfde04000, 0x14f373b};
|
|
LARGE_INTEGER liTime;
|
|
DWORD dwMs;
|
|
ULONG hi, lo;
|
|
|
|
GetSystemTimeAsFileTime((LPFILETIME)&liTime);
|
|
|
|
//
|
|
// Seconds is simply the time difference
|
|
//
|
|
|
|
hi = htonl((ULONG)((liTime.QuadPart - li1900.QuadPart) / 10000000));
|
|
|
|
//
|
|
// Ms is the residue from the seconds calculation.
|
|
//
|
|
|
|
dwMs = (DWORD)(((liTime.QuadPart - li1900.QuadPart) % 10000000) / 10000);
|
|
|
|
//
|
|
// time base in the beginning of the year 1900
|
|
//
|
|
|
|
lo = htonl((unsigned long)(.5+0xFFFFFFFF*(double)(dwMs/1000.0)));
|
|
|
|
return (hi << 16) | (lo >> 16);
|
|
}
|
|
|
|
IPV4_ADDRESS
|
|
IfIndexToIpAddress(
|
|
DWORD dwIfIndex
|
|
)
|
|
{
|
|
// Locate picb
|
|
PICB picb = InterfaceLookupByIfIndex(dwIfIndex);
|
|
|
|
return (picb)? defaultSourceAddress(picb) : 0;
|
|
}
|
|
|
|
DWORD
|
|
McSetMulticastIfByIndex(
|
|
SOCKET s,
|
|
DWORD dwSockType,
|
|
DWORD dwIfIndex
|
|
)
|
|
{
|
|
DWORD dwNum, dwErr;
|
|
IPV4_ADDRESS ipAddr;
|
|
|
|
#ifdef RAW_UNNUMBERED_SUPPORT
|
|
if ((dwSockType is SOCK_RAW)
|
|
#ifdef UDP_UNNUMBERED_SUPPORT
|
|
|| (dwSockType is SOCK_DGRAM)
|
|
#endif
|
|
)
|
|
{
|
|
dwErr = WSAIoctl( s,
|
|
SIO_INDEX_MCASTIF,
|
|
(char*)&dwIfIndex,
|
|
sizeof(dwIfIndex),
|
|
NULL,
|
|
0,
|
|
&dwNum,
|
|
NULL,
|
|
NULL );
|
|
|
|
return dwErr;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If we can't set oif to an ifIndex yet, then we
|
|
// attempt to map it to some IP address
|
|
//
|
|
|
|
ipAddr = IfIndexToIpAddress(dwIfIndex);
|
|
|
|
if (!ipAddr)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
return McSetMulticastIf( s, ipAddr );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
McSetMulticastIf(
|
|
SOCKET s,
|
|
IPV4_ADDRESS ipAddr
|
|
)
|
|
{
|
|
SOCKADDR_IN saSrcAddr;
|
|
|
|
saSrcAddr.sin_family = AF_INET;
|
|
saSrcAddr.sin_port = 0;
|
|
saSrcAddr.sin_addr.s_addr = ipAddr;
|
|
|
|
return setsockopt( s,
|
|
IPPROTO_IP,
|
|
IP_MULTICAST_IF,
|
|
(char *)&saSrcAddr.sin_addr,
|
|
sizeof(IN_ADDR) );
|
|
}
|
|
|
|
DWORD
|
|
McSetMulticastTtl(
|
|
SOCKET s,
|
|
DWORD dwTtl
|
|
)
|
|
{
|
|
return setsockopt( s,
|
|
IPPROTO_IP,
|
|
IP_MULTICAST_TTL,
|
|
(char *)&dwTtl,
|
|
sizeof(dwTtl) );
|
|
}
|
|
|
|
DWORD
|
|
McJoinGroupByIndex(
|
|
IN SOCKET s,
|
|
IN DWORD dwSockType,
|
|
IN IPV4_ADDRESS ipGroup,
|
|
IN DWORD dwIfIndex
|
|
)
|
|
{
|
|
struct ip_mreq imOption;
|
|
IPV4_ADDRESS ipInterface;
|
|
|
|
#ifdef RAW_UNNUMBERED_SUPPORT
|
|
if ((dwSockType is SOCK_RAW)
|
|
#ifdef UDP_UNNUMBERED_SUPPORT
|
|
|| (dwSockType is SOCK_DGRAM)
|
|
#endif
|
|
)
|
|
{
|
|
DWORD dwNum, dwErr;
|
|
|
|
imOption.imr_multiaddr.s_addr = ipGroup;
|
|
imOption.imr_interface.s_addr = dwIfIndex;
|
|
|
|
dwErr = WSAIoctl( s,
|
|
SIO_INDEX_ADD_MCAST,
|
|
(char*)&imOption,
|
|
sizeof(imOption),
|
|
NULL,
|
|
0,
|
|
&dwNum,
|
|
NULL,
|
|
NULL );
|
|
|
|
return dwErr;
|
|
}
|
|
#endif
|
|
|
|
ipInterface = IfIndexToIpAddress(ntohl(dwIfIndex));
|
|
|
|
if (!ipInterface)
|
|
{
|
|
Trace1(MCAST, "McJoinGroup: bad IfIndex 0x%x", ntohl(ipInterface));
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
return McJoinGroup( s, ipGroup, ipInterface );
|
|
}
|
|
|
|
DWORD
|
|
McJoinGroup(
|
|
IN SOCKET s,
|
|
IN IPV4_ADDRESS ipGroup,
|
|
IN IPV4_ADDRESS ipInterface
|
|
)
|
|
/*++
|
|
Description:
|
|
Joins a group on a given interface.
|
|
Called by:
|
|
Locks:
|
|
None
|
|
--*/
|
|
{
|
|
struct ip_mreq imOption;
|
|
|
|
imOption.imr_multiaddr.s_addr = ipGroup;
|
|
imOption.imr_interface.s_addr = ipInterface;
|
|
|
|
return setsockopt( s,
|
|
IPPROTO_IP,
|
|
IP_ADD_MEMBERSHIP,
|
|
(PBYTE)&imOption,
|
|
sizeof(imOption));
|
|
}
|
|
|
|
DWORD
|
|
McSendPacketTo(
|
|
SOCKET s,
|
|
WSABUF *pWsabuf,
|
|
IPV4_ADDRESS dest
|
|
)
|
|
{
|
|
DWORD dwSent, dwRet;
|
|
int iSetIp = 1;
|
|
SOCKADDR_IN to;
|
|
|
|
// Set header include
|
|
|
|
setsockopt( s,
|
|
IPPROTO_IP,
|
|
IP_HDRINCL,
|
|
(char *) &iSetIp,
|
|
sizeof(int) );
|
|
|
|
// Send the packet
|
|
|
|
to.sin_family = AF_INET;
|
|
to.sin_port = 0;
|
|
to.sin_addr.s_addr = dest;
|
|
|
|
dwRet = WSASendTo( s,
|
|
pWsabuf,
|
|
1,
|
|
&dwSent,
|
|
0,
|
|
(const struct sockaddr FAR *)&to,
|
|
sizeof(to),
|
|
NULL, NULL );
|
|
|
|
// Clear header include
|
|
|
|
iSetIp = 0;
|
|
setsockopt( s,
|
|
IPPROTO_IP,
|
|
IP_HDRINCL,
|
|
(char *) &iSetIp,
|
|
sizeof(int) );
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
DWORD
|
|
ForwardMtraceRequest(
|
|
IPV4_ADDRESS dwForwardDest,
|
|
IPV4_ADDRESS dwForwardSrc,
|
|
PMTRACE_HEADER pMtraceMsg,
|
|
DWORD dwMessageLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pass an mtrace request to the next router upstream
|
|
|
|
Locks:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
SOCKADDR_IN saDestAddr;
|
|
INT iLength;
|
|
DWORD dwErr = NO_ERROR;
|
|
|
|
//
|
|
// Recalculate Checksum
|
|
//
|
|
|
|
pMtraceMsg->wChecksum = 0;
|
|
|
|
pMtraceMsg->wChecksum = Compute16BitXSum((PVOID)pMtraceMsg,
|
|
dwMessageLength);
|
|
|
|
if (dwForwardSrc && IN_MULTICAST(ntohl(dwForwardDest)))
|
|
{
|
|
dwErr = McSetMulticastIf( McMiscSocket, dwForwardSrc );
|
|
|
|
}
|
|
|
|
//
|
|
// Send it off
|
|
//
|
|
|
|
saDestAddr.sin_family = AF_INET;
|
|
saDestAddr.sin_port = 0;
|
|
saDestAddr.sin_addr.s_addr = dwForwardDest;
|
|
|
|
iLength = sendto(McMiscSocket,
|
|
(PBYTE)pMtraceMsg,
|
|
dwMessageLength,
|
|
0,
|
|
(PSOCKADDR) &saDestAddr,
|
|
sizeof(SOCKADDR_IN));
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
VOID
|
|
SendMtraceResponse(
|
|
IPV4_ADDRESS dwForwardDest,
|
|
IPV4_ADDRESS dwForwardSrc,
|
|
PMTRACE_HEADER pMtraceMsg,
|
|
DWORD dwMessageLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send a reply to the response address
|
|
|
|
Locks:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
SOCKADDR_IN saDestAddr;
|
|
INT iLength;
|
|
|
|
//
|
|
// Source Address can be any of our addresses, but should
|
|
// be one which is in the multicast routing table if that
|
|
// can be determined.
|
|
// XXX
|
|
//
|
|
|
|
//
|
|
// If the response address is multicast, use the TTL supplied in the header
|
|
//
|
|
|
|
if (IN_MULTICAST(ntohl(dwForwardDest)))
|
|
{
|
|
DWORD dwTtl, dwErr;
|
|
|
|
//
|
|
// Copy Response TTL from traceroute header into IP header
|
|
//
|
|
|
|
dwErr = McSetMulticastTtl( McMiscSocket, (DWORD)pMtraceMsg->byRespTtl );
|
|
}
|
|
|
|
//
|
|
// Change message type to response
|
|
//
|
|
|
|
pMtraceMsg->byType = IGMP_MTRACE_RESPONSE;
|
|
|
|
ForwardMtraceRequest(dwForwardDest,
|
|
dwForwardSrc,
|
|
pMtraceMsg,
|
|
dwMessageLength);
|
|
}
|
|
|
|
BYTE
|
|
MaskToMaskLen(
|
|
IPV4_ADDRESS dwMask
|
|
)
|
|
{
|
|
register int i;
|
|
|
|
dwMask = ntohl(dwMask);
|
|
|
|
for (i=0; i<32 && !(dwMask & (1<<i)); i++);
|
|
|
|
return 32-i;
|
|
}
|
|
|
|
//
|
|
// Test whether an interface is a p2p interface.
|
|
//
|
|
|
|
DWORD
|
|
IsPointToPoint(
|
|
PICB picb
|
|
)
|
|
{
|
|
// all tunnels are p2p
|
|
if (picb->ritType == ROUTER_IF_TYPE_TUNNEL1)
|
|
return 1;
|
|
|
|
// all unnumbered interfaces are p2p
|
|
if (! picb->dwNumAddresses)
|
|
return 1;
|
|
|
|
// a numbered interface with a /32 mask is p2p
|
|
if (picb->pibBindings[0].dwMask == 0xFFFFFFFF)
|
|
return 1;
|
|
|
|
// everything else isn't
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Look up route to S or G ***in the M-RIB***
|
|
// XXX We actually need to query the MGM to get the right route
|
|
// from the routing protocol. Since the MGM doesn't let us do
|
|
// this yet, we'll make a good guess for now. This will work for
|
|
// BGMP but not for PIM-SM (*,G) or CBT.
|
|
//
|
|
BOOL
|
|
McLookupRoute(
|
|
IN IPV4_ADDRESS ipAddress,
|
|
IN BOOL bSkipFirst,
|
|
OUT PBYTE pbySrcMaskLength,
|
|
OUT PIPV4_ADDRESS pipNextHopAddress,
|
|
OUT PDWORD pdwNextHopIfIndex,
|
|
OUT PDWORD pdwNextHopProtocol
|
|
)
|
|
#ifdef HAVE_RTMV2
|
|
{
|
|
RTM_DEST_INFO rdi, rdi2;
|
|
PRTM_ROUTE_INFO pri;
|
|
RTM_NEXTHOP_INFO nhi;
|
|
RTM_ENTITY_INFO rei;
|
|
RTM_NET_ADDRESS naAddress;
|
|
BOOL bRouteFound = FALSE;
|
|
DWORD dwErr;
|
|
|
|
pri = HeapAlloc(
|
|
IPRouterHeap,
|
|
0,
|
|
RTM_SIZE_OF_ROUTE_INFO(g_rtmProfile.MaxNextHopsInRoute)
|
|
);
|
|
|
|
if (pri == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
RTM_IPV4_MAKE_NET_ADDRESS(&naAddress, ipAddress, 32);
|
|
|
|
dwErr = RtmGetMostSpecificDestination( g_hLocalRoute,
|
|
&naAddress,
|
|
RTM_BEST_PROTOCOL,
|
|
RTM_VIEW_MASK_MCAST,
|
|
&rdi );
|
|
|
|
if (bSkipFirst)
|
|
{
|
|
dwErr = RtmGetLessSpecificDestination( g_hLocalRoute,
|
|
rdi.DestHandle,
|
|
RTM_BEST_PROTOCOL,
|
|
RTM_VIEW_MASK_MCAST,
|
|
&rdi2 );
|
|
|
|
RtmReleaseDestInfo( g_hLocalRoute, &rdi);
|
|
|
|
memcpy(&rdi, &rdi2, sizeof(rdi));
|
|
}
|
|
|
|
if (dwErr is NO_ERROR)
|
|
{
|
|
ASSERT( rdi.ViewInfo[0].ViewId is RTM_VIEW_ID_MCAST);
|
|
|
|
dwErr = RtmGetRouteInfo( g_hLocalRoute,
|
|
rdi.ViewInfo[0].Route,
|
|
pri,
|
|
NULL );
|
|
|
|
if (dwErr is NO_ERROR)
|
|
{
|
|
ULONG ulNHopIdx;
|
|
ULONG ulDummyLen;
|
|
|
|
bRouteFound = TRUE;
|
|
|
|
RtmGetEntityInfo( g_hLocalRoute,
|
|
pri->RouteOwner,
|
|
&rei );
|
|
|
|
// XXX Use 1st next hop for now. Should query MGM.
|
|
ulNHopIdx = 0;
|
|
|
|
if (RtmGetNextHopInfo( g_hLocalRoute,
|
|
pri->NextHopsList.NextHops[ulNHopIdx],
|
|
&nhi ) is NO_ERROR )
|
|
{
|
|
RTM_IPV4_GET_ADDR_AND_LEN( *pipNextHopAddress,
|
|
ulDummyLen,
|
|
&nhi.NextHopAddress );
|
|
*pbySrcMaskLength = (BYTE)rdi.DestAddress.NumBits;
|
|
*pdwNextHopIfIndex = nhi.InterfaceIndex;
|
|
*pdwNextHopProtocol= PROTO_FROM_PROTO_ID(
|
|
rei.EntityId.EntityProtocolId );
|
|
|
|
RtmReleaseNextHopInfo( g_hLocalRoute, &nhi );
|
|
}
|
|
|
|
RtmReleaseRouteInfo( g_hLocalRoute, pri );
|
|
}
|
|
|
|
if (g_mcastDebugLevel > 0)
|
|
{
|
|
Trace6(MCAST,
|
|
"%d.%d.%d.%d matched %d.%d.%d.%d/%x",
|
|
PRINT_IPADDR(ipAddress),
|
|
rdi.DestAddress.AddrBits[0],
|
|
rdi.DestAddress.AddrBits[1],
|
|
rdi.DestAddress.AddrBits[2],
|
|
rdi.DestAddress.AddrBits[3],
|
|
rdi.DestAddress.NumBits);
|
|
|
|
// XXX Get and show next hop
|
|
}
|
|
|
|
RtmReleaseDestInfo( g_hLocalRoute, &rdi);
|
|
}
|
|
|
|
HeapFree(IPRouterHeap, 0, pri);
|
|
|
|
return bRouteFound;
|
|
}
|
|
#else
|
|
{
|
|
// RTMV1 has no multicast RIB, and the unicast RIB may be wrong.
|
|
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
VOID
|
|
HandleMtraceRequest(
|
|
WSABUF *pWsabuf
|
|
)
|
|
/*++
|
|
Locks:
|
|
Assumes caller holds read lock on ICB list
|
|
--*/
|
|
{
|
|
DWORD dwSizeOfHeader, dwBlocks, dwOutBufferSize, dwSize;
|
|
DWORD dwProtocolGroup, dwResult, dwErr;
|
|
IPV4_ADDRESS dwForwardDest = 0;
|
|
BYTE byStatusCode = MFE_NO_ERROR;
|
|
BYTE byProtoStatusCode = MFE_NO_ERROR;
|
|
BYTE byProtocol;
|
|
PICB picbIif, picbOif;
|
|
IPV4_ADDRESS dwIifAddr, dwOifAddr;
|
|
WSABUF wsMtraceBuffer;
|
|
BOOL bRouteFound;
|
|
|
|
MIB_IPMCAST_MFE mimInMfe;
|
|
PPROTO_CB pOifOwner, pIifOwner;
|
|
|
|
PMTRACE_HEADER pMtraceMsg;
|
|
PMTRACE_RESPONSE_BLOCK pBlock;
|
|
PMIB_IPMCAST_MFE_STATS mfeStats;
|
|
|
|
PIP_HEADER pIpHeader = (PIP_HEADER)pWsabuf->buf;
|
|
|
|
//
|
|
// Route fields independent of which version of RTM we're using
|
|
//
|
|
|
|
BYTE bySrcMaskLength = 0;
|
|
IPV4_ADDRESS ipNextHopAddress = 0;
|
|
DWORD dwNextHopIfIndex = 0;
|
|
DWORD dwNextHopProtocol= 0;
|
|
|
|
dwSizeOfHeader = ((pIpHeader->byVerLen)&0x0f)<<2;
|
|
|
|
pMtraceMsg = (PMTRACE_HEADER)(((PBYTE)pIpHeader) + dwSizeOfHeader);
|
|
|
|
dwBlocks = (ntohs(pIpHeader->wLength) - dwSizeOfHeader
|
|
- sizeof(MTRACE_HEADER)) / sizeof(MTRACE_RESPONSE_BLOCK);
|
|
|
|
//
|
|
// If Query (no response blocks) received via routeralert and we're
|
|
// not lasthop router, then silently drop it.
|
|
//
|
|
|
|
if (!dwBlocks)
|
|
{
|
|
BOOL isLastHop;
|
|
|
|
//
|
|
// Check whether we're the last-hop router by seeing if we
|
|
// have a multicast-capable interface on the same subnet as
|
|
// the destination address, and we are the router that would
|
|
// forward traffic from the given source onto the oif.
|
|
//
|
|
|
|
dwResult = FindBindingWithRemoteAddress(&picbOif,
|
|
&dwOifAddr,
|
|
pMtraceMsg->dwDestAddress);
|
|
|
|
isLastHop = (dwResult == NO_ERROR);
|
|
|
|
if (!isLastHop)
|
|
{
|
|
// If multicast, or if unicast but not to us, reinject
|
|
|
|
if (IN_MULTICAST(ntohl(pIpHeader->dwDest))
|
|
|| !McIsMyAddress(pMtraceMsg->dwDestAddress))
|
|
{
|
|
Trace1(MCAST, "Mtrace: reinjecting packet to %d.%d.%d.%d",
|
|
PRINT_IPADDR(pIpHeader->dwDest));
|
|
|
|
McSendPacketTo( McMiscSocket,
|
|
pWsabuf,
|
|
pMtraceMsg->dwDestAddress);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Ok, this was received via unicast to us, and we want to
|
|
// trace starting from this router, but we don't
|
|
// know what oif would be used, so we need to put
|
|
// 0 in the message.
|
|
//
|
|
|
|
picbOif = NULL;
|
|
dwOifAddr = 0;
|
|
|
|
//
|
|
// note error code of 0x06
|
|
//
|
|
|
|
byStatusCode = MFE_NOT_LAST_HOP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If Request (response blocks exist) received via non-link-local
|
|
// multicast, drop it.
|
|
//
|
|
|
|
if (IN_MULTICAST(ntohl(pIpHeader->dwDest)) &&
|
|
((pIpHeader->dwDest & LOCAL_NET_MULTICAST_MASK) != LOCAL_NET_MULTICAST))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Match interface on which request arrived
|
|
//
|
|
|
|
dwResult = FindBindingForPacket(pIpHeader,
|
|
&picbOif,
|
|
&dwOifAddr);
|
|
|
|
if(dwResult != NO_ERROR)
|
|
{
|
|
//
|
|
// Drop it if we couldn't find the interface.
|
|
// Since it was received via link-local multicast,
|
|
// this should never happen.
|
|
//
|
|
|
|
if (g_mcastDebugLevel > 0)
|
|
{
|
|
Trace0(MCAST, "Mtrace: no matching interface");
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 1) Insert a new response block into the packet and fill in the
|
|
// Query Arrival Time, Outgoing Interface Address, Output
|
|
// Packet Count, and FwdTTL.
|
|
// if (XXX can insert)
|
|
//
|
|
|
|
{
|
|
dwSize = sizeof(MTRACE_HEADER) + dwBlocks*sizeof(MTRACE_RESPONSE_BLOCK);
|
|
wsMtraceBuffer.len = dwSize + sizeof(MTRACE_RESPONSE_BLOCK);
|
|
wsMtraceBuffer.buf = HeapAlloc(IPRouterHeap, 0, wsMtraceBuffer.len);
|
|
|
|
if (wsMtraceBuffer.buf == NULL)
|
|
{
|
|
Trace0( MCAST, "Couldn't allocate memory for mtrace response" );
|
|
return;
|
|
}
|
|
|
|
CopyMemory(wsMtraceBuffer.buf, pMtraceMsg, dwSize);
|
|
pBlock = (PMTRACE_RESPONSE_BLOCK)(((PBYTE)wsMtraceBuffer.buf) + dwSize);
|
|
dwBlocks++;
|
|
ZeroMemory(pBlock, sizeof(MTRACE_RESPONSE_BLOCK));
|
|
|
|
pBlock->dwQueryArrivalTime = GetCurrentNTP32Time();
|
|
pBlock->dwOifAddr = dwOifAddr;
|
|
if (picbOif) {
|
|
IP_MCAST_COUNTER_INFO oifStats;
|
|
GetInterfaceMcastCounters(picbOif, &oifStats);
|
|
pBlock->dwOifPacketCount = htonl((ULONG)oifStats.OutMcastPkts);
|
|
|
|
if (g_mcastDebugLevel > 0)
|
|
Trace1(MCAST, "dwOifPacketCount = %d", oifStats.OutMcastPkts);
|
|
|
|
pBlock->byOifThreshold = (BYTE)picbOif->dwMcastTtl;
|
|
} else {
|
|
pBlock->dwOifPacketCount = 0;
|
|
pBlock->byOifThreshold = 0;
|
|
}
|
|
}
|
|
// else {
|
|
// byStatusCode = MFE_NO_SPACE;
|
|
// }
|
|
|
|
//
|
|
// 2) Attempt to determine the forwarding information for the
|
|
// source and group specified, using the same mechanisms as
|
|
// would be used when a packet is received from the source
|
|
// destined for the group. (State need not be initiated.)
|
|
//
|
|
|
|
ZeroMemory( &mimInMfe, sizeof(mimInMfe) );
|
|
|
|
mimInMfe.dwGroup = pMtraceMsg->dwGroupAddress;
|
|
mimInMfe.dwSource = pMtraceMsg->dwSourceAddress;
|
|
mimInMfe.dwSrcMask = 0xFFFFFFFF;
|
|
|
|
dwOutBufferSize = 0;
|
|
|
|
dwResult = MgmGetMfeStats(
|
|
&mimInMfe, &dwOutBufferSize, (PBYTE)NULL,
|
|
MGM_MFE_STATS_0
|
|
);
|
|
|
|
if (dwResult isnot NO_ERROR)
|
|
{
|
|
mfeStats = NULL;
|
|
}
|
|
else
|
|
{
|
|
mfeStats = HeapAlloc(IPRouterHeap,
|
|
0,
|
|
dwOutBufferSize);
|
|
|
|
dwResult = MgmGetMfeStats(
|
|
&mimInMfe,
|
|
&dwOutBufferSize,
|
|
(PBYTE)mfeStats,
|
|
MGM_MFE_STATS_0
|
|
);
|
|
|
|
if (dwResult isnot NO_ERROR)
|
|
{
|
|
HeapFree(IPRouterHeap,
|
|
0,
|
|
mfeStats);
|
|
|
|
mfeStats = NULL;
|
|
}
|
|
}
|
|
|
|
if (mfeStats)
|
|
{
|
|
//
|
|
// MFE was found...
|
|
//
|
|
|
|
dwNextHopProtocol = mfeStats->dwRouteProtocol;
|
|
dwNextHopIfIndex = mfeStats->dwInIfIndex;
|
|
ipNextHopAddress = mfeStats->dwUpStrmNgbr;
|
|
bySrcMaskLength = MaskToMaskLen(mfeStats->dwRouteMask);
|
|
|
|
bRouteFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bRouteFound = FALSE;
|
|
|
|
if (pMtraceMsg->dwSourceAddress == 0xFFFFFFFF)
|
|
{
|
|
//
|
|
// G route
|
|
//
|
|
|
|
bRouteFound = McLookupRoute( pMtraceMsg->dwGroupAddress,
|
|
FALSE,
|
|
& bySrcMaskLength,
|
|
& ipNextHopAddress,
|
|
& dwNextHopIfIndex,
|
|
& dwNextHopProtocol );
|
|
|
|
if (ipNextHopAddress is IP_LOOPBACK_ADDRESS)
|
|
{
|
|
// It's one of our addresses, so switch to the interface
|
|
// route instead of the loopback one.
|
|
|
|
bRouteFound = McLookupRoute( pMtraceMsg->dwGroupAddress,
|
|
TRUE,
|
|
& bySrcMaskLength,
|
|
& ipNextHopAddress,
|
|
& dwNextHopIfIndex,
|
|
& dwNextHopProtocol );
|
|
}
|
|
|
|
bySrcMaskLength = 0; // force source mask length to 0
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// S route
|
|
//
|
|
|
|
bRouteFound = McLookupRoute( pMtraceMsg->dwSourceAddress,
|
|
FALSE,
|
|
& bySrcMaskLength,
|
|
& ipNextHopAddress,
|
|
& dwNextHopIfIndex,
|
|
& dwNextHopProtocol );
|
|
|
|
if (ipNextHopAddress is IP_LOOPBACK_ADDRESS)
|
|
{
|
|
// It's one of our addresses, so switch to the interface
|
|
// route instead of the loopback one.
|
|
|
|
bRouteFound = McLookupRoute( pMtraceMsg->dwSourceAddress,
|
|
TRUE,
|
|
& bySrcMaskLength,
|
|
& ipNextHopAddress,
|
|
& dwNextHopIfIndex,
|
|
& dwNextHopProtocol );
|
|
}
|
|
}
|
|
}
|
|
|
|
picbIif = (dwNextHopIfIndex)? InterfaceLookupByIfIndex(dwNextHopIfIndex) : 0;
|
|
dwIifAddr = (picbIif)? defaultSourceAddress(picbIif) : 0;
|
|
|
|
// If the source is directly-connected, make sure the next hop
|
|
// address is equal to the source. Later on below, we'll set the
|
|
// forward destination to the response address
|
|
|
|
if (picbIif
|
|
&& (pMtraceMsg->dwSourceAddress isnot 0xFFFFFFFF)
|
|
&& IsConnectedTo(picbIif, pMtraceMsg->dwSourceAddress, NULL, NULL))
|
|
{
|
|
ipNextHopAddress = pMtraceMsg->dwSourceAddress;
|
|
}
|
|
|
|
//
|
|
// New Rule: if received via link-local multicast, then silently
|
|
// drop requests if we know we're not the forwarder
|
|
//
|
|
|
|
if ((pIpHeader->dwDest & LOCAL_NET_MULTICAST_MASK) == LOCAL_NET_MULTICAST)
|
|
|
|
{
|
|
// If we don't have a route to another iface, we're not forwarder
|
|
if (!picbIif || picbIif==picbOif)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Special case: if we matched a host route pointing back to us,
|
|
// then we've actually reached the source.
|
|
//
|
|
|
|
if (dwIifAddr == IP_LOOPBACK_ADDRESS)
|
|
{
|
|
dwIifAddr = pMtraceMsg->dwSourceAddress;
|
|
}
|
|
|
|
//
|
|
// Initialize all fields
|
|
// spec doesn't say what value to use as "other"
|
|
//
|
|
|
|
byProtocol = 0;
|
|
dwProtocolGroup = ALL_ROUTERS_MULTICAST_GROUP;
|
|
|
|
//
|
|
// 3) If no forwarding information can be determined, set error
|
|
// to MFE_NO_ROUTE, zero remaining fields, and forward to
|
|
// requester.
|
|
//
|
|
|
|
if (!picbIif)
|
|
{
|
|
if (byStatusCode < MFE_NO_ROUTE)
|
|
{
|
|
byStatusCode = MFE_NO_ROUTE;
|
|
}
|
|
|
|
dwForwardDest = pMtraceMsg->dwResponseAddress;
|
|
|
|
pIifOwner = NULL;
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Calculate Mtrace protocol ID and next hop group address
|
|
// (Yes, the protocol ID field in the spec really is one big
|
|
// hairy mess)
|
|
//
|
|
|
|
dwResult = MulticastOwner(picbIif,
|
|
&pIifOwner,
|
|
NULL);
|
|
|
|
if(pIifOwner)
|
|
{
|
|
switch(PROTO_FROM_PROTO_ID(pIifOwner->dwProtocolId))
|
|
{
|
|
//
|
|
// Fill this in for every new protocol added.
|
|
//
|
|
// We'll be nice and fill in code for protocols which aren't
|
|
// implemented yet.
|
|
//
|
|
|
|
#if defined(PROTO_IP_DVMRP) && defined(ALL_DVMRP_ROUTERS_MULTICAST_GROUP)
|
|
case PROTO_IP_DVMRP:
|
|
{
|
|
if (rir.RR_RoutingProtocol is PROTO_IP_LOCAL)
|
|
{
|
|
//
|
|
// Static route
|
|
//
|
|
|
|
byProtocol = 7;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Non-static route
|
|
//
|
|
|
|
byProtocol = 1;
|
|
}
|
|
|
|
dwProtocolGroup = ALL_DVMRP_ROUTERS_MULTICAST_GROUP;
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
#if defined(PROTO_IP_MOSPF) && defined(ALL_MOSPF_ROUTERS_MULTICAST_GROUP)
|
|
case PROTO_IP_MOSPF:
|
|
{
|
|
byProtocol = 2;
|
|
|
|
dwProtocolGroup = ALL_MOSPF_ROUTERS_MULTICAST_GROUP;
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
#if defined(PROTO_IP_PIM) && defined(ALL_PIM_ROUTERS_MULTICAST_GROUP)
|
|
case PROTO_IP_PIM:
|
|
{
|
|
if (rir.RR_RoutingProtocol is PROTO_IP_LOCAL)
|
|
{
|
|
//
|
|
// Static route
|
|
//
|
|
|
|
byProtocol = 6;
|
|
}
|
|
else
|
|
{
|
|
if (0)
|
|
{
|
|
//
|
|
// XXX Non-static, M-RIB route!=U-RIB route
|
|
//
|
|
|
|
byProtocol = 5;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Non-static, PIM over M-RIB==U-RIB
|
|
//
|
|
|
|
byProtocol = 3;
|
|
}
|
|
}
|
|
|
|
dwProtocolGroup = ALL_PIM_ROUTERS_MULTICAST_GROUP;
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
#if defined(PROTO_IP_CBT) && defined(ALL_CBT_ROUTERS_MULTICAST_GROUP)
|
|
case PROTO_IP_CBT:
|
|
{
|
|
byProtocol = 4;
|
|
|
|
dwProtocolGroup = ALL_CBT_ROUTERS_MULTICAST_GROUP;
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// 4) Fill in more information
|
|
//
|
|
|
|
//
|
|
// Incoming Interface Address
|
|
//
|
|
|
|
pBlock->dwIifAddr = dwIifAddr;
|
|
|
|
if (mfeStats)
|
|
{
|
|
//
|
|
// Figure out Previous-Hop Router Address
|
|
//
|
|
|
|
dwForwardDest = mfeStats->dwUpStrmNgbr;
|
|
}
|
|
else
|
|
{
|
|
if ( IsPointToPoint(picbIif) && picbIif->dwRemoteAddress )
|
|
{
|
|
dwForwardDest = picbIif->dwRemoteAddress;
|
|
}
|
|
else if (bRouteFound && ipNextHopAddress)
|
|
{
|
|
dwForwardDest = ipNextHopAddress;
|
|
}
|
|
else
|
|
{
|
|
dwForwardDest = 0;
|
|
}
|
|
}
|
|
|
|
pBlock->dwPrevHopAddr = dwForwardDest;
|
|
|
|
// Okay, if the previous hop address is the source,
|
|
// set the forward destination to the response address
|
|
|
|
if (dwForwardDest is pMtraceMsg->dwSourceAddress)
|
|
{
|
|
ipNextHopAddress = 0;
|
|
dwForwardDest = pMtraceMsg->dwResponseAddress;
|
|
}
|
|
|
|
if (picbIif)
|
|
{
|
|
IP_MCAST_COUNTER_INFO iifStats;
|
|
|
|
GetInterfaceMcastCounters(picbIif, &iifStats);
|
|
|
|
pBlock->dwIifPacketCount = htonl((ULONG)iifStats.InMcastPkts);
|
|
}
|
|
else
|
|
{
|
|
pBlock->dwIifPacketCount = 0;
|
|
}
|
|
|
|
//
|
|
// Total Number of Packets
|
|
//
|
|
|
|
pBlock->dwSGPacketCount = (mfeStats)? htonl(mfeStats->ulInPkts) : 0;
|
|
pBlock->byIifProtocol = byProtocol; // Routing Protocol
|
|
|
|
//
|
|
// length of source mask for S route
|
|
//
|
|
|
|
if (bRouteFound)
|
|
{
|
|
pBlock->bySrcMaskLength = bySrcMaskLength;
|
|
}
|
|
else
|
|
{
|
|
pBlock->bySrcMaskLength = 0;
|
|
}
|
|
|
|
#if 0
|
|
if (XXX starG or better forwarding state)
|
|
{
|
|
pBlock->bySrcMaskLength = 63; // Smask from forwarding info
|
|
}
|
|
|
|
//
|
|
// Set S bit (64) if packet counts aren't (S,G)-specific
|
|
//
|
|
|
|
if (XXX)
|
|
{
|
|
pBlock->bySrcMaskLength |= 64;
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// 5) Check if traceroute is administratively prohibited, or if
|
|
// previous hop router doesn't understand traceroute. If so,
|
|
// forward to requester.
|
|
//
|
|
|
|
#if 0
|
|
if (XXX) {
|
|
|
|
if (byStatusCode < MFE_PROHIBITED)
|
|
{
|
|
byStatusCode = MFE_PROHIBITED;
|
|
}
|
|
|
|
dwForwardDest = pMtraceMsg->dwResponseAddress;
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Check for MFE_OLD_ROUTER - set by routing protocol
|
|
//
|
|
// 6) If reception iface is non-multicast or iif, set appropriate error.
|
|
//
|
|
|
|
if (picbOif)
|
|
{
|
|
dwResult = MulticastOwner(picbOif,
|
|
&pOifOwner,
|
|
NULL);
|
|
|
|
if (pOifOwner == NULL)
|
|
{
|
|
if (byStatusCode < MFE_NO_MULTICAST)
|
|
{
|
|
byStatusCode = MFE_NO_MULTICAST;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (picbOif == picbIif)
|
|
{
|
|
if (byStatusCode < MFE_IIF)
|
|
{
|
|
byStatusCode = MFE_IIF;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pOifOwner = NULL;
|
|
}
|
|
|
|
//
|
|
// Check for MFE_WRONG_IF - set by routing protocol
|
|
//
|
|
// 7) Check for admin scoping on either iif or oif.
|
|
//
|
|
|
|
if ((picbIif
|
|
&& RmHasBoundary(picbIif->dwIfIndex, pMtraceMsg->dwGroupAddress))
|
|
|| (picbOif
|
|
&& RmHasBoundary(picbOif->dwIfIndex, pMtraceMsg->dwGroupAddress)))
|
|
{
|
|
if (byStatusCode < MFE_BOUNDARY_REACHED)
|
|
{
|
|
byStatusCode = MFE_BOUNDARY_REACHED;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// 8) Check for MFE_REACHED_CORE - set by routing protocol
|
|
// 9) Check for MFE_PRUNED_UPSTREAM - set by routing protocol
|
|
// Check for MFE_OIF_PRUNED - set by routing protocol
|
|
// Check for MFE_NOT_FORWARDING:
|
|
// Search for picbOif->(index) and picbOifAddr in oiflist
|
|
//
|
|
|
|
if (mfeStats && picbOif)
|
|
{
|
|
DWORD oifIndex;
|
|
|
|
for (oifIndex=0;
|
|
oifIndex < mfeStats->ulNumOutIf;
|
|
oifIndex++)
|
|
{
|
|
if (picbOif->dwIfIndex==mfeStats->rgmiosOutStats[oifIndex].dwOutIfIndex
|
|
&& dwOifAddr == mfeStats->rgmiosOutStats[oifIndex].dwNextHopAddr)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (oifIndex >= mfeStats->ulNumOutIf)
|
|
{
|
|
if (byStatusCode < MFE_NOT_FORWARDING)
|
|
{
|
|
byStatusCode = MFE_NOT_FORWARDING;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// status code to add is highest value of what iif owner, oif owner,
|
|
// and rtrmgr say.
|
|
//
|
|
|
|
if (pOifOwner && pOifOwner->pfnGetMfeStatus)
|
|
{
|
|
dwResult = (pOifOwner->pfnGetMfeStatus)(picbOif->dwIfIndex,
|
|
pMtraceMsg->dwGroupAddress,
|
|
pMtraceMsg->dwSourceAddress,
|
|
&byProtoStatusCode);
|
|
|
|
if (byStatusCode < byProtoStatusCode)
|
|
{
|
|
byStatusCode = byProtoStatusCode;
|
|
}
|
|
}
|
|
|
|
if (pIifOwner && pIifOwner->pfnGetMfeStatus)
|
|
{
|
|
dwResult = (pIifOwner->pfnGetMfeStatus)(picbIif->dwIfIndex,
|
|
pMtraceMsg->dwGroupAddress,
|
|
pMtraceMsg->dwSourceAddress,
|
|
&byProtoStatusCode);
|
|
|
|
if (byStatusCode < byProtoStatusCode)
|
|
{
|
|
byStatusCode = byProtoStatusCode;
|
|
}
|
|
}
|
|
|
|
pBlock->byStatusCode = (char)mtraceErrCode[byStatusCode];
|
|
|
|
Trace5( MCAST,
|
|
"Mtrace: err %d blks %d maxhops %d iif %d prevhop %d.%d.%d.%d",
|
|
pBlock->byStatusCode,
|
|
dwBlocks,
|
|
pMtraceMsg->byHops,
|
|
((picbIif)? picbIif->dwIfIndex : 0),
|
|
PRINT_IPADDR(pBlock->dwPrevHopAddr));
|
|
|
|
//
|
|
// 10) Send packet on to previous hop or to requester.
|
|
// If prev hop is not known, but iif is known, use a multicast group.
|
|
//
|
|
|
|
if (dwBlocks == pMtraceMsg->byHops)
|
|
{
|
|
dwForwardDest = pMtraceMsg->dwResponseAddress;
|
|
|
|
}
|
|
else
|
|
{
|
|
if (!dwForwardDest)
|
|
{
|
|
if (picbIif)
|
|
{
|
|
pBlock->dwPrevHopAddr = dwForwardDest = dwProtocolGroup;
|
|
|
|
}
|
|
else
|
|
{
|
|
dwForwardDest = pMtraceMsg->dwResponseAddress;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_mcastDebugLevel > 0) {
|
|
Trace1(MCAST, " QueryArrivalTime = %08x", pBlock->dwQueryArrivalTime);
|
|
Trace2(MCAST, " IifAddr = %08x (%d.%d.%d.%d)", pBlock->dwIifAddr,
|
|
PRINT_IPADDR(pBlock->dwIifAddr));
|
|
Trace2(MCAST, " OifAddr = %08x (%d.%d.%d.%d)", pBlock->dwOifAddr,
|
|
PRINT_IPADDR(pBlock->dwOifAddr));
|
|
Trace2(MCAST, " PrevHopAddr = %08x (%d.%d.%d.%d)", pBlock->dwPrevHopAddr,
|
|
PRINT_IPADDR(pBlock->dwPrevHopAddr));
|
|
Trace1(MCAST, " IifPacketCount = %08x", pBlock->dwIifPacketCount );
|
|
Trace1(MCAST, " OifPacketCount = %08x", pBlock->dwOifPacketCount );
|
|
Trace1(MCAST, " SGPacketCount = %08x", pBlock->dwSGPacketCount );
|
|
Trace1(MCAST, " IifProtocol = %02x", pBlock->byIifProtocol );
|
|
Trace1(MCAST, " OifThreshold = %02x", pBlock->byOifThreshold );
|
|
Trace1(MCAST, " SrcMaskLength = %02x", pBlock->bySrcMaskLength );
|
|
Trace1(MCAST, " StatusCode = %02x", pBlock->byStatusCode );
|
|
}
|
|
|
|
if (dwForwardDest is pMtraceMsg->dwResponseAddress)
|
|
{
|
|
Trace2(MCAST,
|
|
"Sending mtrace response to %d.%d.%d.%d from %d.%d.%d.%d",
|
|
PRINT_IPADDR(dwForwardDest),
|
|
PRINT_IPADDR(dwOifAddr));
|
|
|
|
SendMtraceResponse(dwForwardDest,
|
|
dwOifAddr,
|
|
(PMTRACE_HEADER)wsMtraceBuffer.buf,
|
|
dwSize + sizeof(MTRACE_RESPONSE_BLOCK));
|
|
|
|
}
|
|
else
|
|
{
|
|
Trace2(MCAST,
|
|
"Forwarding mtrace request to %d.%d.%d.%d from %d.%d.%d.%d",
|
|
PRINT_IPADDR(dwForwardDest),
|
|
PRINT_IPADDR(dwIifAddr));
|
|
|
|
ForwardMtraceRequest(dwForwardDest,
|
|
dwIifAddr,
|
|
(PMTRACE_HEADER)wsMtraceBuffer.buf,
|
|
dwSize + sizeof(MTRACE_RESPONSE_BLOCK));
|
|
}
|
|
|
|
//
|
|
// Free the buffers
|
|
//
|
|
|
|
if (mfeStats)
|
|
{
|
|
HeapFree(IPRouterHeap,
|
|
0,
|
|
mfeStats);
|
|
}
|
|
|
|
HeapFree(IPRouterHeap,
|
|
0,
|
|
wsMtraceBuffer.buf);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Functions to deal with RAS Server advertisements
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static BOOL g_bRasAdvEnabled = FALSE;
|
|
|
|
DWORD
|
|
SetRasAdvEnable(
|
|
BOOL bEnabled
|
|
)
|
|
{
|
|
LARGE_INTEGER liExpiryTime;
|
|
DWORD dwErr = NO_ERROR;
|
|
|
|
if (bEnabled == g_bRasAdvEnabled)
|
|
return dwErr;
|
|
|
|
g_bRasAdvEnabled = bEnabled;
|
|
|
|
if (bEnabled)
|
|
{
|
|
//
|
|
// create input socket
|
|
//
|
|
|
|
g_UDPMiscSocket = WSASocket(AF_INET,
|
|
SOCK_DGRAM,
|
|
0,
|
|
NULL,
|
|
0,
|
|
0);
|
|
|
|
// Start timer
|
|
liExpiryTime = RtlConvertUlongToLargeInteger(RASADV_STARTUP_DELAY);
|
|
if (!SetWaitableTimer( g_hRasAdvTimer,
|
|
&liExpiryTime,
|
|
RASADV_PERIOD,
|
|
NULL,
|
|
NULL,
|
|
FALSE))
|
|
{
|
|
dwErr = GetLastError();
|
|
|
|
Trace1(ERR,
|
|
"SetRasAdvEnable: Error %d setting waitable timer",
|
|
dwErr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Stop timer
|
|
dwErr = CancelWaitableTimer( g_hRasAdvTimer );
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
VOID
|
|
HandleRasAdvTimer()
|
|
{
|
|
BYTE bHostName[MAX_HOSTNAME_LEN];
|
|
BYTE bMessage[MAX_HOSTNAME_LEN + 80], *p;
|
|
SOCKADDR_IN sinAddr, srcAddr;
|
|
PICB picb = NULL;
|
|
PLIST_ENTRY pleNode;
|
|
DWORD dwErr;
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pGlobalDomainInfo = NULL;
|
|
|
|
if (!g_bRasAdvEnabled)
|
|
return;
|
|
|
|
// Compose message
|
|
gethostname(bHostName, sizeof(bHostName));
|
|
sprintf(bMessage, "Hostname=%s\n", bHostName);
|
|
p = bMessage + strlen(bMessage);
|
|
|
|
// Get the name of the domain this machine is a member of
|
|
dwErr = DsRoleGetPrimaryDomainInformation(
|
|
NULL,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
(LPBYTE *) &pGlobalDomainInfo );
|
|
|
|
if ((dwErr is NO_ERROR) and
|
|
(pGlobalDomainInfo->DomainNameDns isnot NULL))
|
|
{
|
|
char *pType;
|
|
char buff[257];
|
|
|
|
WideCharToMultiByte( CP_ACP,
|
|
0,
|
|
pGlobalDomainInfo->DomainNameDns,
|
|
wcslen(pGlobalDomainInfo->DomainNameDns)+1,
|
|
buff,
|
|
sizeof(buff),
|
|
NULL,
|
|
NULL );
|
|
|
|
if (pGlobalDomainInfo->MachineRole is DsRole_RoleStandaloneWorkstation
|
|
or pGlobalDomainInfo->MachineRole is DsRole_RoleStandaloneServer)
|
|
pType = "Workgroup";
|
|
else
|
|
pType = "Domain";
|
|
|
|
sprintf(p, "%s=%s\n", pType, buff);
|
|
|
|
// Trace1(MCAST, "Sending !%s!", bMessage);
|
|
}
|
|
|
|
sinAddr.sin_family = AF_INET;
|
|
sinAddr.sin_port = htons(RASADV_PORT);
|
|
sinAddr.sin_addr.s_addr = inet_addr(RASADV_GROUP);
|
|
|
|
dwErr = McSetMulticastTtl( g_UDPMiscSocket, RASADV_TTL );
|
|
|
|
// Find a dedicated interface (if any)
|
|
ENTER_READER(ICB_LIST);
|
|
{
|
|
for (pleNode = ICBList.Flink;
|
|
pleNode isnot &ICBList;
|
|
pleNode = pleNode->Flink)
|
|
{
|
|
DWORD dwIndex;
|
|
|
|
picb = CONTAINING_RECORD(pleNode,
|
|
ICB,
|
|
leIfLink);
|
|
|
|
if (! picb->bBound)
|
|
continue;
|
|
|
|
if (picb->ritType == ROUTER_IF_TYPE_DEDICATED)
|
|
{
|
|
dwErr = McSetMulticastIfByIndex( g_UDPMiscSocket,
|
|
SOCK_DGRAM,
|
|
picb->dwIfIndex );
|
|
|
|
// Send a Ras Adv message
|
|
|
|
sendto(g_UDPMiscSocket, bMessage, strlen(bMessage)+1, 0,
|
|
(struct sockaddr *)&sinAddr, sizeof(sinAddr));
|
|
|
|
// If multicast forwarding is enabled, then
|
|
// a single send will get forwarded out all
|
|
// interfaces, so we can stop after the first send
|
|
|
|
if (McMiscSocket != INVALID_SOCKET)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
EXIT_LOCK(ICB_LIST);
|
|
}
|