|
|
/*++
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); }
|