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