/*++ 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 #include #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 { #ifdef KSL_IPINIP 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; } #endif //KSL_IPINIP // 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--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; dwIndexdwNumAddresses && !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; dwIndexdwNumAddresses && !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; #ifdef KSL_IPINIP // // 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; } #endif //KSL_IPINIP 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<ritType == ROUTER_IF_TYPE_TUNNEL1) return 1; #endif //KSL_IPINIP // 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 + (DNS_MAX_NAME_LENGTH + 2) + 128]; // // bMessage is required to hold hostname, the DomanNameDns, and the extra // 128 bytes are kept to store the other misc text that is copied to the // message // BYTE *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)) { int rc; char *pType; char buff[DNS_MAX_NAME_LENGTH+2]; rc = 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"; if ( rc ) { 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); }