Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

907 lines
21 KiB

  1. /*++
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. Module Name:
  4. routing\netsh\ip\route.c
  5. --*/
  6. #include "precomp.h"
  7. #pragma hdrstop
  8. DWORD
  9. AddSetDelRtmRouteInfo(
  10. IN PINTERFACE_ROUTE_INFO pRoute,
  11. IN LPCWSTR pwszIfName,
  12. IN DWORD dwCommand,
  13. IN DWORD dwFlags
  14. )
  15. /*++
  16. Routine Description:
  17. Adds/deletes normal (read as non persistant)
  18. routes on interfaces.
  19. Arguments:
  20. pRoute - route to add/set/delete
  21. pwszIfName - Interface Name
  22. dwCommand - Add, set, or delete
  23. Return Value:
  24. NO_ERROR
  25. --*/
  26. {
  27. ULONG dwOutEntrySize;
  28. DWORD dwRes, i;
  29. PMIB_IPDESTTABLE lpTable;
  30. PMIB_IPDESTROW pEntry;
  31. MIB_OPAQUE_QUERY QueryBuff[3]; // more than enough
  32. MIB_OPAQUE_QUERY *pQuery = QueryBuff;
  33. PMIB_OPAQUE_INFO pInfo;
  34. DEFINE_MIB_BUFFER(pRouteInfo, MIB_IPDESTROW, pRouteRow);
  35. if (!pRoute->dwRtInfoIfIndex)
  36. {
  37. //
  38. // Get the interface index from friendly name
  39. //
  40. dwRes = IpmontrGetIfIndexFromFriendlyName(g_hMIBServer,
  41. pwszIfName,
  42. &pRoute->dwRtInfoIfIndex);
  43. if (dwRes != NO_ERROR)
  44. {
  45. return dwRes;
  46. }
  47. //
  48. // The interface probably is disconnected
  49. //
  50. if (pRoute->dwRtInfoIfIndex == 0)
  51. {
  52. DisplayMessage(g_hModule, EMSG_INTERFACE_INVALID_OR_DISC);
  53. return ERROR_INVALID_PARAMETER;
  54. }
  55. }
  56. //
  57. // Use MprAdmin api to add, del or set entry
  58. //
  59. switch(dwCommand)
  60. {
  61. case ADD_COMMAND:
  62. case SET_COMMAND:
  63. //
  64. // Does this route already exist in the router ?
  65. //
  66. // Get all this protocol routes on dest
  67. pQuery->dwVarId = ROUTE_MATCHING;
  68. pQuery->rgdwVarIndex[0] = pRoute->dwRtInfoDest;
  69. pQuery->rgdwVarIndex[1] = pRoute->dwRtInfoMask;
  70. pQuery->rgdwVarIndex[2] = RTM_VIEW_MASK_ANY;
  71. pQuery->rgdwVarIndex[3] = pRoute->dwRtInfoProto;
  72. pInfo = NULL;
  73. dwRes = MibGet(PID_IP,
  74. IPRTRMGR_PID,
  75. (PVOID) pQuery,
  76. sizeof(MIB_OPAQUE_QUERY) + 3 * sizeof(DWORD),
  77. (PVOID *) &pInfo,
  78. &dwOutEntrySize);
  79. if ( dwRes isnot NO_ERROR )
  80. {
  81. DisplayMessage(g_hModule, MSG_IP_DIM_ERROR, dwRes );
  82. return dwRes;
  83. }
  84. if ( pInfo isnot NULL )
  85. {
  86. //
  87. // Search for a matching route
  88. //
  89. lpTable = (PMIB_IPDESTTABLE)(pInfo->rgbyData);
  90. for (i=0; i<lpTable->dwNumEntries; i++)
  91. {
  92. pEntry = &lpTable->table[i];
  93. if ((pEntry->dwForwardIfIndex == pRoute->dwRtInfoIfIndex) &&
  94. (pEntry->dwForwardNextHop == pRoute->dwRtInfoNextHop))
  95. {
  96. break;
  97. }
  98. }
  99. if (i == lpTable->dwNumEntries)
  100. {
  101. //
  102. // No matching route found - quit if set
  103. //
  104. if (dwCommand == SET_COMMAND)
  105. {
  106. MprAdminMIBBufferFree((PVOID)pInfo);
  107. return ERROR_NOT_FOUND;
  108. }
  109. }
  110. else
  111. {
  112. //
  113. // A matching route found - quit if add
  114. //
  115. if (dwCommand == ADD_COMMAND)
  116. {
  117. MprAdminMIBBufferFree((PVOID)pInfo);
  118. return ERROR_OBJECT_ALREADY_EXISTS;
  119. }
  120. }
  121. }
  122. else
  123. {
  124. //
  125. // No matching routes found - quit if set
  126. //
  127. if (dwCommand == SET_COMMAND)
  128. {
  129. return ERROR_NOT_FOUND;
  130. }
  131. }
  132. //
  133. // Convert the route to a ip route row format
  134. //
  135. pRouteInfo->dwId = ROUTE_MATCHING;
  136. pRouteRow->dwForwardDest = pRoute->dwRtInfoDest;
  137. pRouteRow->dwForwardMask = pRoute->dwRtInfoMask;
  138. pRouteRow->dwForwardPolicy = 0;
  139. pRouteRow->dwForwardNextHop = pRoute->dwRtInfoNextHop;
  140. pRouteRow->dwForwardIfIndex = pRoute->dwRtInfoIfIndex;
  141. pRouteRow->dwForwardType = 0;
  142. pRouteRow->dwForwardProto = pRoute->dwRtInfoProto;
  143. pRouteRow->dwForwardAge = INFINITE;
  144. pRouteRow->dwForwardNextHopAS = 0;
  145. pRouteRow->dwForwardMetric1 = pRoute->dwRtInfoMetric1;
  146. pRouteRow->dwForwardMetric2 = pRoute->dwRtInfoMetric2;
  147. pRouteRow->dwForwardMetric3 = pRoute->dwRtInfoMetric3;
  148. pRouteRow->dwForwardMetric4 = MIB_IPROUTE_METRIC_UNUSED;
  149. pRouteRow->dwForwardMetric5 = MIB_IPROUTE_METRIC_UNUSED;
  150. pRouteRow->dwForwardPreference = pRoute->dwRtInfoPreference;
  151. pRouteRow->dwForwardViewSet = pRoute->dwRtInfoViewSet;
  152. if (dwCommand == ADD_COMMAND)
  153. {
  154. dwRes = MprAdminMIBEntryCreate(g_hMIBServer,
  155. PID_IP,
  156. IPRTRMGR_PID,
  157. (PVOID)pRouteInfo,
  158. MIB_INFO_SIZE(MIB_IPDESTROW));
  159. }
  160. else
  161. {
  162. if (dwFlags & FIELDS_NOT_SPECIFIED)
  163. {
  164. //
  165. // Get the old preference, metric, or view
  166. //
  167. if (dwFlags & PREF_NOT_SPECIFIED)
  168. {
  169. pRouteRow->dwForwardPreference=pEntry->dwForwardPreference;
  170. }
  171. if (dwFlags & METRIC_NOT_SPECIFIED)
  172. {
  173. pRouteRow->dwForwardMetric1 = pEntry->dwForwardMetric1;
  174. }
  175. if (dwFlags & VIEW_NOT_SPECIFIED)
  176. {
  177. pRouteRow->dwForwardViewSet = pEntry->dwForwardViewSet;
  178. }
  179. }
  180. dwRes = MprAdminMIBEntrySet(g_hMIBServer,
  181. PID_IP,
  182. IPRTRMGR_PID,
  183. (PVOID)pRouteInfo,
  184. MIB_INFO_SIZE(MIB_IPDESTROW));
  185. }
  186. // Free the old route information obtained
  187. if (pInfo)
  188. {
  189. MprAdminMIBBufferFree((PVOID)pInfo);
  190. }
  191. break;
  192. case DELETE_COMMAND:
  193. {
  194. DWORD rgdwInfo[6];
  195. PMIB_OPAQUE_QUERY pIndex = (PMIB_OPAQUE_QUERY)rgdwInfo;
  196. pIndex->dwVarId = ROUTE_MATCHING;
  197. pIndex->rgdwVarIndex[0] = pRoute->dwRtInfoDest;
  198. pIndex->rgdwVarIndex[1] = pRoute->dwRtInfoMask;
  199. pIndex->rgdwVarIndex[2] = pRoute->dwRtInfoIfIndex;
  200. pIndex->rgdwVarIndex[3] = pRoute->dwRtInfoNextHop;
  201. pIndex->rgdwVarIndex[4] = pRoute->dwRtInfoProto;
  202. dwRes = MprAdminMIBEntryDelete(g_hMIBServer,
  203. PID_IP,
  204. IPRTRMGR_PID,
  205. (PVOID)pIndex,
  206. sizeof(rgdwInfo));
  207. break;
  208. }
  209. default:
  210. dwRes = ERROR_INVALID_PARAMETER;
  211. }
  212. return dwRes;
  213. }
  214. DWORD
  215. AddSetDelPersistentRouteInfo(
  216. IN PINTERFACE_ROUTE_INFO pRoute,
  217. IN LPCWSTR pwszIfName,
  218. IN DWORD dwCommand,
  219. IN DWORD dwFlags
  220. )
  221. /*++
  222. Routine Description:
  223. Adds/deletes persitant routes on interfaces.
  224. Arguments:
  225. route - route to add/set/delete
  226. pwszIfName - Interface Name
  227. dwCommand - Add, set, or delete
  228. Return Value:
  229. ERROR_OKAY
  230. --*/
  231. {
  232. DWORD dwRes;
  233. PINTERFACE_ROUTE_INFO pOldTable, pNewTable;
  234. DWORD dwIfType, dwSize, dwCount;
  235. pNewTable = NULL;
  236. do
  237. {
  238. dwRes = IpmontrGetInfoBlockFromInterfaceInfo(pwszIfName,
  239. IP_ROUTE_INFO,
  240. (PBYTE *) &pOldTable,
  241. &dwSize,
  242. &dwCount,
  243. &dwIfType);
  244. if((dwRes is ERROR_NOT_FOUND) &&
  245. dwCommand is ADD_COMMAND)
  246. {
  247. //
  248. // No route info but we are asked to add
  249. //
  250. pOldTable = NULL;
  251. dwRes = NO_ERROR;
  252. dwCount = 0;
  253. }
  254. if(dwRes isnot NO_ERROR)
  255. {
  256. break;
  257. }
  258. //
  259. // These take the old table and return a new one in its stead
  260. //
  261. switch(dwCommand)
  262. {
  263. case ADD_COMMAND:
  264. dwRes = AddRoute(pOldTable,
  265. pRoute,
  266. dwIfType,
  267. &dwCount,
  268. &pNewTable);
  269. break;
  270. case DELETE_COMMAND:
  271. dwRes = DeleteRoute(pOldTable,
  272. pRoute,
  273. dwIfType,
  274. &dwCount,
  275. &pNewTable);
  276. break;
  277. case SET_COMMAND:
  278. dwRes = SetRoute(pOldTable,
  279. pRoute,
  280. dwIfType,
  281. dwFlags,
  282. &dwCount);
  283. pNewTable = pOldTable;
  284. pOldTable = NULL;
  285. break;
  286. }
  287. if(dwRes != NO_ERROR)
  288. {
  289. break;
  290. }
  291. //
  292. // Set the new info back
  293. //
  294. dwRes = IpmontrSetInfoBlockInInterfaceInfo(pwszIfName,
  295. IP_ROUTE_INFO,
  296. (PBYTE)pNewTable,
  297. sizeof(INTERFACE_ROUTE_INFO),
  298. dwCount);
  299. if(dwRes != NO_ERROR)
  300. {
  301. break;
  302. }
  303. pNewTable = NULL;
  304. } while ( FALSE );
  305. if(pOldTable)
  306. {
  307. FREE_BUFFER(pOldTable);
  308. }
  309. if(pNewTable)
  310. {
  311. HeapFree(GetProcessHeap(),
  312. 0,
  313. pNewTable);
  314. pNewTable = NULL;
  315. }
  316. switch(dwRes)
  317. {
  318. case NO_ERROR:
  319. {
  320. dwRes = ERROR_OKAY;
  321. break;
  322. }
  323. case ERROR_NOT_FOUND:
  324. {
  325. WCHAR wszBuffer[MAX_INTERFACE_NAME_LEN+1];
  326. DWORD dwSizeTemp = sizeof(wszBuffer);
  327. IpmontrGetFriendlyNameFromIfName( pwszIfName, wszBuffer, &dwSizeTemp);
  328. DisplayMessage(g_hModule, EMSG_IP_NO_ROUTE_INFO, wszBuffer);
  329. dwRes = ERROR_SUPPRESS_OUTPUT;
  330. break;
  331. }
  332. case ERROR_NOT_ENOUGH_MEMORY:
  333. {
  334. DisplayMessage(g_hModule, MSG_IP_NOT_ENOUGH_MEMORY);
  335. dwRes = ERROR_SUPPRESS_OUTPUT;
  336. break;
  337. }
  338. }
  339. return dwRes;
  340. }
  341. DWORD
  342. SetRoute(
  343. IN PINTERFACE_ROUTE_INFO pTable,
  344. IN PINTERFACE_ROUTE_INFO pRoute,
  345. IN DWORD dwIfType,
  346. IN DWORD dwFlags,
  347. IN OUT PDWORD pdwCount
  348. )
  349. {
  350. ULONG ulIndex, i;
  351. //
  352. // If the count is 0, the function will return false
  353. // and we will error out
  354. //
  355. if(!IsRoutePresent(pTable,
  356. pRoute,
  357. dwIfType,
  358. *pdwCount,
  359. &ulIndex))
  360. {
  361. return ERROR_NOT_FOUND;
  362. }
  363. if (dwFlags & FIELDS_NOT_SPECIFIED)
  364. {
  365. //
  366. // Preserve the old values if not specified
  367. //
  368. if (dwFlags & PREF_NOT_SPECIFIED)
  369. {
  370. pRoute->dwRtInfoPreference = pTable[ulIndex].dwRtInfoPreference;
  371. }
  372. if (dwFlags & METRIC_NOT_SPECIFIED)
  373. {
  374. pRoute->dwRtInfoMetric1 = pTable[ulIndex].dwRtInfoMetric1;
  375. }
  376. if (dwFlags & VIEW_NOT_SPECIFIED)
  377. {
  378. pRoute->dwRtInfoViewSet = pTable[ulIndex].dwRtInfoViewSet;
  379. }
  380. }
  381. pTable[ulIndex] = *pRoute;
  382. return NO_ERROR;
  383. }
  384. DWORD
  385. AddRoute(
  386. IN PINTERFACE_ROUTE_INFO pOldTable,
  387. IN PINTERFACE_ROUTE_INFO pRoute,
  388. IN DWORD dwIfType,
  389. IN OUT PDWORD pdwCount,
  390. OUT INTERFACE_ROUTE_INFO **ppNewTable
  391. )
  392. /*++
  393. Routine Description:
  394. Adds a route to the current info
  395. Arguments:
  396. Return Value:
  397. NO_ERROR
  398. --*/
  399. {
  400. ULONG ulIndex, i;
  401. if(IsRoutePresent(pOldTable,
  402. pRoute,
  403. dwIfType,
  404. *pdwCount,
  405. &ulIndex))
  406. {
  407. return ERROR_OBJECT_ALREADY_EXISTS;
  408. }
  409. //
  410. // Just create a block with size n + 1
  411. //
  412. *ppNewTable = HeapAlloc(GetProcessHeap(),
  413. 0,
  414. ((*pdwCount) + 1) * sizeof(INTERFACE_ROUTE_INFO));
  415. if(*ppNewTable is NULL)
  416. {
  417. return ERROR_NOT_ENOUGH_MEMORY;
  418. }
  419. for(i = 0; i < *pdwCount; i++)
  420. {
  421. //
  422. // structure copy
  423. //
  424. (*ppNewTable)[i] = pOldTable[i];
  425. }
  426. //
  427. // copy the new route
  428. //
  429. (*ppNewTable)[i] = *pRoute;
  430. *pdwCount += 1;
  431. return NO_ERROR;
  432. }
  433. DWORD
  434. DeleteRoute(
  435. IN PINTERFACE_ROUTE_INFO pOldTable,
  436. IN PINTERFACE_ROUTE_INFO pRoute,
  437. IN DWORD dwIfType,
  438. IN OUT PDWORD pdwCount,
  439. OUT INTERFACE_ROUTE_INFO **ppNewTable
  440. )
  441. /*++
  442. Routine Description:
  443. Deletes a route from an interface
  444. Arguments:
  445. Return Value:
  446. NO_ERROR
  447. --*/
  448. {
  449. ULONG ulIndex, i, j;
  450. //
  451. // If the count is 0, the function will return false
  452. // and we will error out
  453. //
  454. if(!IsRoutePresent(pOldTable,
  455. pRoute,
  456. dwIfType,
  457. *pdwCount,
  458. &ulIndex))
  459. {
  460. return ERROR_NOT_FOUND;
  461. }
  462. //
  463. // If the count is 1
  464. //
  465. *pdwCount -= 1;
  466. if(*pdwCount is 0)
  467. {
  468. *ppNewTable = NULL;
  469. return NO_ERROR;
  470. }
  471. //
  472. // delete the route
  473. //
  474. *ppNewTable = HeapAlloc(GetProcessHeap(),
  475. 0,
  476. (*pdwCount) * sizeof(INTERFACE_ROUTE_INFO));
  477. if(*ppNewTable is NULL)
  478. {
  479. return ERROR_NOT_ENOUGH_MEMORY;
  480. }
  481. i = j = 0;
  482. while(i <= *pdwCount)
  483. {
  484. if(i == ulIndex)
  485. {
  486. i++;
  487. continue;
  488. }
  489. //
  490. // structure copy
  491. //
  492. (*ppNewTable)[j] = pOldTable[i];
  493. i++;
  494. j++;
  495. }
  496. return NO_ERROR;
  497. }
  498. BOOL
  499. IsRoutePresent(
  500. IN PINTERFACE_ROUTE_INFO pTable,
  501. IN PINTERFACE_ROUTE_INFO pRoute,
  502. IN DWORD dwIfType,
  503. IN ULONG ulCount,
  504. OUT PULONG pulIndex
  505. )
  506. /*++
  507. Routine Description:
  508. Checks to see if interface is already present
  509. Arguments:
  510. Return Value:
  511. NO_ERROR
  512. --*/
  513. {
  514. ULONG i;
  515. BOOL bDontMatchNHop;
  516. if((dwIfType is ROUTER_IF_TYPE_DEDICATED) or
  517. (dwIfType is ROUTER_IF_TYPE_INTERNAL))
  518. {
  519. bDontMatchNHop = FALSE;
  520. }
  521. else
  522. {
  523. bDontMatchNHop = TRUE;
  524. }
  525. // Do this check just to keep the prefix checker happy
  526. if (pTable is NULL)
  527. {
  528. return FALSE;
  529. }
  530. for(i = 0; i < ulCount; i++)
  531. {
  532. if((pTable[i].dwRtInfoDest is pRoute->dwRtInfoDest) and
  533. (pTable[i].dwRtInfoMask is pRoute->dwRtInfoMask) and
  534. #if 0
  535. (pTable[i].dwRtInfoProto is pRoute->dwRtInfoProto) and
  536. #endif
  537. (bDontMatchNHop or
  538. (pTable[i].dwRtInfoNextHop is pRoute->dwRtInfoNextHop)))
  539. {
  540. *pulIndex = i;
  541. return TRUE;
  542. }
  543. }
  544. return FALSE;
  545. }
  546. DWORD
  547. ShowIpPersistentRoute(
  548. IN HANDLE hFile, OPTIONAL
  549. IN LPCWSTR pwszIfName,
  550. IN OUT PDWORD pdwNumRows
  551. )
  552. /*++
  553. Routine Description:
  554. Show the static (persistent) routes on the interface
  555. Arguments:
  556. pwszIfName - Interface name
  557. Return Value:
  558. NO_ERROR
  559. --*/
  560. {
  561. PINTERFACE_ROUTE_INFO pRoutes;
  562. DWORD dwErr, dwBlkSize, dwCount, dwIfType, dwNumParsed, i;
  563. WCHAR wszNextHop[ADDR_LENGTH + 1];
  564. WCHAR wszIfDesc[MAX_INTERFACE_NAME_LEN + 1];
  565. PWCHAR pwszProto, pwszToken, pwszQuoted;
  566. WCHAR wszViews[3];
  567. dwErr = GetInterfaceDescription(pwszIfName,
  568. wszIfDesc,
  569. &dwNumParsed);
  570. if (!dwNumParsed)
  571. {
  572. wcscpy(wszIfDesc, pwszIfName);
  573. }
  574. //
  575. // Retrieve the routes
  576. //
  577. dwErr = IpmontrGetInfoBlockFromInterfaceInfo(pwszIfName,
  578. IP_ROUTE_INFO,
  579. (PBYTE *) &pRoutes,
  580. &dwBlkSize,
  581. &dwCount,
  582. &dwIfType);
  583. if (dwErr != NO_ERROR)
  584. {
  585. return dwErr;
  586. }
  587. if((pRoutes == NULL) ||
  588. (dwCount == 0))
  589. {
  590. return NO_ERROR;
  591. }
  592. if(hFile == NULL)
  593. {
  594. if (*pdwNumRows is 0)
  595. {
  596. DisplayMessage(g_hModule, MSG_RTR_ROUTE_HDR);
  597. }
  598. pwszQuoted = NULL;
  599. }
  600. else
  601. {
  602. pwszQuoted = MakeQuotedString(wszIfDesc);
  603. }
  604. for(i = 0; i < dwCount; i++)
  605. {
  606. wszViews[0] = (pRoutes[i].dwRtInfoViewSet & RTM_VIEW_MASK_UCAST)? 'U':' ';
  607. wszViews[1] = (pRoutes[i].dwRtInfoViewSet & RTM_VIEW_MASK_MCAST)? 'M':' ';
  608. wszViews[2] = '\0';
  609. switch(pRoutes[i].dwRtInfoProto)
  610. {
  611. case PROTO_IP_NT_AUTOSTATIC:
  612. {
  613. pwszProto = MakeString(g_hModule, STRING_NT_AUTOSTATIC);
  614. pwszToken = TOKEN_VALUE_AUTOSTATIC;
  615. break;
  616. }
  617. case PROTO_IP_NT_STATIC:
  618. {
  619. pwszProto = MakeString(g_hModule, STRING_STATIC);
  620. pwszToken = TOKEN_VALUE_STATIC;
  621. break;
  622. }
  623. case PROTO_IP_NT_STATIC_NON_DOD:
  624. {
  625. pwszProto = MakeString(g_hModule, STRING_NONDOD);
  626. pwszToken = TOKEN_VALUE_NONDOD;
  627. break;
  628. }
  629. default:
  630. {
  631. pwszProto = MakeString(g_hModule, STRING_PROTO_UNKNOWN);
  632. pwszToken = NULL;
  633. break;
  634. }
  635. }
  636. MakeUnicodeIpAddr(wszNextHop,
  637. inet_ntoa(*((struct in_addr *)&(pRoutes[i].dwRtInfoNextHop))));
  638. if(hFile)
  639. {
  640. if(pwszToken)
  641. {
  642. WCHAR wszMask[ADDR_LENGTH + 1], wszDest[ADDR_LENGTH + 1];
  643. PWCHAR pwszView = NULL;
  644. MakeUnicodeIpAddr(wszDest,
  645. inet_ntoa(*((struct in_addr *)&(pRoutes[i].dwRtInfoDest))));
  646. MakeUnicodeIpAddr(wszMask,
  647. inet_ntoa(*((struct in_addr *)&(pRoutes[i].dwRtInfoMask))));
  648. switch (pRoutes[i].dwRtInfoViewSet)
  649. {
  650. case RTM_VIEW_MASK_UCAST: pwszView=TOKEN_VALUE_UNICAST ; break;
  651. case RTM_VIEW_MASK_MCAST: pwszView=TOKEN_VALUE_MULTICAST; break;
  652. case RTM_VIEW_MASK_UCAST
  653. |RTM_VIEW_MASK_MCAST: pwszView=TOKEN_VALUE_BOTH; break;
  654. }
  655. if (pwszView)
  656. {
  657. DisplayMessageT( DMP_IP_ADDSET_PERSISTENTROUTE,
  658. wszDest,
  659. wszMask,
  660. pwszQuoted,
  661. wszNextHop,
  662. pwszToken,
  663. pRoutes[i].dwRtInfoPreference,
  664. pRoutes[i].dwRtInfoMetric1,
  665. pwszView );
  666. }
  667. }
  668. }
  669. else
  670. {
  671. WCHAR wcszBuffer[80];
  672. MakePrefixStringW( wcszBuffer,
  673. pRoutes[i].dwRtInfoDest,
  674. pRoutes[i].dwRtInfoMask );
  675. DisplayMessage(g_hModule,
  676. MSG_RTR_ROUTE_INFO,
  677. wcszBuffer,
  678. pwszProto,
  679. pRoutes[i].dwRtInfoPreference,
  680. pRoutes[i].dwRtInfoMetric1,
  681. wszNextHop,
  682. wszViews,
  683. wszIfDesc);
  684. (*pdwNumRows)++;
  685. }
  686. FreeString(pwszProto);
  687. }
  688. if(pwszQuoted)
  689. {
  690. FreeQuotedString(pwszQuoted);
  691. }
  692. HeapFree(GetProcessHeap(),
  693. 0,
  694. pRoutes);
  695. return NO_ERROR;
  696. }