示例#1
0
 /// <summary>
 /// Invoked when IP data is being delivered through the specified interface.
 /// </summary>
 /// <param name="ifc">The interface through which the data was received.</param>
 /// <param name="data">A sequence of bytes received.</param>
 /// <exception cref="ArgumentNullException">Thrown if either argument
 /// is null.</exception>
 void OnIpInput(Interface ifc, byte[] data)
 {
     ifc.ThrowIfNull("ifc");
     data.ThrowIfNull("data");
     WriteLine(ifc.FullName + " has received new IP packet.");
     try {
         var packet = IpPacket.Deserialize(data);
         // This method is called in "interrupt context" and can execute
         // on behalf of different NIC's simultaneously. The IP stack
         // is usually single threaded however, so incoming packets are queued
         // globally and processed sequentially.
         if (inputQueue.Count == 0)
         {
             Simulation.Callback(nodalProcessingDelay, ProcessPackets);
         }
         try {
             // Enqueue the packet and the interface through which it was
             // received.
             inputQueue.Enqueue(new Tuple <IpPacket, Interface>(packet, ifc));
         } catch (InvalidOperationException) {
             // If the host's input queue is full, we must drop the packet.
             WriteLine("IP input queue overflow, dropping packet.");
             // Send a "Source Quench" ICMP to the packet's originator.
             SendIcmp(ifc, packet.Source, IcmpPacket.SourceQuench(packet));
         }
     } catch (SerializationException) {
         WriteLine(ifc.FullName + " has detected a bad checksum, " +
                   "discarding IP packet.");
     }
 }
示例#2
0
        /// <summary>
        /// Fragments the specified packet into multiple packets taking into
        /// account the specified maximum transmission unit.
        /// </summary>
        /// <param name="packet">The packet to fragment.</param>
        /// <param name="Mtu">The maximum transmission unit; The maximum size,
        /// in bytes, each of the fragmented packets may have.</param>
        /// <returns>An enumerable collection of packet fragments.</returns>
        public IEnumerable <IpPacket> FragmentPacket(IpPacket packet, int Mtu)
        {
            // The maximum size of a segment is the MTU minus the IP header size.
            var maxSegmentSize = Mtu - 20;
            var numSegments    = (int)Math.Ceiling(packet.Data.Length /
                                                   (double)maxSegmentSize);
            var    list = new List <IpPacket>();
            ushort ident = (ushort)(Simulation.Time % 0xFFFF), offset = 0;

            for (var i = 0; i < numSegments; i++)
            {
                // Set MoreFragments flag for all but the last segment.
                var mf    = i < (numSegments - 1);
                var flags = mf ? (packet.Flags | IpFlag.MoreFragments) :
                            packet.Flags;
                var dataSize = Math.Min(maxSegmentSize,
                                        packet.Data.Length - offset * 8);
                var data = new byte[dataSize];
                // Add offset of original packet, as the original packet might be
                // a fragment itself.
                var packetOffset =
                    (ushort)(packet.FragmentOffset + offset);
                Array.Copy(packet.Data, offset * 8, data, 0, dataSize);
                var segment = new IpPacket(packet.Destination, packet.Source,
                                           packet.Protocol, packet.Ihl, packet.Dscp, packet.TimeToLive,
                                           ident, flags, packetOffset, data);
                offset = (ushort)(offset + (maxSegmentSize / 8));
                list.Add(segment);
            }
            return(list);
        }
示例#3
0
        /// <summary>
        /// Resolves the specified IPv4 destination address to a physical
        /// address and hands the specified IPv4 packet down to the link
        /// layer.
        /// </summary>
        /// <param name="ifc">The interface through which to output the
        /// data.</param>
        /// <param name="destination">The logical address of the destination
        /// host, which can different from the final destination address
        /// contained in the IP packet.</param>
        /// <param name="packet">The IPv4 packet to transmit.</param>
        /// <exception cref="ArgumentNullException">Thrown if any of the arguments
        /// is null.</exception>
        void Output(Interface ifc, IpAddress destination, IpPacket packet)
        {
            ifc.ThrowIfNull("ifc");
            destination.ThrowIfNull("destination");
            packet.ThrowIfNull("packet");
            // Translate IP address into MAC-Address.
            var macDestination = arp.Lookup(ifc, destination);

            // IP address is not in our ARP table.
            if (macDestination == null)
            {
                // Put packet on hold until the MAC-48 destination address has
                // been figured out.
                WaitingPacketsOf(ifc).Add(
                    new Tuple <IpAddress, IpPacket>(destination, packet));
                WriteLine(ifc.FullName + " is putting IP packet on-hold due to pending ARP request.");
                // Schedule a new ARP request.
                arp.Resolve(ifc, destination);
            }
            else
            {
                WriteLine(ifc.FullName + " is queueing IP packet for " + destination);
                Output(ifc, macDestination, packet);
            }
        }
示例#4
0
        /// <summary>
        /// Computes the 16-bit checksum of the specified IPv4 packet.
        /// </summary>
        /// <param name="packet">The packet to compute the checksum for.</param>
        /// <param name="withChecksumField">true to include the packet's
        /// checksum field in the calculation; otherwise false.</param>
        /// <returns>The checksum of the specified IPv4 packet.</returns>
        /// <exception cref="ArgumentNullException">Thrown if the packet
        /// argument is null.</exception>
        public static ushort ComputeChecksum(IpPacket packet,
                                             bool withChecksumField = false)
        {
            packet.ThrowIfNull("packet");
            // The version and IHL fields are 4 bit wide each.
            var vi = (byte)(((packet.Ihl & 0x0F) << 4) |
                            (((int)packet.Version) & 0x0F));
            // The flags field is 3 bits and the fragment offset 13 bits wide.
            var ffo = (ushort)(((packet.FragmentOffset & 0x1FFF) << 3) |
                               ((int)packet.Flags & 0x07));
            var bytes = new ByteBuilder()
                        .Append(vi)
                        .Append(packet.Dscp)
                        .Append(packet.TotalLength)
                        .Append(packet.Identification)
                        .Append(ffo)
                        .Append(packet.TimeToLive)
                        .Append((byte)packet.Protocol)
                        .Append(withChecksumField ? packet.Checksum : (ushort)0)
                        .Append(packet.Source.Bytes)
                        .Append(packet.Destination.Bytes)
                        .ToArray();
            var sum = 0;

            // Treat the header bytes as a sequence of unsigned 16-bit values and
            // sum them up.
            for (var n = 0; n < bytes.Length; n += 2)
            {
                sum += BitConverter.ToUInt16(bytes, n);
            }
            // Use carries to compute the 1's complement sum.
            sum = (sum >> 16) + (sum & 0xFFFF);
            // Return the inverted 16-bit result.
            return((ushort)(~sum));
        }
示例#5
0
        /// <summary>
        /// Deserializes an IpPacket instance from the specified sequence of
        /// bytes.
        /// </summary>
        /// <param name="data">The sequence of bytes to deserialize an IpPacket
        /// object from.</param>
        /// <returns>A deserialized IpPacket object.</returns>
        /// <exception cref="ArgumentNullException">Thrown if the data argument is
        /// null.</exception>
        /// <exception cref="SerializationException">Thrown if the IP packet could
        /// not be deserialized from the specified byte array.</exception>
        public static IpPacket Deserialize(byte[] data)
        {
            data.ThrowIfNull("data");
            using (var ms = new MemoryStream(data)) {
                using (var reader = new BinaryReader(ms)) {
                    var    vi = reader.ReadByte();
                    var    version = (IpVersion)(vi & 0x0F);
                    byte   ihl = (byte)(vi >> 4), dscp = reader.ReadByte();
                    ushort totalLength = reader.ReadUInt16(),
                           identification = reader.ReadUInt16(), ffo = reader.ReadUInt16();
                    var       flags          = (IpFlag)(ffo & 0x07);
                    var       fragmentOffset = (ushort)(ffo >> 3);
                    var       ttl            = reader.ReadByte();
                    var       type           = (IpProtocol)reader.ReadByte();
                    var       checksum       = reader.ReadUInt16();
                    IpAddress src            = new IpAddress(reader.ReadBytes(4)),
                              dst            = new IpAddress(reader.ReadBytes(4));

                    var packet = new IpPacket(version, ihl, dscp, totalLength,
                                              identification, flags, fragmentOffset, ttl, type, checksum,
                                              src, dst);
                    // Computing the checksum should yield a value of 0 unless errors are
                    // detected.
                    if (ComputeChecksum(packet, true) != 0)
                    {
                        throw new SerializationException("The IPv4 header is corrupted.");
                    }
                    // If no errors have been detected, read the data section.
                    packet.Data = reader.ReadBytes(totalLength - 20);
                    return(packet);
                }
            }
        }
示例#6
0
 /// <summary>
 /// Determines whether the specified IP packet is for us.
 /// </summary>
 /// <param name="packet">The IP packet to examine.</param>
 /// <returns>True if the IP packet's destination matches one of the
 /// host's interfaces' addresses; Otherwise false.</returns>
 bool IsPacketForUs(IpPacket packet)
 {
     foreach (var ifc in interfaces)
     {
         if (ifc.IpAddress == packet.Destination)
         {
             return(true);
         }
     }
     return(false);
 }
示例#7
0
        /// <summary>
        /// Reassembles fragmented IP packets and hands them up to the transport
        /// layer once they have been fully reassembled.
        /// </summary>
        /// <param name="packet">An IP packet representing a fragment of a
        /// fragmented packet.</param>
        public void ReassemblePacket(IpPacket packet)
        {
            // Fragments belong to the same datagram if they have the same source,
            // destination, protocol, and identifier fields (RFC 791, p. 28).
            var hash = Hash.Sha256(packet.Source +
                                   packet.Destination.ToString() + packet.Protocol +
                                   packet.Identification
                                   );

            // Group related fragments in a set under the same dictionary key.
            if (!fragments.ContainsKey(hash))
            {
                fragments.Add(hash, new HashSet <IpPacket>());
            }
            fragments[hash].Add(packet);
            // Figure out if we already have all fragments so that we can reassemble
            // the original packet.
            var uf = new UnionFind(65536);
            var originalDataSize = 0;

            foreach (var p in fragments[hash])
            {
                var from = p.FragmentOffset * 8;
                var to   = from + p.Data.Length - 1;
                uf.Union(from, to);
                uf.Union(to, to + 1);
                // Derive original packet size from last fragment.
                if (!p.Flags.HasFlag(IpFlag.MoreFragments))
                {
                    originalDataSize = from + p.Data.Length;
                }
            }
            // If this is still 0, last segment has not arrived yet.
            if (originalDataSize == 0)
            {
                return;
            }
            // If the first and the last byte are not part of the same component,
            // not all fragments have arrived yet.
            if (!uf.Connected(0, originalDataSize))
            {
                return;
            }
            var data = new byte[originalDataSize];

            foreach (var p in fragments[hash])
            {
                Array.Copy(p.Data, 0, data, p.FragmentOffset * 8, p.Data.Length);
            }
            // Hand up reassembled data to transport layer.
            HandUp(data, packet.Protocol);
        }
示例#8
0
 /// <summary>
 /// Determines whether the specified IP packet is a fragment.
 /// </summary>
 /// <param name="packet">The IP packet to examine.</param>
 /// <returns>true if the packet is a fragment. Otherwise
 /// false.</returns>
 bool IsFragment(IpPacket packet)
 {
     if (packet.Flags.HasFlag(IpFlag.MoreFragments))
     {
         return(true);
     }
     // The last fragment does not have the MoreFragments flag set, but has
     // a non-zero Fragment Offset field, differentiating it from an
     // unfragmented packet.
     if (packet.FragmentOffset > 0)
     {
         return(true);
     }
     return(false);
 }
示例#9
0
        /// <summary>
        /// Traverses the routing table and returns the best route for the
        /// specified packet.
        /// </summary>
        /// <param name="packet">The packet to find a route for.</param>
        /// <returns>The best route found in the routing table or null if
        /// no route was found.</returns>
        Route FindRoute(IpPacket packet)
        {
            Route best = null;

            foreach (var r in routingTable)
            {
                if ((r.Destination & r.Netmask) ==
                    (packet.Destination & r.Netmask))
                {
                    if (best == null)
                    {
                        best = r;
                    }
                    else if (best.Metric > r.Metric)
                    {
                        best = r;
                    }
                }
            }
            return(best);
        }
示例#10
0
        /// <summary>
        /// Attempts to route the specified packet.
        /// </summary>
        /// <param name="packet">The packet to route.</param>
        /// <param name="ifc">The interface through which the packet was
        /// received.</param>
        void RoutePacket(IpPacket packet, Interface ifc)
        {
            // See if we can find a machting route in our routing table.
            var route = FindRoute(packet);

            // Drop packet and send "Unreachable" ICMP back to packet originator.
            if (route == null)
            {
                SendIcmp(ifc, packet.Source, IcmpPacket.Unreachable(packet));
                return;
            }
            // Do we have to fragment the packet?
            if (route.Interface.MaximumTransmissionUnit < packet.TotalLength)
            {
                // Drop packet and send "Fragmentation Required" ICMP back to
                // packet originator.
                if (packet.Flags.HasFlag(IpFlag.DontFragment))
                {
                    SendIcmp(ifc, packet.Source, IcmpPacket.FragmentationRequired(packet));
                    return;
                }
                var packets = FragmentPacket(packet,
                                             route.Interface.MaximumTransmissionUnit);
                // Forward fragmented packets.
                foreach (var p in packets)
                {
                    Output(route.Interface, route.Gateway != null ? route.Gateway :
                           p.Destination, p);
                }
            }
            else
            {
                // Forward packet.
                Output(route.Interface, route.Gateway != null ? route.Gateway :
                       packet.Destination, packet);
            }
        }
示例#11
0
        /// <summary>
        /// Wraps the specified higher-level data into IP packets and
        /// transmits them to the specified destination.
        /// </summary>
        /// <param name="ifc">The interface through which to output the data.</param>
        /// <param name="destination">The logical address of the destination
        /// host.</param>
        /// <param name="data">The higher-level data to transmit.</param>
        /// <param name="type">The type of the higher-level data.</param>
        /// <exception cref="ArgumentNullException">Thrown if any of the arguments
        /// is null.</exception>
        /// <remarks>This API is exposed to the next higher-up layer.</remarks>
        public void Output(Interface ifc, IpAddress destination, byte[] data,
                           IpProtocol type)
        {
            ifc.ThrowIfNull("ifc");
            destination.ThrowIfNull("destination");
            data.ThrowIfNull("data");
            // Construct IP packets of the size of the MTU of the data-link.
            var maxDataSize = ifc.MaximumTransmissionUnit - 20;
            var numPackets  = (int)Math.Ceiling(data.Length / (double)maxDataSize);
            var sameSubnet  = (destination & ifc.Netmask) == (ifc.IpAddress & ifc.Netmask);

            for (int i = 0, index = 0; i < numPackets; i++)
            {
                var numBytes   = Math.Min(maxDataSize, data.Length - index);
                var packetData = new byte[numBytes];
                Array.Copy(data, index, packetData, 0, numBytes);
                index = index + numBytes;
                // Construct the packet.
                var packet = new IpPacket(destination, ifc.IpAddress, type, packetData);
                // If source and destination are in the same subnet, we can deliver the
                // packet directly. Otherwise send it to the configured default gateway.
                Output(ifc, sameSubnet ? destination : ifc.Gateway, packet);
            }
        }
示例#12
0
		/// <summary>
		/// Computes the 16-bit checksum of the specified IPv4 packet.
		/// </summary>
		/// <param name="packet">The packet to compute the checksum for.</param>
		/// <param name="withChecksumField">true to include the packet's
		/// checksum field in the calculation; otherwise false.</param>
		/// <returns>The checksum of the specified IPv4 packet.</returns>
		/// <exception cref="ArgumentNullException">Thrown if the packet
		/// argument is null.</exception>
		public static ushort ComputeChecksum(IpPacket packet,
			bool withChecksumField = false) {
			packet.ThrowIfNull("packet");
			// The version and IHL fields are 4 bit wide each.
			var vi = (byte) (((packet.Ihl & 0x0F) << 4) |
				(((int) packet.Version) & 0x0F));
			// The flags field is 3 bits and the fragment offset 13 bits wide.
			var ffo = (ushort) (((packet.FragmentOffset & 0x1FFF) << 3) |
				((int) packet.Flags & 0x07));
			var bytes = new ByteBuilder()
				.Append(vi)
				.Append(packet.Dscp)
				.Append(packet.TotalLength)
				.Append(packet.Identification)
				.Append(ffo)
				.Append(packet.TimeToLive)
				.Append((byte) packet.Protocol)
				.Append(withChecksumField ? packet.Checksum : (ushort)0)
				.Append(packet.Source.Bytes)
				.Append(packet.Destination.Bytes)
				.ToArray();
			var sum = 0;
			// Treat the header bytes as a sequence of unsigned 16-bit values and
			// sum them up.
			for (var n = 0; n < bytes.Length; n += 2)
				sum += BitConverter.ToUInt16(bytes, n);
			// Use carries to compute the 1's complement sum.
			sum = (sum >> 16) + (sum & 0xFFFF);
			// Return the inverted 16-bit result.
			return (ushort)(~ sum);
		}
示例#13
0
		/// <summary>
		/// Deserializes an IpPacket instance from the specified sequence of
		/// bytes.
		/// </summary>
		/// <param name="data">The sequence of bytes to deserialize an IpPacket
		/// object from.</param>
		/// <returns>A deserialized IpPacket object.</returns>
		/// <exception cref="ArgumentNullException">Thrown if the data argument is
		/// null.</exception>
		/// <exception cref="SerializationException">Thrown if the IP packet could
		/// not be deserialized from the specified byte array.</exception>
		public static IpPacket Deserialize(byte[] data) {
			data.ThrowIfNull("data");
			using (var ms = new MemoryStream(data)) {
				using (var reader = new BinaryReader(ms)) {
					var vi = reader.ReadByte();
					var version = (IpVersion)(vi & 0x0F);
					byte ihl = (byte) (vi >> 4), dscp = reader.ReadByte();
					ushort totalLength = reader.ReadUInt16(),
						identification = reader.ReadUInt16(), ffo = reader.ReadUInt16();
					var flags = (IpFlag) (ffo & 0x07);
					var fragmentOffset = (ushort) (ffo >> 3);
					var ttl = reader.ReadByte();
					var type = (IpProtocol) reader.ReadByte();
					var checksum = reader.ReadUInt16();
					IpAddress src = new IpAddress(reader.ReadBytes(4)),
						dst = new IpAddress(reader.ReadBytes(4));

					var packet = new IpPacket(version, ihl, dscp, totalLength,
						identification, flags, fragmentOffset, ttl, type, checksum,
						src, dst);
					// Computing the checksum should yield a value of 0 unless errors are
					// detected.
					if (ComputeChecksum(packet, true) != 0)
						throw new SerializationException("The IPv4 header is corrupted.");
					// If no errors have been detected, read the data section.
					packet.Data = reader.ReadBytes(totalLength - 20);
					return packet;
				}
			}
		}