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.

552 lines
11 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. changebc.c
  5. Abstract:
  6. The change broadcast handlers
  7. Author:
  8. Stefan Solomon 07/11/1995
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. BOOL RTMSignaledChanges = FALSE;
  14. BOOL RIPSignaledChanges = FALSE;
  15. BOOL DestroyStartChangesBcastWi = FALSE;
  16. // List of interfaces CBs which can send changes broadcasts
  17. LIST_ENTRY IfChangesBcastList;
  18. PWORK_ITEM
  19. CreateChangesBcastWi(PICB icbp);
  20. VOID
  21. AddRouteToChangesBcast(PICB icbp,
  22. PIPX_ROUTE IpxRoutep);
  23. VOID
  24. FlushChangesBcastQueue(PICB icbp);
  25. BOOL
  26. IsChangeBcastPacketEmpty(PUCHAR hdrp);
  27. VOID
  28. CreateStartChangesBcastWi(VOID)
  29. {
  30. PWORK_ITEM wip;
  31. InitializeListHead(&IfChangesBcastList);
  32. if((wip = AllocateWorkItem(START_CHANGES_BCAST_TYPE)) == NULL) {
  33. // !!! log -> can't allocate wi
  34. return;
  35. }
  36. StartWiTimer(wip, CHANGES_BCAST_TIME);
  37. }
  38. VOID
  39. ProcessRTMChanges(VOID)
  40. {
  41. RTMSignaledChanges = TRUE;
  42. }
  43. VOID
  44. ProcessRIPChanges(VOID)
  45. {
  46. RIPSignaledChanges = TRUE;
  47. }
  48. /*++
  49. Function: StartChangesBcast
  50. Descr: Called by the worker every 1 second
  51. Remark: >> Called with the database lock held <<
  52. --*/
  53. VOID
  54. StartChangesBcast(PWORK_ITEM wip)
  55. {
  56. PLIST_ENTRY lep;
  57. PICB icbp;
  58. PWORK_ITEM bcwip; // bcast change work item
  59. BOOL skipit, lastmessage;
  60. IPX_ROUTE IpxRoute;
  61. if(DestroyStartChangesBcastWi) {
  62. FreeWorkItem(wip);
  63. return;
  64. }
  65. if(!RTMSignaledChanges && !RIPSignaledChanges) {
  66. // re-queue in the timer queue if no changes signaled in the last 1 sec
  67. StartWiTimer(wip, CHANGES_BCAST_TIME);
  68. return;
  69. }
  70. // make a list of interfaces with
  71. // oper state UP. This is a local list of nodes where each node points to
  72. // an interface with state UP and lock held.
  73. lep = IndexIfList.Flink;
  74. while(lep != &IndexIfList)
  75. {
  76. icbp = CONTAINING_RECORD(lep, ICB, IfListLinkage);
  77. if((icbp->InterfaceIndex != 0) &&
  78. (icbp->IfStats.RipIfOperState == OPER_STATE_UP) &&
  79. (icbp->IfConfigInfo.UpdateMode == IPX_STANDARD_UPDATE) &&
  80. (icbp->IfConfigInfo.Supply == ADMIN_STATE_ENABLED)) {
  81. ACQUIRE_IF_LOCK(icbp);
  82. if((bcwip = CreateChangesBcastWi(icbp)) == NULL) {
  83. RELEASE_IF_LOCK(icbp);
  84. break;
  85. }
  86. else
  87. {
  88. // insert the bcast wi in the interface changes bcast queue
  89. InsertTailList(&icbp->ChangesBcastQueue, &bcwip->Linkage);
  90. // insert the interface CB in the global if changes bcast list
  91. InsertTailList(&IfChangesBcastList, &icbp->AuxLinkage);
  92. }
  93. }
  94. lep = lep->Flink;
  95. }
  96. // dequeue the RTM messages. For each message fill in the bcast changes
  97. // wi packets according to the split horizon algorithm
  98. if(RIPSignaledChanges) {
  99. ACQUIRE_RIP_CHANGED_LIST_LOCK;
  100. while(DequeueRouteChangeFromRip(&IpxRoute) == NO_ERROR) {
  101. lep = IfChangesBcastList.Flink;
  102. while(lep != &IfChangesBcastList)
  103. {
  104. icbp = CONTAINING_RECORD(lep, ICB, AuxLinkage);
  105. AddRouteToChangesBcast(icbp, &IpxRoute);
  106. lep = lep->Flink;
  107. }
  108. }
  109. RELEASE_RIP_CHANGED_LIST_LOCK;
  110. RIPSignaledChanges = FALSE;
  111. }
  112. if(RTMSignaledChanges) {
  113. while(DequeueRouteChangeFromRtm(&IpxRoute, &skipit, &lastmessage) == NO_ERROR)
  114. {
  115. if(skipit) {
  116. if(lastmessage) {
  117. break;
  118. }
  119. else
  120. {
  121. continue;
  122. }
  123. }
  124. else
  125. {
  126. lep = IfChangesBcastList.Flink;
  127. while(lep != &IfChangesBcastList)
  128. {
  129. icbp = CONTAINING_RECORD(lep, ICB, AuxLinkage);
  130. AddRouteToChangesBcast(icbp, &IpxRoute);
  131. lep = lep->Flink;
  132. }
  133. if(lastmessage) {
  134. break;
  135. }
  136. }
  137. }
  138. RTMSignaledChanges = FALSE;
  139. }
  140. // send the first bcast change wi for each if CB
  141. while(!IsListEmpty(&IfChangesBcastList))
  142. {
  143. lep = RemoveHeadList(&IfChangesBcastList);
  144. icbp = CONTAINING_RECORD(lep, ICB, AuxLinkage);
  145. if(!IsListEmpty(&icbp->ChangesBcastQueue)) {
  146. lep = RemoveHeadList(&icbp->ChangesBcastQueue);
  147. bcwip = CONTAINING_RECORD(lep, WORK_ITEM, Linkage);
  148. // check if the bcast work item packet contains at least one net entry
  149. if(!IsChangeBcastPacketEmpty(bcwip->Packet)) {
  150. // send the bcast change work item
  151. if(IfRefSendSubmit(bcwip) != NO_ERROR) {
  152. // can't send on this interface -> Flush the changes bc queue
  153. FlushChangesBcastQueue(icbp);
  154. // and free the current changes bcast wi
  155. FreeWorkItem(bcwip);
  156. }
  157. }
  158. else
  159. {
  160. FreeWorkItem(bcwip);
  161. }
  162. }
  163. RELEASE_IF_LOCK(icbp);
  164. }
  165. // requeue the start changes bcast wi in timer queue
  166. StartWiTimer(wip, CHANGES_BCAST_TIME);
  167. }
  168. /*++
  169. Function: IfChangeBcast
  170. Descr: if the interface is operational then
  171. the change bcast packet work item is freed and the next change bacst
  172. wi is dequeued from the interface change bcast queue.
  173. else
  174. the work item is discarded
  175. Remark: Called with the interface lock held
  176. --*/
  177. VOID
  178. IfChangeBcast(PWORK_ITEM wip)
  179. {
  180. PICB icbp;
  181. PWORK_ITEM list_wip;
  182. PLIST_ENTRY lep;
  183. icbp = wip->icbp;
  184. if(icbp->IfStats.RipIfOperState != OPER_STATE_UP) {
  185. // flush the associated changes bcast queue if any
  186. FlushChangesBcastQueue(icbp);
  187. }
  188. else
  189. {
  190. if(!IsListEmpty(&icbp->ChangesBcastQueue)) {
  191. // send next bcast change
  192. lep = RemoveHeadList(&icbp->ChangesBcastQueue);
  193. list_wip = CONTAINING_RECORD(lep, WORK_ITEM, Linkage);
  194. // submit the work item for send and increment the ref count
  195. if(IfRefSendSubmit(list_wip) != NO_ERROR) {
  196. // can't send on this interface -> Flush the changes bc queue
  197. FlushChangesBcastQueue(icbp);
  198. // and free the one we intended to send
  199. FreeWorkItem(list_wip);
  200. }
  201. }
  202. }
  203. FreeWorkItem(wip);
  204. }
  205. /*++
  206. Function: ShutdownInterfaces
  207. Descr: called to:
  208. 1. Initiate a bcast update on all interfaces with the down routes
  209. 2. check on the bcast update termination
  210. Remark: called with database locked
  211. Note: Because the database is locked when this routine is called,
  212. no interface can change its operational state while this routine
  213. is executing
  214. --*/
  215. #define IsStartShutdown() \
  216. wip->WorkItemSpecific.WIS_ShutdownInterfaces.ShutdownState == SHUTDOWN_START
  217. #define IsCheckShutdown() \
  218. wip->WorkItemSpecific.WIS_ShutdownInterfaces.ShutdownState == SHUTDOWN_STATUS_CHECK
  219. #define SetCheckShutdown() \
  220. wip->WorkItemSpecific.WIS_ShutdownInterfaces.ShutdownState = SHUTDOWN_STATUS_CHECK;
  221. /*++
  222. Function: ShutdownInterfaces
  223. Descr: if START_SHUTDOWN then:
  224. initiates if shutdown bcast on all active ifs and
  225. removes (deletes) all inactive ifs
  226. else
  227. removes all ifs which finished shutdown bcast
  228. Remark: called with database lock held
  229. --*/
  230. VOID
  231. ShutdownInterfaces(PWORK_ITEM wip)
  232. {
  233. PLIST_ENTRY lep;
  234. PICB icbp;
  235. if(IsStartShutdown()) {
  236. lep = IndexIfList.Flink;
  237. while(lep != &IndexIfList)
  238. {
  239. icbp = CONTAINING_RECORD(lep, ICB, IfListLinkage);
  240. lep = lep->Flink;
  241. ACQUIRE_IF_LOCK(icbp);
  242. if(icbp->IfStats.RipIfOperState != OPER_STATE_UP) {
  243. // interface down -> delete it
  244. Trace(CHANGEBC_TRACE, "ShutdownInterfaces: delete inactive if %d\n", icbp->InterfaceIndex);
  245. if(!DeleteRipInterface(icbp)) {
  246. // if cb moved on discarded list, still referenced
  247. RELEASE_IF_LOCK(icbp);
  248. }
  249. }
  250. else
  251. {
  252. // interface up -> remove its rip routes
  253. DeleteAllRipRoutes(icbp->InterfaceIndex);
  254. RELEASE_IF_LOCK(icbp);
  255. }
  256. }
  257. }
  258. else
  259. {
  260. SS_ASSERT(IsCheckShutdown());
  261. lep = IndexIfList.Flink;
  262. while(lep != &IndexIfList)
  263. {
  264. icbp = CONTAINING_RECORD(lep, ICB, IfListLinkage);
  265. lep = lep->Flink;
  266. ACQUIRE_IF_LOCK(icbp);
  267. if(IsListEmpty(&icbp->ChangesBcastQueue)) {
  268. Trace(CHANGEBC_TRACE, "ShutdownInterfaces: delete shut-down if %d\n", icbp->InterfaceIndex);
  269. // interface broadcasted all changes -> delete it
  270. if(!DeleteRipInterface(icbp)) {
  271. // if cb moved on discarded list, still referenced
  272. RELEASE_IF_LOCK(icbp);
  273. }
  274. }
  275. else
  276. {
  277. // interface still broadcasting
  278. RELEASE_IF_LOCK(icbp);
  279. }
  280. }
  281. }
  282. if(!IsListEmpty(&IndexIfList)) {
  283. SetCheckShutdown();
  284. StartWiTimer(wip, 5000);
  285. }
  286. else
  287. {
  288. // no more ifs up
  289. FreeWorkItem(wip);
  290. // signal the worker thread to stop
  291. SetEvent(WorkerThreadObjects[TERMINATE_WORKER_EVENT]);
  292. }
  293. }
  294. /*++
  295. Function: CreateChnagesBcastWi
  296. Descr: allocates and inits the wi and packet header for a chnages bacst
  297. --*/
  298. PWORK_ITEM
  299. CreateChangesBcastWi(PICB icbp)
  300. {
  301. PWORK_ITEM wip;
  302. UCHAR ripsocket[2];
  303. if((wip = AllocateWorkItem(CHANGE_BCAST_PACKET_TYPE)) == NULL) {
  304. return NULL;
  305. }
  306. // init the bcast work item
  307. wip->icbp = icbp;
  308. wip->AdapterIndex = icbp->AdapterBindingInfo.AdapterIndex;
  309. PUTUSHORT2SHORT(ripsocket, IPX_RIP_SOCKET);
  310. SetRipIpxHeader(wip->Packet,
  311. icbp,
  312. bcastnode,
  313. ripsocket,
  314. RIP_RESPONSE);
  315. // set initial packet length
  316. PUTUSHORT2SHORT(wip->Packet + IPXH_LENGTH, RIP_INFO); // header length + RIP op code
  317. return wip;
  318. }
  319. /*++
  320. Function: AddRouteToChangesBcast
  321. Descr: checks if the route should be bcasted on this if
  322. walks the list of broadcast change work items queued at the
  323. if CB and sets the network entry in the last packet
  324. If last packet is full allocates a new work bcast work item
  325. --*/
  326. VOID
  327. AddRouteToChangesBcast(PICB icbp,
  328. PIPX_ROUTE IpxRoutep)
  329. {
  330. PUCHAR hdrp;
  331. PLIST_ENTRY lep;
  332. USHORT pktlen;
  333. PWORK_ITEM wip; // changes bcast wi ptr
  334. // check if to bcast the route on this if
  335. if(!IsRouteAdvertisable(icbp, IpxRoutep)) {
  336. return;
  337. }
  338. // go to the last bcast change wi in the list
  339. lep = icbp->ChangesBcastQueue.Blink;
  340. if(lep == &icbp->ChangesBcastQueue) {
  341. // changes bcast queue empty !
  342. return;
  343. }
  344. wip = CONTAINING_RECORD(lep, WORK_ITEM, Linkage);
  345. // check if the last packet is full
  346. GETSHORT2USHORT(&pktlen, wip->Packet + IPXH_LENGTH);
  347. if(pktlen >= RIP_PACKET_LEN) {
  348. // we need a new packet
  349. if((wip = CreateChangesBcastWi(icbp)) == NULL) {
  350. // out of memory
  351. return;
  352. }
  353. InsertTailList(&icbp->ChangesBcastQueue, &wip->Linkage);
  354. GETSHORT2USHORT(&pktlen, wip->Packet + IPXH_LENGTH);
  355. }
  356. SetNetworkEntry(wip->Packet + pktlen, IpxRoutep, icbp->LinkTickCount);
  357. pktlen += NE_ENTRYSIZE;
  358. PUTUSHORT2SHORT(&wip->Packet + IPXH_LENGTH, pktlen);
  359. }
  360. BOOL
  361. IsChangeBcastPacketEmpty(PUCHAR hdrp)
  362. {
  363. USHORT pktlen;
  364. GETSHORT2USHORT(&pktlen, hdrp + IPXH_LENGTH);
  365. if(pktlen > RIP_INFO) {
  366. return FALSE;
  367. }
  368. else
  369. {
  370. return TRUE;
  371. }
  372. }
  373. VOID
  374. FlushChangesBcastQueue(PICB icbp)
  375. {
  376. PLIST_ENTRY lep;
  377. PWORK_ITEM wip;
  378. // flush the associated changes bcast queue if any
  379. while(!IsListEmpty(&icbp->ChangesBcastQueue))
  380. {
  381. lep = RemoveHeadList(&icbp->ChangesBcastQueue);
  382. wip = CONTAINING_RECORD(lep, WORK_ITEM, Linkage);
  383. FreeWorkItem(wip);
  384. }
  385. }