EnetArp.Mod 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. MODULE EnetArp;
  2. (**
  3. AUTHOR: Alexey Morozov, HighDim GmbH, 2015
  4. PURPOSE: Ethernet networking stack, ARP protocol
  5. *)
  6. IMPORT
  7. SYSTEM, EnetBase, EnetTiming, Interfaces := EnetInterfaces, Trace := EnetTrace;
  8. CONST
  9. ArpHwTypeEth* = 0x0100; (** ARP hardware type *)
  10. (**
  11. ARP operation types
  12. *)
  13. ArpOpRequest* = 0x0100;
  14. ArpOpResponse* = 0x0200;
  15. ArpCacheSize* = 256;
  16. SubnetMaskC = 0x00FFFFFF; (* mask for class C subnetwork (255.255.255.0) *)
  17. UpdateIntervalMs = 10000; (* update ARP cache state interval in ms *)
  18. ArpEntryRequestTimeoutMs = 100; (* ARP entry request timeout in ms *)
  19. ArpEntryExpireTimeoutMs = 20*60*1000; (* default ARP entry expiration timeout in ms *)
  20. TYPE
  21. Int32 = EnetBase.Int32;
  22. Int16 = EnetBase.Int16;
  23. Int = EnetBase.Int;
  24. (**
  25. ARP cache
  26. *)
  27. ArpCache* = POINTER TO ArpCacheDesc;
  28. ArpCacheDesc* = RECORD(EnetBase.IpAddrCacheDesc)
  29. entries*: ARRAY ArpCacheSize OF ArpEntry;
  30. requested: ArpEntry; (* linked list of entries being under resolution *)
  31. END;
  32. (**
  33. Entry of Address Resolution Protocol (ARP) cache
  34. *)
  35. ArpEntry* = POINTER TO ArpEntryDesc;
  36. ArpEntryDesc* = RECORD(EnetBase.IpAddrCacheEntryDesc)
  37. static*: BOOLEAN; (** TRUE for static entries not to be removed automatically *)
  38. resolved*: BOOLEAN; (** becomes TRUE after the IPv4 address of the entry has been resolved *)
  39. requested*: BOOLEAN; (** TRUE if the entry is in the requested list *)
  40. timestamp*: EnetTiming.Time; (* time when the entry was registered as resolved *)
  41. completionHandler: EnetBase.TaskHandler; (* completion handler from the user *)
  42. timeoutHandler: EnetBase.TaskHandler; (* request timeout handler *)
  43. prevReq, nextReq: ArpEntry; (* for managing a linked list of requested entries *)
  44. cache: ArpCache;
  45. END;
  46. (*
  47. Initialize ARP cache
  48. *)
  49. PROCEDURE InitArpCache(cache: ArpCache; intf: EnetBase.Interface);
  50. VAR
  51. i: Int;
  52. entry: ArpEntry;
  53. BEGIN
  54. ASSERT(cache # NIL);
  55. FOR i := 0 TO ArpCacheSize-1 DO
  56. NEW(entry);
  57. entry.ipAddr.addr[0] := 0;
  58. entry.ipAddr.ver := 4;
  59. entry.macAddr := EnetBase.NilMacAddr;
  60. entry.static := FALSE;
  61. entry.resolved := FALSE;
  62. entry.requested := FALSE;
  63. entry.timestamp := 0;
  64. entry.cache := cache;
  65. NEW(entry.timeoutHandler);
  66. cache.entries[i] := entry;
  67. END;
  68. cache.requested := NIL;
  69. cache.cleanCache := CleanArpCache;
  70. cache.addStaticEntry := AddStaticEntry;
  71. cache.enumerateEntries := EnumerateEntries;
  72. cache.intf := intf;
  73. END InitArpCache;
  74. PROCEDURE IsSubnetMaskC(CONST subnetMask: EnetBase.IpAddr): BOOLEAN;
  75. BEGIN
  76. RETURN SYSTEM.MSK(subnetMask.addr[0],SubnetMaskC) = SubnetMaskC;
  77. END IsSubnetMaskC;
  78. (*
  79. Get a free ARP entry given an IP address
  80. *)
  81. PROCEDURE GetFreeEntry(cache: ArpCache; ipv4Addr: Int32): ArpEntry;
  82. VAR
  83. i: Int;
  84. entry: ArpEntry;
  85. BEGIN
  86. IF IsSubnetMaskC(cache.intf.ipv4SubnetMask) THEN
  87. i := (ipv4Addr DIV 1000000H) MOD 100H; (* the most significant byte *)
  88. entry := cache.entries[i];
  89. ASSERT(~entry.static & ~entry.resolved & ~entry.requested);
  90. entry.ipAddr.addr[0] := ipv4Addr;
  91. RETURN entry;
  92. ELSE
  93. HALT(100);
  94. END;
  95. END GetFreeEntry;
  96. (*
  97. Find a resolved ARP cache entry given an IPv4 address
  98. *)
  99. PROCEDURE FindResolvedEntryByIpAddr(cache: ArpCache; ipv4Addr: Int32): ArpEntry;
  100. VAR
  101. i: Int;
  102. entry: ArpEntry;
  103. BEGIN
  104. IF IsSubnetMaskC(cache.intf.ipv4SubnetMask) THEN
  105. i := (ipv4Addr DIV 1000000H) MOD 100H; (* the most significant byte *)
  106. entry := cache.entries[i];
  107. IF entry.resolved THEN RETURN entry; ELSE RETURN NIL; END;
  108. ELSE
  109. HALT(100);
  110. END;
  111. END FindResolvedEntryByIpAddr;
  112. (*
  113. Find a requested ARP entry given an IPv4 address
  114. *)
  115. PROCEDURE FindRequestedEntryByIpAddr(cache: ArpCache; ipv4Addr: Int32; removeIfResolved: BOOLEAN): ArpEntry;
  116. VAR entry, prev: ArpEntry;
  117. BEGIN
  118. entry := cache.requested;
  119. WHILE (entry # NIL) & (entry.ipAddr.addr[0] # ipv4Addr) DO
  120. prev := entry;
  121. entry := prev.nextReq;
  122. END;
  123. IF removeIfResolved & (entry # NIL) THEN
  124. RemoveRequestedEntry(cache,entry);
  125. END;
  126. RETURN entry;
  127. END FindRequestedEntryByIpAddr;
  128. (*
  129. Add an ARP entry to the requested list
  130. *)
  131. PROCEDURE AddRequestedEntry(cache: ArpCache; entry: ArpEntry);
  132. BEGIN
  133. entry.requested := TRUE;
  134. entry.prevReq := NIL;
  135. IF cache.requested = NIL THEN
  136. entry.nextReq := NIL;
  137. cache.requested := entry;
  138. ELSE
  139. entry.nextReq := cache.requested;
  140. cache.requested.prevReq := entry;
  141. cache.requested := entry;
  142. END;
  143. END AddRequestedEntry;
  144. (*
  145. Remove an ARP entry from the requested list
  146. *)
  147. PROCEDURE RemoveRequestedEntry(cache: ArpCache; entry: ArpEntry);
  148. BEGIN
  149. entry.requested := FALSE;
  150. IF entry.prevReq # NIL THEN entry.prevReq.nextReq := entry.nextReq;
  151. ELSIF entry = cache.requested THEN cache.requested := entry.nextReq; END;
  152. IF entry.nextReq # NIL THEN entry.nextReq.prevReq := entry.prevReq; END;
  153. entry.nextReq := NIL;
  154. entry.prevReq := NIL;
  155. END RemoveRequestedEntry;
  156. (*
  157. Make an ARP announcement on a given network interface
  158. *)
  159. PROCEDURE ArpAnnouncement(intf: EnetBase.Interface; VAR res: Int): BOOLEAN;
  160. VAR packet: EnetBase.Packet;
  161. BEGIN
  162. IF ~Interfaces.GetTxPacket(intf,packet) THEN
  163. res := EnetBase.ErrTxPacketPoolEmpty;
  164. RETURN FALSE;
  165. END;
  166. (* setup Ethernet frame header *)
  167. packet.ethFrameHdr.dstMacAddr := EnetBase.BroadcastMacAddr;
  168. packet.ethFrameHdr.srcMacAddr := intf.dev.macAddr;
  169. packet.ethFrameHdr.etherType := EnetBase.EtherTypeArp;
  170. (* setup ARP header *)
  171. packet.arpHdr.hwType := ArpHwTypeEth;
  172. packet.arpHdr.protoType := EnetBase.EtherTypeIpv4;
  173. packet.arpHdr.hwAddrLen := 6;
  174. packet.arpHdr.protoAddrLen := 4;
  175. packet.arpHdr.operation := ArpOpRequest;
  176. packet.arpHdr.srcMacAddr := intf.dev.macAddr;
  177. packet.arpHdr.srcIpAddr := intf.ipv4Addr.addr[0];
  178. packet.arpHdr.dstMacAddr := EnetBase.BroadcastMacAddr;
  179. packet.arpHdr.dstIpAddr := intf.ipv4Addr.addr[0];
  180. packet.dataLen := SIZEOF(EnetBase.EthFrameHdr) + SIZEOF(EnetBase.ArpHdr);
  181. RETURN Interfaces.SendPacket(intf,packet,{},NIL,res);
  182. END ArpAnnouncement;
  183. (**
  184. Send an ARP request for resolving a given IP address
  185. *)
  186. PROCEDURE ArpRequest(intf: EnetBase.Interface; CONST ipv4AddrToResolve: Int32; VAR res: Int): BOOLEAN;
  187. VAR
  188. packet: EnetBase.Packet;
  189. BEGIN
  190. IF ~Interfaces.GetTxPacket(intf,packet) THEN
  191. res := EnetBase.ErrTxPacketPoolEmpty;
  192. RETURN FALSE;
  193. END;
  194. (* setup Ethernet frame header *)
  195. packet.ethFrameHdr.dstMacAddr := EnetBase.BroadcastMacAddr;
  196. packet.ethFrameHdr.srcMacAddr := intf.dev.macAddr;
  197. packet.ethFrameHdr.etherType := EnetBase.EtherTypeArp;
  198. (* setup ARP header *)
  199. packet.arpHdr.hwType := ArpHwTypeEth;
  200. packet.arpHdr.protoType := EnetBase.EtherTypeIpv4;
  201. packet.arpHdr.hwAddrLen := 6;
  202. packet.arpHdr.protoAddrLen := 4;
  203. packet.arpHdr.operation := ArpOpRequest;
  204. packet.arpHdr.srcMacAddr := intf.dev.macAddr;
  205. packet.arpHdr.srcIpAddr := intf.ipv4Addr.addr[0];
  206. packet.arpHdr.dstMacAddr := EnetBase.NilMacAddr;
  207. packet.arpHdr.dstIpAddr := ipv4AddrToResolve;
  208. packet.dataLen := SIZEOF(EnetBase.EthFrameHdr) + SIZEOF(EnetBase.ArpHdr);
  209. RETURN Interfaces.SendPacket(intf,packet,{},NIL,res);
  210. END ArpRequest;
  211. (*
  212. Handle an ARP packet
  213. *)
  214. PROCEDURE HandleArpPacket(intf: EnetBase.Interface; packet: EnetBase.Packet; flags: SET);
  215. VAR
  216. cache: ArpCache;
  217. entry: ArpEntry;
  218. srcIpAddr: Int32;
  219. res: Int;
  220. completionHandler, timeoutHandler: EnetBase.TaskHandler;
  221. BEGIN
  222. IF (packet.arpHdr.hwType = ArpHwTypeEth) & (packet.arpHdr.protoType = EnetBase.EtherTypeIpv4) & (packet.arpHdr.dstIpAddr = intf.ipv4Addr.addr[0]) THEN
  223. IF packet.arpHdr.operation = ArpOpRequest THEN
  224. packet.arpHdr.operation := ArpOpResponse;
  225. packet.arpHdr.dstMacAddr := packet.arpHdr.srcMacAddr;
  226. packet.arpHdr.dstIpAddr := packet.arpHdr.srcIpAddr;
  227. packet.arpHdr.srcMacAddr := intf.dev.macAddr;
  228. packet.arpHdr.srcIpAddr := intf.ipv4Addr.addr[0];
  229. IF ~Interfaces.ReplyEth(intf,packet,{},NIL,res) THEN
  230. END;
  231. ELSIF packet.arpHdr.operation = ArpOpResponse THEN
  232. cache := intf.ipv4AddrCache(ArpCache);
  233. IF EnetBase.ThreadSafe THEN cache.acquireWrite; END;
  234. (* find the corresponding ARP entry request *)
  235. srcIpAddr := packet.arpHdr.srcIpAddr;
  236. entry := FindRequestedEntryByIpAddr(cache,srcIpAddr,TRUE);
  237. IF entry # NIL THEN (*! account for cases when the requested entry was removed earlier due to timeout expiration *)
  238. entry.macAddr := packet.arpHdr.srcMacAddr;
  239. entry.timestamp := EnetTiming.getTimeCounter();
  240. entry.resolved := TRUE;
  241. EnetBase.RemoveTask(intf,entry.timeoutHandler); (* remove timeout handler from the interface non-periodic task list *)
  242. completionHandler := entry.completionHandler;
  243. entry.completionHandler := NIL;
  244. END;
  245. IF EnetBase.ThreadSafe THEN cache.releaseWrite; END;
  246. (* notify the user about the operation completion *)
  247. IF completionHandler # NIL THEN
  248. completionHandler.res := 0;
  249. IF completionHandler.handle # NIL THEN
  250. completionHandler.handle(completionHandler);
  251. END;
  252. END;
  253. END;
  254. END;
  255. END HandleArpPacket;
  256. (*
  257. Request resolution of a new ARP cache entry
  258. *)
  259. PROCEDURE ArpEntryRequest(
  260. intf: EnetBase.Interface;
  261. cache: ArpCache;
  262. entry: ArpEntry;
  263. completionHandler: EnetBase.TaskHandler;
  264. handleTimeout: BOOLEAN;
  265. VAR res: Int): BOOLEAN;
  266. VAR
  267. i: Int;
  268. b: BOOLEAN;
  269. BEGIN
  270. IF ArpRequest(intf,entry.ipAddr.addr[0],res) THEN
  271. AddRequestedEntry(cache,entry); (* add the requested entry to the linked list of requested entries *)
  272. entry.completionHandler := completionHandler;
  273. IF handleTimeout THEN
  274. entry.timeoutHandler.handle := HandleArpEntryRequestTimeout;
  275. entry.timeoutHandler.param := entry;
  276. EnetBase.ScheduleTask(intf,entry.timeoutHandler,FALSE,entryRequestTimeout);
  277. END;
  278. res := EnetBase.OpInProgress;
  279. RETURN TRUE;
  280. ELSE
  281. RETURN FALSE;
  282. END;
  283. END ArpEntryRequest;
  284. (*
  285. Resolve IPv4 address, thread-safe
  286. *)
  287. PROCEDURE ResolveIpv4Addr(intf: EnetBase.Interface; CONST ipAddr: EnetBase.IpAddr; VAR macAddr: EnetBase.MacAddr; completionHandler: EnetBase.TaskHandler; VAR res: Int): BOOLEAN;
  288. VAR
  289. cache: ArpCache;
  290. entry: ArpEntry;
  291. b: BOOLEAN;
  292. BEGIN
  293. ASSERT(ipAddr.ver = 4);
  294. (* Broadcast address is always resolved to broadcast MAC *)
  295. IF ipAddr = EnetBase.BroadcastIpAddr THEN
  296. macAddr := EnetBase.BroadcastMacAddr;
  297. res := 0;
  298. IF (completionHandler # NIL) & (completionHandler.handle # NIL) THEN
  299. completionHandler.res := 0;
  300. EnetBase.ScheduleTask(intf, completionHandler, FALSE, 1)
  301. END;
  302. RETURN TRUE
  303. END;
  304. cache := intf.ipv4AddrCache(ArpCache);
  305. IF EnetBase.ThreadSafe THEN cache.acquireRead; END;
  306. entry := FindResolvedEntryByIpAddr(cache,ipAddr.addr[0]);
  307. IF entry # NIL THEN
  308. macAddr := entry.macAddr;
  309. IF EnetBase.ThreadSafe THEN cache.releaseRead; END;
  310. res := 0;
  311. b := TRUE;
  312. IF (completionHandler # NIL) & (completionHandler.handle # NIL) THEN
  313. completionHandler.res := 0;
  314. EnetBase.ScheduleTask(intf, completionHandler, FALSE, 1)
  315. END;
  316. ELSE
  317. IF EnetBase.ThreadSafe THEN cache.releaseRead; END;
  318. IF completionHandler # NIL THEN
  319. completionHandler.res := EnetBase.OpInProgress;
  320. IF EnetBase.ThreadSafe THEN cache.acquireWrite; END;
  321. entry := FindRequestedEntryByIpAddr(cache,ipAddr.addr[0],FALSE);
  322. IF entry # NIL THEN (*! the specified IP address is being already in resolution *)
  323. EnetBase.LinkTaskHandlers(entry.completionHandler,completionHandler);
  324. ELSE (* send an ARP request *)
  325. entry := GetFreeEntry(cache,ipAddr.addr[0]);
  326. ASSERT(entry # NIL);
  327. b := ArpEntryRequest(intf,cache,entry,completionHandler,TRUE,res);
  328. END;
  329. IF EnetBase.ThreadSafe THEN cache.releaseWrite; END;
  330. ELSE
  331. b := FALSE;
  332. res := EnetBase.ErrUnresolvedAddr;
  333. END;
  334. END;
  335. RETURN b;
  336. END ResolveIpv4Addr;
  337. PROCEDURE CleanArpCache(cache: EnetBase.IpAddrCache; cleanStatic: BOOLEAN);
  338. VAR
  339. i: Int;
  340. entry: ArpEntry;
  341. BEGIN
  342. WITH cache : ArpCache DO
  343. IF EnetBase.ThreadSafe THEN cache.acquireWrite; END;
  344. FOR i := 0 TO ArpCacheSize-1 DO
  345. entry := cache.entries[i];
  346. IF ~entry.requested & (cleanStatic OR ~entry.static) THEN
  347. entry.resolved := FALSE;
  348. END;
  349. END;
  350. IF EnetBase.ThreadSafe THEN cache.releaseWrite; END;
  351. END;
  352. END CleanArpCache;
  353. (*
  354. Add a static entry to the address resolution table
  355. *)
  356. PROCEDURE AddStaticEntry(cache: EnetBase.IpAddrCache; CONST ipAddr: EnetBase.IpAddr; CONST macAddr: EnetBase.MacAddr; VAR res: Int): BOOLEAN;
  357. VAR
  358. i: Int;
  359. entry: ArpEntry;
  360. BEGIN
  361. ASSERT(ipAddr.ver = 4);
  362. WITH cache : ArpCache DO
  363. IF IsSubnetMaskC(cache.intf.ipv4SubnetMask) THEN
  364. IF EnetBase.ThreadSafe THEN cache.acquireWrite; END;
  365. i := (ipAddr.addr[0] DIV 1000000H) MOD 100H; (* the most significant byte *)
  366. entry := cache.entries[i];
  367. entry.ipAddr := ipAddr;
  368. entry.macAddr := macAddr;
  369. entry.static := TRUE;
  370. entry.resolved := TRUE;
  371. entry.requested := FALSE;
  372. entry.timestamp := 0;
  373. entry.prevReq := NIL;
  374. entry.nextReq := NIL;
  375. IF EnetBase.ThreadSafe THEN cache.releaseWrite; END;
  376. res := 0;
  377. RETURN TRUE;
  378. ELSE
  379. HALT(100);
  380. END;
  381. END;
  382. END AddStaticEntry;
  383. (*
  384. Enumerate all resolved entries of the address resolution table
  385. *)
  386. PROCEDURE EnumerateEntries(cache: EnetBase.IpAddrCache; enumerator: PROCEDURE{DELEGATE}(entry: EnetBase.IpAddrCacheEntry));
  387. VAR
  388. i: Int;
  389. entry: ArpEntry;
  390. BEGIN
  391. ASSERT(enumerator # NIL);
  392. WITH cache : ArpCache DO
  393. IF EnetBase.ThreadSafe THEN cache.acquireRead; END;
  394. FOR i := 0 TO ArpCacheSize-1 DO
  395. entry := cache.entries[i];
  396. IF entry.resolved THEN
  397. enumerator(entry);
  398. END;
  399. END;
  400. IF EnetBase.ThreadSafe THEN cache.releaseRead; END;
  401. END;
  402. END EnumerateEntries;
  403. PROCEDURE ShowEntry(entry: ArpEntry);
  404. VAR j: Int;
  405. BEGIN
  406. Trace.StringLn("ipv4Addr=0x" & Trace.Hx(entry.ipAddr.addr[0],8));
  407. Trace.String("macAddr=" & Trace.Hx(entry.macAddr.addr[0],2));
  408. FOR j := 1 TO LEN(entry.macAddr.addr)-1 DO
  409. Trace.String(":" & Trace.Hx(entry.macAddr.addr[j],2));
  410. END;
  411. Trace.StringLn("");
  412. Trace.StringLn("static=" & entry.static);
  413. Trace.StringLn("resolved=" & entry.resolved);
  414. Trace.StringLn("requested=" & entry.requested);
  415. Trace.StringLn("timestamp=" & entry.timestamp);
  416. END ShowEntry;
  417. PROCEDURE ShowArpCache*(cache: ArpCache);
  418. VAR
  419. i, j, n: Int;
  420. entry: ArpEntry;
  421. BEGIN
  422. IF EnetBase.ThreadSafe THEN cache.acquireRead; END;
  423. Trace.StringLn("resolved entries: ");
  424. n := 0;
  425. FOR i := 0 TO LEN(cache.entries)-1 DO
  426. entry := cache.entries[i];
  427. IF entry.resolved THEN
  428. Trace.StringLn("#" & n);
  429. ShowEntry(entry);
  430. INC(n);
  431. END;
  432. END;
  433. IF n = 0 THEN
  434. Trace.StringLn("none");
  435. END;
  436. Trace.StringLn("requested entries: ");
  437. n := 0;
  438. entry := cache.requested;
  439. WHILE entry # NIL DO
  440. Trace.StringLn("#" & n);
  441. ShowEntry(entry);
  442. INC(n);
  443. entry := entry.nextReq;
  444. END;
  445. IF EnetBase.ThreadSafe THEN cache.releaseRead; END;
  446. IF n = 0 THEN
  447. Trace.StringLn("none");
  448. END;
  449. END ShowArpCache;
  450. PROCEDURE HandleArpEntryRequestTimeout(handler: EnetBase.TaskHandler);
  451. VAR
  452. intf: EnetBase.Interface;
  453. cache: ArpCache;
  454. requested: ArpEntry;
  455. completionHandler, tmp: EnetBase.TaskHandler;
  456. BEGIN
  457. requested := handler.param(ArpEntry);
  458. cache := requested.cache;
  459. intf := cache.intf;
  460. IF EnetBase.ThreadSafe THEN cache.acquireWrite; END;
  461. completionHandler := requested.completionHandler;
  462. RemoveRequestedEntry(cache,requested);
  463. IF EnetBase.ThreadSafe THEN cache.releaseWrite; END;
  464. (* inform the user about timeout expiration *)
  465. WHILE completionHandler # NIL DO
  466. completionHandler.res := EnetBase.ErrTimeoutExpired;
  467. IF completionHandler.handle # NIL THEN
  468. completionHandler.handle(completionHandler);
  469. END;
  470. tmp := completionHandler.next;
  471. completionHandler.next := NIL; (*! unlink handlers *)
  472. completionHandler := tmp;
  473. END;
  474. END HandleArpEntryRequestTimeout;
  475. PROCEDURE Update(handler: EnetBase.TaskHandler);
  476. VAR
  477. intf: EnetBase.Interface;
  478. cache: ArpCache;
  479. entry: ArpEntry;
  480. t: EnetTiming.Time;
  481. i, res: Int;
  482. requestedEntryUpdate: BOOLEAN;
  483. BEGIN
  484. intf := handler.param(EnetBase.Interface);
  485. cache := intf.ipv4AddrCache(ArpCache);
  486. IF EnetBase.ThreadSafe THEN cache.acquireWrite; END;
  487. (*
  488. update expired dynamic entries
  489. *)
  490. t := EnetTiming.getTimeCounter();
  491. requestedEntryUpdate := FALSE;
  492. FOR i := 0 TO ArpCacheSize-1 DO
  493. entry := cache.entries[i];
  494. IF ~requestedEntryUpdate & ~entry.static & entry.resolved & ~entry.requested & (t - entry.timestamp >= entryExpireTimeout) THEN
  495. IF ArpEntryRequest(intf,cache,entry,NIL,FALSE,res) THEN (*! do not schedule timeout task - no task scheduling is allowed in a task handler *)
  496. requestedEntryUpdate := TRUE; (*! request updating of one entry at a time to avoid ARP message storm *)
  497. Trace.StringLn("requested update of an ARP entry: ");
  498. ShowEntry(entry);
  499. ELSE
  500. entry.resolved := FALSE;
  501. ASSERT(~entry.requested);
  502. END;
  503. ELSIF ~entry.static & entry.resolved & entry.requested & (t - entry.timestamp >= entryRequestTimeout) THEN
  504. RemoveRequestedEntry(cache,entry);
  505. entry.resolved := FALSE;
  506. END;
  507. END;
  508. IF EnetBase.ThreadSafe THEN cache.releaseWrite; END;
  509. END Update;
  510. VAR
  511. entryRequestTimeout: EnetTiming.Time;
  512. entryExpireTimeout: EnetTiming.Time;
  513. PROCEDURE Install*(intf: EnetBase.Interface);
  514. VAR
  515. cache: ArpCache;
  516. updateTask: EnetBase.TaskHandler;
  517. BEGIN
  518. IF entryRequestTimeout = 0 THEN
  519. entryRequestTimeout := EnetTiming.fromMilli(ArpEntryRequestTimeoutMs);
  520. entryExpireTimeout := EnetTiming.fromMilli(ArpEntryExpireTimeoutMs);
  521. END;
  522. NEW(cache);
  523. InitArpCache(cache,intf);
  524. intf.ipv4AddrResolve := ResolveIpv4Addr;
  525. intf.ipv4AddrCache := cache;
  526. EnetBase.SetEthFrameHandler(intf,EnetBase.EtherTypeArp,HandleArpPacket);
  527. NEW(updateTask);
  528. updateTask.res := 0;
  529. updateTask.handle := Update;
  530. updateTask.param := intf;
  531. EnetBase.ScheduleTask(intf,updateTask,TRUE,EnetTiming.fromMilli(UpdateIntervalMs));
  532. END Install;
  533. END EnetArp.