windows-nt/Source/XPSP1/NT/net/rras/ip/rtrmgr/mcmisc.c
2020-09-26 16:20:57 +08:00

2791 lines
70 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
mcmisc.c
Abstract:
This module implements routines associated with mrinfo and mtrace
functionality.
Author:
dthaler@microsoft.com 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);
}