/*++ Copyright (c) 1999, Microsoft Corporation Module Name: net\routing\netsh\ip\protocols\msdpopt.c Abstract: MSDP command options implementation. This module contains handlers for the configuration commands supported by the MSDP Protocol. Author: Dave Thaler (dthaler) 21-May-1999 Revision History: --*/ #include "precomp.h" #pragma hdrstop #include #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) #define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) // // Forward declarations // ULONG QueryTagArray( IN PTCHAR ppwszArgumentArray[], IN ULONG ululArgumentCount, IN TAG_TYPE pttTagTypeArray[], IN ULONG ulTagTypeCount, OUT PULONG* ppulTagArray ); ULONG ValidateTagTypeArray( IN TAG_TYPE pttTagTypeArray[], IN ULONG ulTagTypeCount ); DWORD HandleMsdpAddPeer( PWCHAR pwszMachineName, PTCHAR* ArgumentArray, DWORD ulArgumentIndex, DWORD ulArgumentCount, DWORD dwFlags, PVOID pvData, BOOL* CommandDone ) { ULONG ulArgumentsLeft; ULONG BitVector; DWORD dwErr; ULONG ulErrorIndex = 0; ULONG i; PULONG pulTagArray; ULONG InfoSize; DWORD dwBufferSize = MAX_INTERFACE_NAME_LEN + 1; WCHAR wszInterfaceName[MAX_INTERFACE_NAME_LEN + 1]; TAG_TYPE TagTypeArray[] = { { TOKEN_OPT_NAME, NS_REQ_PRESENT, FALSE }, { TOKEN_OPT_REMADDR, NS_REQ_PRESENT, FALSE }, { TOKEN_OPT_LOCALADDR, NS_REQ_PRESENT, FALSE }, { TOKEN_OPT_KEEPALIVE, NS_REQ_ZERO, FALSE }, { TOKEN_OPT_CONNECTRETRY, NS_REQ_ZERO, FALSE }, { TOKEN_OPT_CACHING, NS_REQ_ZERO, FALSE }, { TOKEN_OPT_DEFAULTPEER, NS_REQ_ZERO, FALSE }, { TOKEN_OPT_ENCAPSMETHOD, NS_REQ_ZERO, FALSE }, }; PMSDP_IPV4_PEER_CONFIG pPeer = NULL; VERIFY_INSTALLED(MS_IP_MSDP, L"MSDP"); if (ulArgumentIndex >= ulArgumentCount) { return ERROR_SHOW_USAGE; } ulArgumentsLeft = ulArgumentCount - ulArgumentIndex; // // We convert the optional tags into an array of 'TagTypeArray' indices // which guide is in our scanning of the argument list. // Since the tags are optional, this process may result in no tags at all, // in which case we assume that arguments are specified in exactly the order // given in 'TagTypeArray' above. // dwErr = QueryTagArray( &ArgumentArray[ulArgumentIndex], ulArgumentsLeft, TagTypeArray, NUM_TAGS_IN_TABLE(TagTypeArray), &pulTagArray ); if (dwErr) { return dwErr; } BitVector = 0; do { dwErr = MakeMsdpIPv4PeerConfig(&pPeer); if (dwErr isnot NO_ERROR) { break; } // // We now scan the argument list, converting the arguments // into information in our 'VrouterGiven' structure. // for (i = 0; i < ulArgumentsLeft; i++) { switch(pulTagArray ? pulTagArray[i] : i) { case 0: { // name wcscpy(wszInterfaceName, ArgumentArray[i+ulArgumentIndex]); break; } case 1: { // remaddr pPeer->ipRemoteAddress = GetIpAddress( ArgumentArray[i + ulArgumentIndex]); break; } case 2: { // localaddr pPeer->ipLocalAddress = GetIpAddress( ArgumentArray[i + ulArgumentIndex]); break; } case 3: { // keepalive if (!MatchToken(ArgumentArray[i+ulArgumentIndex], TOKEN_OPT_VALUE_DEFAULT)) { pPeer->ulKeepAlive = _tcstoul( ArgumentArray[i + ulArgumentIndex], NULL, 10); pPeer->dwConfigFlags |= MSDP_PEER_CONFIG_KEEPALIVE; } break; } case 4: { // connectretry if (!MatchToken(ArgumentArray[i+ulArgumentIndex], TOKEN_OPT_VALUE_DEFAULT)) { pPeer->ulConnectRetry = _tcstoul( ArgumentArray[i + ulArgumentIndex], NULL, 10); pPeer->dwConfigFlags |= MSDP_PEER_CONFIG_CONNECTRETRY; } break; } case 5: { // caching DWORD dwValue; TOKEN_VALUE TokenArray[] = { { TOKEN_OPT_VALUE_NO, FALSE }, { TOKEN_OPT_VALUE_YES, TRUE } }; pPeer->dwConfigFlags &= ~MSDP_PEER_CONFIG_CACHING; dwErr = MatchEnumTag( g_hModule, ArgumentArray[i + ulArgumentIndex], NUM_TOKENS_IN_TABLE(TokenArray), TokenArray, &dwValue ); if (dwErr isnot NO_ERROR) { dwErr = ERROR_INVALID_PARAMETER; break; } if (dwValue is TRUE) { pPeer->dwConfigFlags |= MSDP_PEER_CONFIG_CACHING; } break; } case 6: { // defaultpeer DWORD dwValue; TOKEN_VALUE TokenArray[] = { { TOKEN_OPT_VALUE_NO, FALSE }, { TOKEN_OPT_VALUE_YES, TRUE } }; pPeer->dwConfigFlags &= ~MSDP_PEER_CONFIG_DEFAULTPEER; dwErr = MatchEnumTag( g_hModule, ArgumentArray[i + ulArgumentIndex], NUM_TOKENS_IN_TABLE(TokenArray), TokenArray, &dwValue ); if (dwErr isnot NO_ERROR) { dwErr = ERROR_INVALID_PARAMETER; break; } if (dwValue is TRUE) { pPeer->dwConfigFlags |= MSDP_PEER_CONFIG_DEFAULTPEER; } break; } case 7: { // encapsulation DWORD dwValue; dwErr = MatchEnumTag( g_hModule, ArgumentArray[i + ulArgumentIndex], MSDP_ENCAPS_SIZE, (PTOKEN_VALUE)MsdpEncapsTokenArray, &dwValue ); if (dwErr isnot NO_ERROR) { dwErr = ERROR_INVALID_PARAMETER; break; } pPeer->dwEncapsMethod = dwValue; break; } } } if (dwErr isnot NO_ERROR) { break; } // higher IP is passive. Setting this bit has no effect // except on the "show peer" report when the router isn't running. if (ntohl(pPeer->ipLocalAddress) > ntohl(pPeer->ipRemoteAddress)) { pPeer->dwConfigFlags |= MSDP_PEER_CONFIG_PASSIVE; } // // Update the configuration with the new settings. // Note that the update routine may perform additional validation // in the process of reconciling the new settings // with any existing settings. // dwErr = MsdpAddIPv4PeerInterface(pwszMachineName, wszInterfaceName, pPeer ); } while (FALSE); if (pPeer) { FREE(pPeer); } if (pulTagArray) { FREE(pulTagArray); } if (dwErr is NO_ERROR) { dwErr = ERROR_OKAY; } return dwErr; } DWORD HandleMsdpDeletePeer( PWCHAR pwszMachineName, PTCHAR* ArgumentArray, DWORD ulArgumentIndex, DWORD ulArgumentCount, DWORD dwFlags, PVOID pvData, BOOL* CommandDone ) { ULONG ulArgumentsLeft; ULONG BitVector; DWORD dwErr; ULONG ulErrorIndex = 0; ULONG i; PULONG pulTagArray; ULONG InfoSize; WCHAR wszInterfaceName[MAX_INTERFACE_NAME_LEN + 1]; DWORD dwBufferSize = sizeof(wszInterfaceName); TAG_TYPE TagTypeArray[] = { { TOKEN_OPT_NAME, NS_REQ_PRESENT, FALSE } }; VERIFY_INSTALLED(MS_IP_MSDP, L"MSDP"); if (ulArgumentIndex >= ulArgumentCount) { return ERROR_SHOW_USAGE; } ulArgumentsLeft = ulArgumentCount - ulArgumentIndex; // // We convert the optional tags into an array of 'TagTypeArray' indices // which guide is in our scanning of the argument list. // Since the tags are optional, this process may result in no tags at all, // in which case we assume that arguments are specified in exactly the order // given in 'TagTypeArray' above. // dwErr = QueryTagArray( &ArgumentArray[ulArgumentIndex], ulArgumentsLeft, TagTypeArray, NUM_TAGS_IN_TABLE(TagTypeArray), &pulTagArray ); if (dwErr) { return dwErr; } BitVector = 0; for (i = 0; i < ulArgumentsLeft; i++) { switch(pulTagArray ? pulTagArray[i] : i) { case 0: { // name IpmontrGetIfNameFromFriendlyName( ArgumentArray[i + ulArgumentIndex], wszInterfaceName, &dwBufferSize); break; } } } dwErr = IpmontrDeleteInterface( pwszMachineName, wszInterfaceName ); if (dwErr is NO_ERROR) { dwErr = ERROR_OKAY; } return dwErr; } DWORD HandleMsdpInstall( PWCHAR pwszMachineName, PTCHAR* ArgumentArray, DWORD ulArgumentIndex, DWORD ulArgumentCount, DWORD dwFlags, PVOID pvData, BOOL* CommandDone ) { DWORD dwErr = ERROR_OKAY; PUCHAR pGlobalInfo; ULONG ulLength; if (ulArgumentIndex != ulArgumentCount) { return ERROR_SHOW_USAGE; } // // To install MSDP, we construct the default configuration // and add it to the global configuration for the router. // dwErr = MakeMsdpGlobalConfig(&pGlobalInfo, &ulLength); if (dwErr isnot NO_ERROR) { DisplayError(g_hModule, dwErr); } else { dwErr = IpmontrSetInfoBlockInGlobalInfo( MS_IP_MSDP, pGlobalInfo, ulLength, 1 ); FREE(pGlobalInfo); if (dwErr is NO_ERROR) { dwErr = ERROR_OKAY; } else { DisplayError(g_hModule, dwErr); } } return dwErr; } DWORD HandleMsdpSetGlobal( PWCHAR pwszMachineName, PTCHAR* ArgumentArray, ULONG ulArgumentIndex, ULONG ulArgumentCount, DWORD dwFlags, PVOID pvData, BOOL* CommandDone ) { ULONG ulArgumentsLeft; DWORD dwErr; PULONG pulTagArray = NULL; DWORD dwLoggingLevel, dwAcceptAll; ULONG i, ulTemp; ULONG ulErrorIndex; PMSDP_GLOBAL_CONFIG pGlobalInfo = NULL; TAG_TYPE TagTypeArray[] = { { TOKEN_OPT_LOGGINGLEVEL, FALSE, FALSE }, { TOKEN_OPT_KEEPALIVE, FALSE, FALSE }, { TOKEN_OPT_SAHOLDDOWN, FALSE, FALSE }, { TOKEN_OPT_CONNECTRETRY, FALSE, FALSE }, { TOKEN_OPT_ACCEPTALL, FALSE, FALSE }, { TOKEN_OPT_CACHELIFETIME,FALSE, FALSE }, }; VERIFY_INSTALLED(MS_IP_MSDP, L"MSDP"); if (ulArgumentIndex >= ulArgumentCount) { return ERROR_SHOW_USAGE; } ulArgumentsLeft = ulArgumentCount - ulArgumentIndex; // // We convert the optional tags into an array of 'TagTypeArray' indices // which guide us in our scanning of the argument list. // Since the tags are optional, this process may result in no tags at all, // in which case we assume that arguments are specified in exactly the order // given in 'TagTypeArray' above. // dwErr = QueryTagArray( &ArgumentArray[ulArgumentIndex], ulArgumentsLeft, TagTypeArray, NUM_TAGS_IN_TABLE(TagTypeArray), &pulTagArray ); if (dwErr) { return dwErr; } do { dwErr = GetMsdpGlobalConfig( &pGlobalInfo ); if (dwErr isnot NO_ERROR) { break; } for (i = 0; i < ulArgumentsLeft; i++) { switch(pulTagArray ? pulTagArray[i] : i) { case 0: { // loglevel TOKEN_VALUE TokenArray[] = { { TOKEN_OPT_VALUE_NONE, MSDP_LOGGING_NONE }, { TOKEN_OPT_VALUE_ERROR, MSDP_LOGGING_ERROR }, { TOKEN_OPT_VALUE_WARN, MSDP_LOGGING_WARN }, { TOKEN_OPT_VALUE_INFO, MSDP_LOGGING_INFO } }; dwErr = MatchEnumTag( g_hModule, ArgumentArray[i + ulArgumentIndex], NUM_TOKENS_IN_TABLE(TokenArray), TokenArray, &pGlobalInfo->dwLoggingLevel ); if (dwErr) { dwErr = ERROR_INVALID_PARAMETER; ulErrorIndex = i; i = ulArgumentsLeft; break; } TagTypeArray[pulTagArray ? pulTagArray[i] : i].bPresent = TRUE; break; } case 1: { // keepalive pGlobalInfo->ulDefKeepAlive = _tcstoul( ArgumentArray[i + ulArgumentIndex], NULL, 10); break; } case 2: { // SA holddown pGlobalInfo->ulSAHolddown = _tcstoul( ArgumentArray[i + ulArgumentIndex], NULL, 10); break; } case 3: { // connectretry pGlobalInfo->ulDefConnectRetry = _tcstoul( ArgumentArray[i + ulArgumentIndex], NULL, 10); break; } case 4: { // acceptall TOKEN_VALUE TokenArray[] = { { TOKEN_OPT_VALUE_DISABLE, FALSE }, { TOKEN_OPT_VALUE_ENABLE, TRUE } }; pGlobalInfo->dwFlags &= ~MSDP_GLOBAL_FLAG_ACCEPT_ALL; dwErr = MatchEnumTag( g_hModule, ArgumentArray[i + ulArgumentIndex], NUM_TOKENS_IN_TABLE(TokenArray), TokenArray, &dwAcceptAll ); if (dwErr isnot NO_ERROR) { dwErr = ERROR_INVALID_PARAMETER; break; } if (dwAcceptAll is TRUE) { pGlobalInfo->dwFlags |= MSDP_GLOBAL_FLAG_ACCEPT_ALL; } break; } case 5: { // cachelifetime ulTemp = _tcstoul( ArgumentArray[i + ulArgumentIndex], NULL, 10); if ((ulTemp>0) and (ulTempulCacheLifetime = ulTemp; break; } } } if (dwErr isnot NO_ERROR) { break; } dwErr = SetMsdpGlobalConfig(pGlobalInfo); } while (FALSE); if (pulTagArray) { FREE(pulTagArray); } if (pGlobalInfo) { FREE(pGlobalInfo); } if (dwErr is NO_ERROR) { dwErr = ERROR_OKAY; } return dwErr; } DWORD HandleMsdpSetPeer( PWCHAR pwszMachineName, PTCHAR* ArgumentArray, DWORD ulArgumentIndex, DWORD ulArgumentCount, DWORD dwCmdFlags, PVOID pvData, BOOL* CommandDone ) { IPV4_ADDRESS ipLocalAddress, ipRemoteAddress; ULONG ulKeepAlive=0, ulSAPeriod=0, ulConnectRetry=0; DWORD dwEncapsMethod=0; DWORD dwErr = NO_ERROR; DWORD dwFlags = 0, dwFlagsMask = 0; ULONG ulArgumentsLeft; ULONG i; PMSDP_IPV4_PEER_CONFIG pPeer = NULL; PULONG pulTagArray; WCHAR wszInterfaceName[MAX_INTERFACE_NAME_LEN + 1]; DWORD dwBufferSize = sizeof(wszInterfaceName); TAG_TYPE TagTypeArray[] = { { TOKEN_OPT_NAME, NS_REQ_PRESENT, FALSE }, { TOKEN_OPT_REMADDR, NS_REQ_ZERO, FALSE }, { TOKEN_OPT_LOCALADDR, NS_REQ_ZERO, FALSE }, { TOKEN_OPT_KEEPALIVE, NS_REQ_ZERO, FALSE }, { TOKEN_OPT_CONNECTRETRY, NS_REQ_ZERO, FALSE }, { TOKEN_OPT_CACHING, NS_REQ_ZERO, FALSE }, { TOKEN_OPT_DEFAULTPEER, NS_REQ_ZERO, FALSE }, { TOKEN_OPT_ENCAPSMETHOD, NS_REQ_ZERO, FALSE }, }; VERIFY_INSTALLED(MS_IP_MSDP, L"MSDP"); if (ulArgumentIndex >= ulArgumentCount) { return ERROR_SHOW_USAGE; } ulArgumentsLeft = ulArgumentCount - ulArgumentIndex; // // We convert the optional tags into an array of 'TagTypeArray' indices // which guide is in our scanning of the argument list. // Since the tags are optional, this process may result in no tags at all, // in which case we assume that arguments are specified in exactly the order // given in 'TagTypeArray' above. // dwErr = QueryTagArray( &ArgumentArray[ulArgumentIndex], ulArgumentsLeft, TagTypeArray, NUM_TAGS_IN_TABLE(TagTypeArray), &pulTagArray ); if (dwErr) { return dwErr; } do { for (i = 0; i < ulArgumentsLeft; i++) { switch(pulTagArray ? pulTagArray[i] : i) { case 0: { // name IpmontrGetIfNameFromFriendlyName( ArgumentArray[i + ulArgumentIndex], wszInterfaceName, &dwBufferSize); break; } case 1: { // remaddr ipRemoteAddress = GetIpAddress( ArgumentArray[i + ulArgumentIndex]); break; } case 2: { // localaddr ipLocalAddress = GetIpAddress( ArgumentArray[i + ulArgumentIndex]); break; } case 3: { // keepalive dwFlagsMask |= MSDP_PEER_CONFIG_KEEPALIVE; if (!MatchToken(ArgumentArray[i+ulArgumentIndex], TOKEN_OPT_VALUE_DEFAULT)) { ulKeepAlive = _tcstoul( ArgumentArray[i + ulArgumentIndex], NULL, 10); dwFlags |= MSDP_PEER_CONFIG_KEEPALIVE; } break; } case 4: { // connectretry dwFlagsMask |= MSDP_PEER_CONFIG_CONNECTRETRY; if (!MatchToken(ArgumentArray[i+ulArgumentIndex], TOKEN_OPT_VALUE_DEFAULT)) { ulConnectRetry = _tcstoul( ArgumentArray[i + ulArgumentIndex], NULL, 10); dwFlags |= MSDP_PEER_CONFIG_CONNECTRETRY; } break; } case 5: { // caching DWORD dwValue; TOKEN_VALUE TokenArray[] = { { TOKEN_OPT_VALUE_NO, FALSE }, { TOKEN_OPT_VALUE_YES, TRUE } }; dwFlagsMask |= MSDP_PEER_CONFIG_CONNECTRETRY; dwErr = MatchEnumTag( g_hModule, ArgumentArray[i + ulArgumentIndex], NUM_TOKENS_IN_TABLE(TokenArray), TokenArray, &dwValue ); if (dwErr isnot NO_ERROR) { dwErr = ERROR_INVALID_PARAMETER; break; } if (dwValue is TRUE) { dwFlags |= MSDP_PEER_CONFIG_CACHING; } break; } case 6: { // defaultpeer DWORD dwValue; TOKEN_VALUE TokenArray[] = { { TOKEN_OPT_VALUE_NO, FALSE }, { TOKEN_OPT_VALUE_YES, TRUE } }; dwFlagsMask |= MSDP_PEER_CONFIG_DEFAULTPEER; dwErr = MatchEnumTag( g_hModule, ArgumentArray[i + ulArgumentIndex], NUM_TOKENS_IN_TABLE(TokenArray), TokenArray, &dwValue ); if (dwErr isnot NO_ERROR) { dwErr = ERROR_INVALID_PARAMETER; break; } if (dwValue is TRUE) { dwFlags |= MSDP_PEER_CONFIG_DEFAULTPEER; } break; } case 7: { // encapsulation DWORD dwValue; TOKEN_VALUE TokenArray[] = { { TOKEN_OPT_VALUE_NONE, MSDP_ENCAPS_NONE }, }; dwErr = MatchEnumTag( g_hModule, ArgumentArray[i + ulArgumentIndex], NUM_TOKENS_IN_TABLE(TokenArray), TokenArray, &dwValue ); if (dwErr isnot NO_ERROR) { dwErr = ERROR_INVALID_PARAMETER; break; } dwEncapsMethod = dwValue; break; } } } if (dwErr isnot NO_ERROR) { break; } // Locate peer dwErr = GetMsdpInterfaceConfig(wszInterfaceName, &pPeer); if (dwErr isnot NO_ERROR) { break; } // Update fields if (TagTypeArray[1].bPresent) { pPeer->ipRemoteAddress = ipRemoteAddress; } if (TagTypeArray[2].bPresent) { pPeer->ipLocalAddress = ipLocalAddress; } if (TagTypeArray[3].bPresent) { pPeer->ulKeepAlive = ulKeepAlive; } if (TagTypeArray[4].bPresent) { pPeer->ulConnectRetry = ulConnectRetry; } if (TagTypeArray[5].bPresent) { pPeer->dwEncapsMethod = dwEncapsMethod; } pPeer->dwConfigFlags = (pPeer->dwConfigFlags & ~dwFlagsMask) | dwFlags; // higher IP is passive. Setting bit has no effect except on // the "show peer" output when the router isn't running. if (ntohl(pPeer->ipLocalAddress) > ntohl(pPeer->ipRemoteAddress)) { pPeer->dwConfigFlags |= MSDP_PEER_CONFIG_PASSIVE; } else { pPeer->dwConfigFlags &= ~MSDP_PEER_CONFIG_PASSIVE; } // Update the configuration with the new settings. dwErr = SetMsdpInterfaceConfig(wszInterfaceName, pPeer); } while (FALSE); if (pPeer) { FREE(pPeer); } if (pulTagArray) { FREE(pulTagArray); } if (dwErr is NO_ERROR) { dwErr = ERROR_OKAY; } return dwErr; } DWORD HandleMsdpShowGlobal( PWCHAR pwszMachineName, PTCHAR* ArgumentArray, DWORD ulArgumentIndex, DWORD ulArgumentCount, DWORD dwFlags, PVOID pvData, BOOL* CommandDone ) { VERIFY_INSTALLED(MS_IP_MSDP, L"MSDP"); if (ulArgumentIndex != ulArgumentCount) { return ERROR_SHOW_USAGE; } ShowMsdpGlobalInfo(FORMAT_VERBOSE); return NO_ERROR; } DWORD HandleMsdpShowPeer( PWCHAR pwszMachineName, PTCHAR* ArgumentArray, DWORD ulArgumentIndex, DWORD ulArgumentCount, DWORD dwFlags, PVOID pvData, BOOL* CommandDone ) { ULONG ulArgumentsLeft; DWORD dwErr = NO_ERROR; PULONG pulTagArray = NULL; TAG_TYPE TagTypeArray[] = { { TOKEN_OPT_REMADDR, NS_REQ_ZERO, FALSE }, { TOKEN_OPT_NAME, NS_REQ_ZERO, FALSE }, }; VERIFY_INSTALLED(MS_IP_MSDP, L"MSDP"); if (ulArgumentIndex is ulArgumentCount) { return ShowMsdpPeerInfo(FORMAT_TABLE, NULL, NULL); } if (ulArgumentIndex > ulArgumentCount) { return ERROR_SHOW_USAGE; } ulArgumentsLeft = ulArgumentCount - ulArgumentIndex; // // We convert the optional tags into an array of 'TagTypeArray' indices // which guide is in our scanning of the argument list. // Since the tags are optional, this process may result in no tags // at all, in which case we assume that arguments are specified in // exactly the order given in 'TagTypeArray' above. // dwErr = QueryTagArray( &ArgumentArray[ulArgumentIndex], ulArgumentsLeft, TagTypeArray, NUM_TAGS_IN_TABLE(TagTypeArray), &pulTagArray ); if (dwErr) { return dwErr; } if (!pulTagArray) { dwErr = ShowMsdpPeerInfo(FORMAT_VERBOSE,NULL,NULL); } else if (pulTagArray[0] is 0) { // address dwErr = ShowMsdpPeerInfo(FORMAT_VERBOSE, ArgumentArray[ulArgumentIndex], NULL); } else if (pulTagArray[0] is 1) { // name dwErr = ShowMsdpPeerInfo(FORMAT_VERBOSE, NULL, ArgumentArray[ulArgumentIndex]); } else { dwErr = ERROR_SHOW_USAGE; } if (pulTagArray) { FREE(pulTagArray); } return dwErr; } DWORD HandleMsdpUninstall( PWCHAR pwszMachineName, PTCHAR* ArgumentArray, DWORD ulArgumentIndex, DWORD ulArgumentCount, DWORD dwFlags, PVOID pvData, BOOL* CommandDone ) { DWORD dwErr, dwTotal; ULONG ulNumInterfaces, i; PMPR_INTERFACE_0 pmi0 = NULL; if (ulArgumentIndex isnot ulArgumentCount) { return ERROR_SHOW_USAGE; } // First delete all peers. We need to do this ourselves since // IpmontrDeleteProtocol won't delete the peer interfaces. dwErr = IpmontrInterfaceEnum((PBYTE *) &pmi0, &ulNumInterfaces, &dwTotal); if (dwErr isnot NO_ERROR) { return dwErr; } for (i=0; i