PageRenderTime 65ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/verify/src/Checked/Libraries/NetStack2/ARP.cs

#
C# | 707 lines | 510 code | 114 blank | 83 comment | 58 complexity | a719b2f10267126708972747d3d3daed MD5 | raw file
  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Microsoft Research Singularity
  4. //
  5. // Copyright (c) Microsoft Corporation. All rights reserved.
  6. //
  7. // File: ARP.sg
  8. //
  9. // Note: are fun
  10. //
  11. //#define DEBUG_ARP
  12. using System;
  13. using System.Diagnostics;
  14. using System.Collections;
  15. using System.Text;
  16. //using System.SchedulerTime;
  17. using System.Threading;
  18. using System.Net.IP;
  19. using Drivers.Net;
  20. using Microsoft.Singularity.NetStack.Protocols;
  21. using Microsoft.Singularity.Channels;
  22. using Microsoft.Singularity;
  23. using Microsoft.Singularity.V1.Services;
  24. using Microsoft.SingSharp;
  25. //using Allocation = SharedHeapService.Allocation;
  26. using SchedulerTime = System.DateTime;
  27. namespace Microsoft.Singularity.NetStack.Protocols{}
  28. namespace Microsoft.Singularity.NetStack2
  29. {
  30. public class ArpHeader
  31. {
  32. public ushort htype;
  33. public ushort pad0;
  34. public ushort ptype;
  35. public byte hlen;
  36. public byte plen;
  37. public ushort op;
  38. public ushort pad1;
  39. public EthernetAddress senderEthernetAddr;
  40. public IPv4 senderIPAddr;
  41. public EthernetAddress destEthernetAddr;
  42. public IPv4 destIPAddr;
  43. public static int Size = 28;
  44. public static ushort ARP_REQUEST = 1;
  45. public static ushort ARP_REPLY = 2;
  46. public ArpHeader(Bytes packet, ushort index)
  47. {
  48. //since this is already known to be an arp request
  49. //we skip the sanity checks...
  50. VTable.Assert(packet.Length - index >= 96);
  51. // check hardware type == 0x0001 (Ethernet)
  52. htype = NetworkBitConverter.ToUInt16(packet, index);
  53. DebugStub.Assert(htype == 0x001);
  54. index +=2;
  55. // check protocol type == 0x0800 (IP)
  56. ptype = NetworkBitConverter.ToUInt16(packet, index);
  57. DebugStub.Assert(ptype == 0x0800);
  58. index += 2;
  59. // check hardware address len is 6 bytes
  60. hlen = packet[index++];
  61. DebugStub.Assert(hlen == 6);
  62. // check IP address len is 4 bytes
  63. plen = packet[index++];
  64. DebugStub.Assert(plen == 4);
  65. op = NetworkBitConverter.ToUInt16(packet, index);
  66. index += 2;
  67. senderEthernetAddr = EthernetAddress.ParseBytes(packet.Array, packet.Start + index);
  68. index += EthernetAddress.Length;
  69. uint addr = NetworkBitConverter.ToUInt32(packet, index);
  70. index += 4;
  71. senderIPAddr = new IPv4(addr);
  72. destEthernetAddr = EthernetAddress.ParseBytes(packet.Array, packet.Start + index);
  73. index += EthernetAddress.Length;
  74. addr = NetworkBitConverter.ToUInt32(packet, index);
  75. index += 4;
  76. destIPAddr = new IPv4(addr);
  77. //sgc complains
  78. pad0 = 0;
  79. pad1 = 0;
  80. }
  81. public static int Write(Bytes pkt,
  82. EthernetAddress srchw,
  83. IPv4 srcip,
  84. ushort operation,
  85. EthernetAddress targethw,
  86. IPv4 targetip)
  87. {
  88. int o = 0;
  89. pkt[o++] = 0x00; pkt[o++] = 0x01; // hardware type = 0x0001
  90. pkt[o++] = 0x08; pkt[o++] = 0x00; // protocol type = 0x0800
  91. pkt[o++] = 0x06; // hardware addr len (bytes)
  92. pkt[o++] = 0x04; // protocol address len (bytes)
  93. pkt[o++] = (byte) (operation >> 8);
  94. pkt[o++] = (byte) (operation & 0xff);
  95. srchw.CopyOut(pkt.Array, pkt.Start + o);
  96. o += EthernetAddress.Length;
  97. srcip.CopyOut(pkt.Array, pkt.Start + o);
  98. o += IPv4.Length;
  99. targethw.CopyOut(pkt.Array, pkt.Start + o);
  100. o += EthernetAddress.Length;
  101. targetip.CopyOut(pkt.Array, pkt.Start + o);
  102. o += IPv4.Length;
  103. return o;
  104. }
  105. }
  106. public class ARP: IThreadStart
  107. {
  108. // ARP variables
  109. private ArpTable arpTable = null;
  110. public static int ARP_REQUEST = 1;
  111. public static int ARP_REPLY = 2;
  112. //this is a soft limit
  113. private const int DefaultMaxPendingArpRequests = 256;
  114. private AutoResetEvent arpHandle;
  115. private Queue pendingRequestsFreelist;
  116. private Queue pendingRequests;
  117. private MonitorLock pendingRequestsLock = new MonitorLock();
  118. // Pending request polling period
  119. private static readonly TimeSpan PollPeriod = TimeSpan.FromSeconds(1);
  120. [Conditional("DEBUG_ARP")]
  121. public static void DebugPrint(string format,
  122. params object [] arguments)
  123. {
  124. DebugStub.Print("ARP: {0}",
  125. DebugStub.ArgList(
  126. string.Format(format, arguments))
  127. );
  128. }
  129. private class PendingArpRequest
  130. {
  131. public IPv4 address;
  132. public SchedulerTime requestExpiration;
  133. public AutoResetEvent requestEvent;
  134. public EthernetAddress localMac;
  135. public bool active;
  136. public IAdapter adapter;
  137. public TContainerVectorQueueByte txContainer;
  138. public PendingArpRequest()
  139. {
  140. this.requestEvent = new AutoResetEvent(false);
  141. this.active = false;
  142. txContainer = new TContainerVectorQueueByte(
  143. new VectorQueueByte()
  144. );
  145. }
  146. }
  147. private void ARPManageThread()
  148. {
  149. DebugPrint("ARP managment thread spinning up\n");
  150. //the pending requests list is ordered by deadline...
  151. //finding a requests when we rceive a reply is in the worst case O(n)...
  152. //Hopefully we won't have many oustanding requests, and the first request out
  153. //will often return first...
  154. //track two different timeouts here...ARP requests and ARP table aging.
  155. SchedulerTime ageTableTimeout;
  156. SchedulerTime now;
  157. SchedulerTime nextTimer;
  158. ageTableTimeout = SchedulerTime.Now;
  159. ageTableTimeout = ageTableTimeout.AddMinutes(5);
  160. while(true) {
  161. now = SchedulerTime.Now;
  162. if (now > ageTableTimeout) {
  163. arpTable.AgeTable();
  164. ageTableTimeout = SchedulerTime.Now;
  165. ageTableTimeout = ageTableTimeout.AddMinutes(5);
  166. }
  167. using (pendingRequestsLock.Lock()) {
  168. nextTimer = SchedulerTime.MaxValue;
  169. bool done = false;
  170. while (!done) {
  171. if (pendingRequests.Count == 0) {
  172. done = true;
  173. continue;
  174. }
  175. PendingArpRequest pendingArpRequest = (PendingArpRequest) pendingRequests.Peek();
  176. if (pendingArpRequest == null) {
  177. done = true;
  178. continue;
  179. }
  180. if (pendingArpRequest.requestExpiration > now) {
  181. nextTimer = pendingArpRequest.requestExpiration;
  182. done = true;
  183. continue;
  184. }
  185. else {
  186. pendingArpRequest = (PendingArpRequest) pendingRequests.Dequeue();
  187. if (pendingArpRequest.active == true) {
  188. //We need error propagation here...
  189. pendingArpRequest.active = false;
  190. DebugStub.Assert(false);
  191. }
  192. pendingRequestsFreelist.Enqueue(pendingArpRequest);
  193. }
  194. }
  195. }
  196. if (ageTableTimeout < nextTimer) {
  197. DebugPrint("setting nextTimer ageTableTimeout\n");
  198. nextTimer = ageTableTimeout;
  199. }
  200. bool rc;
  201. rc = arpHandle.WaitOne(nextTimer);
  202. }
  203. }
  204. [Microsoft.Contracts.NotDelayed]
  205. public ARP()
  206. {
  207. DebugPrint("Initializing ARP module\n");
  208. arpTable = new ArpTable(this);
  209. pendingRequests = new Queue();
  210. pendingRequestsFreelist = new Queue();
  211. //start the array list with 256 elements to
  212. for(int i = 0; i < DefaultMaxPendingArpRequests; i++) {
  213. PendingArpRequest pendingArpRequest =
  214. new PendingArpRequest();
  215. pendingRequestsFreelist.Enqueue(pendingArpRequest);
  216. }
  217. arpHandle = new AutoResetEvent(false);
  218. Thread arpThread = new Thread(this);
  219. DebugStub.Assert(arpThread != null);
  220. arpThread.Start();
  221. }
  222. public void Run()
  223. {
  224. System.DebugStub.Print("ARP@" + Kernel.CurrentThread + ". ");
  225. ARPManageThread();
  226. }
  227. // Original note from Yaron:
  228. // ARP logic: see RFC 826 http://www.faqs.org/rfcs/rfc826.html
  229. //
  230. public void ProcessIncomingPacket(Bytes packet, IAdapter adapter)
  231. {
  232. //Get the ARP packet info located after the ethernet header
  233. ArpHeader arpHeader = new ArpHeader(packet, 14);
  234. DebugPrint("ARP: ProcessIncomingPacket\n");
  235. //do some checks to make sure the packet is copacetic
  236. if (arpHeader.htype != 0x1) {
  237. DebugPrint("ARP: ProcessIncomingPacket got wrong hardware type? 0x{0,8:x}\n",
  238. arpHeader.htype);
  239. //delete packet;
  240. return;
  241. }
  242. if (arpHeader.ptype != 0x0800) {
  243. DebugPrint("ARP: ProcessIncomingPacket got wrong protocol? 0x{0,8:x}\n",
  244. arpHeader.ptype);
  245. //delete packet;
  246. return;
  247. }
  248. //ethernet address should be 6 bytes
  249. if (arpHeader.hlen != 6) {
  250. DebugPrint("ARP: ProcessIncomingPacket got wrong hw length? 0x{0,8:x}\n",
  251. arpHeader.hlen);
  252. //delete packet;
  253. return;
  254. }
  255. if (arpHeader.plen != 4) {
  256. DebugPrint("ARP: ProcessIncomingPacket got wrong protocol address length? 0x{0,8:x}\n",
  257. arpHeader.plen);
  258. //delete packet;
  259. return;
  260. }
  261. DebugPrint("Incoming packet\n");
  262. bool merged = false;
  263. bool updated = false;
  264. ArpEntry target = arpTable.Lookup(arpHeader.senderIPAddr);
  265. if (target != null && target.Dynamic == true) {
  266. DebugPrint("ARP UPDATE\n");
  267. // we have it already - just update the details...
  268. target.MacAddress = arpHeader.senderEthernetAddr;
  269. target.EntryAge = arpTable.Age;
  270. merged = true;
  271. updated = true;
  272. }
  273. if (merged == false) {
  274. DebugPrint("ARP ADDITION\n");
  275. arpTable.AddEntry(new ArpEntry(arpHeader.senderIPAddr,
  276. arpHeader.senderEthernetAddr, true));
  277. merged = true;
  278. UpdatePendingRequests(arpHeader.senderIPAddr, arpHeader.senderEthernetAddr);
  279. }
  280. //Is this a local address
  281. bool forSelf = IP.IsLocalAddress(arpHeader.destIPAddr);
  282. if (forSelf == false) {
  283. //delete packet;
  284. return;
  285. }
  286. // now figure out the opcode
  287. if (arpHeader.op == ARP_REQUEST) {
  288. DebugPrint("Handling request ({0},{1}) ---> ({2},{3} \npkt dest {4} {5})\n",
  289. arpHeader.senderIPAddr, arpHeader.senderEthernetAddr,
  290. arpHeader.destIPAddr, adapter.HardwareAddress,
  291. arpHeader.destIPAddr, arpHeader.destEthernetAddr
  292. );
  293. int dataLength = EthernetHeader.Size + ArpHeader.Size;
  294. VTable.Assert(packet.Length >= dataLength);
  295. Bytes data = Bitter.SplitOff(ref packet, EthernetHeader.Size);
  296. EthernetHeader.Write(packet,
  297. adapter.HardwareAddress,
  298. arpHeader.senderEthernetAddr,
  299. EthernetHeader.PROTOCOL_ARP);
  300. //use arp header to format reply
  301. ArpHeader.Write(data, adapter.HardwareAddress,
  302. arpHeader.destIPAddr, ArpHeader.ARP_REPLY,
  303. arpHeader.senderEthernetAddr, arpHeader.senderIPAddr);
  304. adapter.PopulateTxRing(packet, data);
  305. }
  306. else {
  307. // otherwise we are done
  308. DebugPrint(
  309. "Handling reply ({2},{3}) <--- ({0},{1})\n",
  310. arpHeader.senderIPAddr, arpHeader.senderEthernetAddr,
  311. arpHeader.destIPAddr, arpHeader.destEthernetAddr
  312. );
  313. //delete packet;
  314. }
  315. if (merged && !updated) {
  316. DebugPrint(arpTable.ToString());
  317. }
  318. }
  319. public void ArpRequest(IPv4 sourceIP,
  320. IPv4 targetIP,
  321. EthernetAddress localMac,
  322. Bytes header,
  323. Bytes buffer,
  324. IAdapter adapter)
  325. {
  326. // AutoResetEvent requestComplete =
  327. AddPendingRequest(targetIP, TimeSpan.FromSeconds(3), localMac, header, buffer, adapter);
  328. // initiate an arp request...
  329. DebugPrint("Initiating request " +
  330. "({0},{1}) --> ({2},{3})\n",
  331. sourceIP, localMac, targetIP, EthernetAddress.Zero);
  332. //eventially we'll want to follow Orion's conservation of
  333. //packets philosophy
  334. Bytes arpHeader = new Bytes(new byte [EthernetHeader.Size]);
  335. Bytes arpMsg = new Bytes(new byte [ArpHeader.Size]);
  336. //xxx I'd like to get rid of EthernetHeader eventually...
  337. EthernetHeader.Write(arpHeader,
  338. localMac,
  339. EthernetAddress.Broadcast,
  340. EthernetHeader.PROTOCOL_ARP);
  341. ArpHeader.Write(arpMsg, localMac, sourceIP,
  342. ArpHeader.ARP_REQUEST, EthernetAddress.Zero,
  343. targetIP);
  344. adapter.PopulateTxRing(arpHeader, arpMsg);
  345. // DebugPrint("ArpRequest: waiting for reply\n");
  346. //requestComplete.WaitOne();
  347. // DebugPrint("ArpRequest: reply received!\n");
  348. }
  349. public bool Lookup(IPv4 targetIP, out EthernetAddress macAddress)
  350. {
  351. return arpTable.Lookup(targetIP, out macAddress);
  352. }
  353. private AutoResetEvent AddPendingRequest(IPv4 address,
  354. TimeSpan timeout,
  355. EthernetAddress localMac,
  356. Bytes header,
  357. Bytes buffer,
  358. IAdapter adapter
  359. )
  360. {
  361. PendingArpRequest pendingRequest = (PendingArpRequest) pendingRequestsFreelist.Dequeue();
  362. VTable.Assert(pendingRequest != null);
  363. pendingRequest.address = address;
  364. pendingRequest.active = true;
  365. pendingRequest.localMac = localMac;
  366. pendingRequest.adapter = adapter;
  367. VectorQueueByte txBuffer = pendingRequest.txContainer.Acquire();
  368. txBuffer.AddTail(header);
  369. txBuffer.AddTail(buffer);
  370. pendingRequest.txContainer.Release(txBuffer);
  371. using (pendingRequestsLock.Lock()) {
  372. pendingRequests.Enqueue(pendingRequest);
  373. }
  374. SchedulerTime expiration = SchedulerTime.Now;
  375. expiration = expiration.Add(timeout);
  376. pendingRequest.requestExpiration = expiration;
  377. //poke the wait thread
  378. if (pendingRequests.Count == 1) {
  379. arpHandle.Set();
  380. }
  381. return pendingRequest.requestEvent;
  382. }
  383. private void UpdatePendingRequests(IPv4 ipAddress,
  384. EthernetAddress macAddress)
  385. {
  386. using (pendingRequestsLock.Lock()) {
  387. //Sigh...we're missing a linked list in the current Singularity C# runtime
  388. foreach (PendingArpRequest pendingRequest in pendingRequests) {
  389. VTable.Assert(pendingRequest != null);
  390. if (pendingRequest.address == ipAddress) {
  391. pendingRequest.active = false;
  392. DebugStub.WriteLine("found waiting arp request...sending on out");
  393. VectorQueueByte txBuffer = pendingRequest.txContainer.Acquire();
  394. Bytes header = txBuffer.ExtractHead();
  395. Bytes buffer = txBuffer.ExtractHead();
  396. VTable.Assert(header != null);
  397. VTable.Assert(buffer != null);
  398. pendingRequest.txContainer.Release(txBuffer);
  399. //Format ethernet header
  400. EthernetHeader.Write(header, pendingRequest.localMac, macAddress, EthernetHeader.PROTOCOL_IP);
  401. //send it!
  402. VTable.Assert(pendingRequest.adapter != null);
  403. pendingRequest.adapter.PopulateTxRing(header, buffer);
  404. continue;
  405. }
  406. }
  407. }
  408. }
  409. }
  410. // an arp entry (we only deal with IPv4)
  411. public class ArpEntry
  412. {
  413. private IPv4 ipAddress;
  414. private EthernetAddress mac;
  415. private int entryAge;
  416. private bool dynamic;
  417. public int EntryAge
  418. {
  419. get { return entryAge; }
  420. set { entryAge = value; }
  421. }
  422. public EthernetAddress MacAddress
  423. {
  424. get { return mac; }
  425. set { mac = value; }
  426. }
  427. public IPv4 IPAddress
  428. {
  429. get { return ipAddress; }
  430. }
  431. public bool Dynamic
  432. {
  433. get { return dynamic; }
  434. }
  435. // create a new entry
  436. public ArpEntry(IPv4 ipAddress, EthernetAddress mac, bool dynamic)
  437. {
  438. this.ipAddress = ipAddress;
  439. this.mac = mac;
  440. this.dynamic = dynamic;
  441. this.entryAge = ArpTable.MaxAge;
  442. }
  443. public override string ToString()
  444. {
  445. return String.Format("{0} {1} {2} {3}",
  446. ipAddress, mac, entryAge,
  447. dynamic ? "dynamic" : "static");
  448. }
  449. }
  450. // define the arp table
  451. internal class ArpTable
  452. {
  453. Hashtable arpEntries; // <Key = IPv4 address, Value = ArpEntry>
  454. // max table size
  455. protected readonly int maxEntries;
  456. // default entry age
  457. protected int defaultAge;
  458. // get the default age
  459. public int Age { get { return defaultAge; } }
  460. // the default age
  461. public const int MaxAge = 50;
  462. public const int defaultSize = 128;
  463. // aging timeout [msec]
  464. public static readonly TimeSpan AgePeriod = TimeSpan.FromMinutes(5);
  465. // our parent
  466. protected ARP arp;
  467. [Conditional("DEBUG_ARP")]
  468. internal static void DebugPrint(string format,
  469. params object [] arguments)
  470. {
  471. DebugStub.Print("ARP: {0}",
  472. DebugStub.ArgList(
  473. string.Format(format, arguments))
  474. );
  475. }
  476. [Conditional("DEBUG_ARP")]
  477. internal static void DebugPrint(string format)
  478. {
  479. DebugStub.Print("ARP: {0}",
  480. DebugStub.ArgList(format));
  481. }
  482. public ArpTable(int size, int age, ARP arp)
  483. {
  484. DebugPrint("creating ArpTable size={0}, age={1}\n",
  485. size, age);
  486. arpEntries = new Hashtable(size);
  487. maxEntries = size;
  488. defaultAge = age;
  489. this.arp = arp;
  490. }
  491. public ArpTable(ARP arp)
  492. {
  493. DebugPrint("creating ArpTable size={0}, age={1}\n",
  494. defaultSize, MaxAge);
  495. arpEntries = new Hashtable(defaultSize);
  496. maxEntries = defaultSize;
  497. defaultAge = MaxAge;
  498. this.arp = arp;
  499. }
  500. // add a new entry
  501. // return false if there is no more room
  502. public bool AddEntry(ArpEntry e)
  503. {
  504. // if no more room, make one
  505. if (arpEntries.Count >= maxEntries) {
  506. PurgeLRUEntry();
  507. }
  508. e.EntryAge = this.defaultAge;
  509. arpEntries.Add(e.IPAddress, e);
  510. DebugPrint("Added entry {0}\n", e);
  511. return true;
  512. }
  513. private void RemoveEntry(ArpEntry e)
  514. {
  515. arpEntries.Remove(e.IPAddress);
  516. DebugPrint("Removed entry for {0}\n", e);
  517. }
  518. public void RemoveEntry(IPv4 targetIP)
  519. {
  520. arpEntries.Remove(targetIP);
  521. DebugPrint("Removed entry for {0}\n");
  522. }
  523. // makes a room for a new entry, get rid of
  524. // the least recently used entry (LRU)
  525. public void PurgeLRUEntry()
  526. {
  527. if (arpEntries.Count == 0)
  528. return;
  529. // can use a LRU list to avoid O(n)
  530. // but this is a kind of a small table...
  531. IDictionaryEnumerator dicEnum = arpEntries.GetEnumerator();
  532. dicEnum.MoveNext(); // get the first entry
  533. ArpEntry lruElement = (ArpEntry)dicEnum.Value;
  534. while (dicEnum.MoveNext()) {
  535. ArpEntry current = (ArpEntry)dicEnum.Value;
  536. if (current.EntryAge < lruElement.EntryAge) {
  537. lruElement = current;
  538. }
  539. }
  540. RemoveEntry(lruElement);
  541. }
  542. // age the dynamic table entries, if age drops to 0 purge entry
  543. internal bool AgeTable()
  544. {
  545. int startCount = arpEntries.Count;
  546. // Can't hold iterator and add or remove items so use
  547. // list to hold items to be deleted.
  548. ArrayList purgeItems = new ArrayList();
  549. foreach (ArpEntry e in arpEntries.Values) {
  550. if (e.Dynamic == true && --e.EntryAge == 0) {
  551. purgeItems.Add(e);
  552. }
  553. }
  554. foreach (ArpEntry e in purgeItems) {
  555. RemoveEntry(e);
  556. }
  557. return startCount < arpEntries.Count;
  558. }
  559. public ArpEntry Lookup(IPv4 destination)
  560. {
  561. return arpEntries[destination] as ArpEntry;
  562. }
  563. // an upper layer interface to get the mac
  564. // to a target IP. The upper protocol must
  565. // provide the Mux for the target IP.
  566. // if we have it then we return true + macAddress
  567. // and refresh the age to create a LRU list
  568. public bool Lookup(IPv4 targetIP, out EthernetAddress macAddress)
  569. {
  570. ArpEntry e = arpEntries[targetIP] as ArpEntry;
  571. if (e != null) {
  572. e.EntryAge = Age;
  573. macAddress = e.MacAddress;
  574. return true;
  575. }
  576. macAddress = EthernetAddress.Zero;
  577. return false;
  578. }
  579. public override string ToString()
  580. {
  581. StringBuilder stringBuilder = new StringBuilder();
  582. stringBuilder.Append("Internet Address Physical Address Type \n");
  583. stringBuilder.Append("-----------------------------------------------------------\n");
  584. string [] types = { "static", "dynamic" };
  585. foreach (ArpEntry e in arpEntries.Values) {
  586. stringBuilder.Append(e.IPAddress + " " + e.MacAddress +
  587. " " + types[ e.Dynamic ? 1 : 0] +
  588. " [Age=" + e.EntryAge + "]\n");
  589. }
  590. return stringBuilder.ToString();
  591. }
  592. }
  593. }