/// <summary> /// Looks up the Layer-2 (MAC-48) physical address in the ARP cache for /// the specified IPv4 address. /// </summary> /// <param name="ifc">The interface to look an address up for.</param> /// <param name="ipAddress">The IPv4 address to look up the respective /// MAC-48 address for.</param> /// <returns>A MAC-48 address or null if the lookup could not be satisfied /// from the ARP cache.</returns> /// <exception cref="ArgumentNullException">Thrown if the ipAddress parameter /// is null.</exception> public MacAddress Lookup(Interface ifc, IpAddress ipAddress) { ipAddress.ThrowIfNull("ipAddress"); if (!cache.ContainsKey(ifc.Name)) cache.Add(ifc.Name, new ArpCache()); ArpEntry entry = cache[ifc.Name][ipAddress]; if (entry != null && entry.Expired == false) return entry.MacAddress; return null; }
/// <summary> /// Resolves the specified IPv4 address to a Layer-2 (MAC-48) /// physical address. /// </summary> /// <param name="ifc">The interface to look an address up for.</param> /// <param name="ipAddress">The IPv4 address to resolve.</param> /// <exception cref="ArgumentNullException">Thrown if either of the /// arguments is null.</exception> /// <remarks>This API is exposed to the next higher-up layer. In other /// words, it is called by the Network layer to resolve an IPv4 address /// to the corresponding MAC-48 address.</remarks> public void Resolve(Interface ifc, IpAddress ipAddress) { ifc.ThrowIfNull("ifc"); ipAddress.ThrowIfNull("ipAddress"); // If there's already a pending ARP request for the IP, don't // issue another one. if (arpInProgress.Contains(ipAddress)) return; // Output an ARP request to physical broadcast address // FF:FF:FF:FF:FF:FF. arpInProgress.Add(ipAddress); ArpPacket packet = new ArpPacket(ifc.Nic.MacAddress, ifc.IpAddress, ipAddress); WriteLine(ifc.FullName + " is constructing an ARP request for " + ipAddress + "."); output(ifc, broadcastAddress, packet); }
/// <summary> /// Initializes a new instance. /// </summary> /// <param name="destination">The IP address of the destination network. This, /// together with the netmask describes the destination network id.</param> /// <param name="netmask">The netmask that, together with the destination /// parameter describes the destination network id.</param> /// <param name="gateway">The gateway through which the destination network /// can be reached.</param> /// <param name="interface">The local interface through which the gateway /// can be reached.</param> /// <param name="metric">The metric of using the route.</param> void Init(IpAddress destination, IpAddress netmask, IpAddress gateway, Interface @interface, int metric) { destination.ThrowIfNull(nameof(destination)); netmask.ThrowIfNull(nameof(netmask)); @interface.ThrowIfNull(nameof(@interface)); if (metric < 0) throw new ArgumentException("The metric value must be greater than " + "or equal to zero.", nameof(metric)); Destination = destination; Netmask = netmask; Gateway = gateway; Interface = @interface; Metric = metric; }
/// <summary> /// Initializes a new instance of the Route class. /// </summary> /// <param name="cidrNetworkId">The network id of the destination network in /// CIDR notation.</param> /// <param name="gateway">The gateway through which the destination network /// can be reached.</param> /// <param name="interface">The local interface through which the gateway /// can be reached.</param> /// <param name="metric">The metric of using the route.</param> public Route(string cidrNetworkId, string gateway, Interface @interface, int metric) { var tuple = IpAddress.ParseCIDRNotation(cidrNetworkId); Init(tuple.Item1, tuple.Item2, gateway != null ? new IpAddress(gateway) : null, @interface, metric); }
/// <summary> /// Initializes a new instance of the Route class. /// </summary> /// <param name="destination">The IP address of the destination network. This, /// together with the netmask describes the destination network id.</param> /// <param name="netmask">The netmask that, together with the destination /// parameter describes the destination network id.</param> /// <param name="gateway">The gateway through which the destination network /// can be reached.</param> /// <param name="interface">The local interface through which the gateway /// can be reached.</param> /// <param name="metric">The metric of using the route.</param> public Route(IpAddress destination, IpAddress netmask, IpAddress gateway, Interface @interface, int metric) { Init(destination, netmask, gateway, @interface, metric); }
/// <summary> /// Returns an enumerable collection of ARP entries for the specified /// interface. /// </summary> /// <param name="ifc">The interface whose ARP table to return.</param> /// <returns>An enumerable collection of ARP entries.</returns> /// <exception cref="ArgumentNullException">Thrown if the ifc parameter /// is null.</exception> public IEnumerable<ArpEntry> ArpTableOf(Interface ifc) { ifc.ThrowIfNull("ifc"); if (!cache.ContainsKey(ifc.Name)) cache.Add(ifc.Name, new ArpCache()); return cache[ifc.Name].Values; }
/// <summary> /// Examines and processes the ARP message contained in the specified byte /// array. /// </summary> /// <param name="ifc">The interface through which the data was /// received.</param> /// <param name="data">A sequence of bytes containing an ARP packet.</param> /// <exception cref="ArgumentNullException">Thrown if either parameter /// is null.</exception> /// <exception cref="SerializationException">Thrown if the data array does /// not contain a valid ARP packet.</exception> public void OnInput(Interface ifc, byte[] data) { ifc.ThrowIfNull("ifc"); data.ThrowIfNull("data"); WriteLine(ifc.FullName + " has received an ARP message."); var packet = ArpPacket.Deserialize(data); // If it's our own packet, don't do anything with it. if (packet.MacAddressSender == ifc.Nic.MacAddress) return; // Update our ARP cache with the sender's information. if (!cache.ContainsKey(ifc.Name)) cache.Add(ifc.Name, new ArpCache()); cache[ifc.Name].Add(packet.IpAddressSender, new ArpEntry(packet.IpAddressSender, packet.MacAddressSender)); if(packet.IsResponse) WriteLine(ifc.FullName + " is updating its ARP table with [" + packet.IpAddressSender + ", " + packet.MacAddressSender + "]"); // Remove IP address from pending list, if present. arpInProgress.Remove(packet.IpAddressSender); if (packet.IsRequest) { // It's an ARP request and we are the recipient, so send an ARP reply // back to the sender. if (packet.IpAddressTarget == ifc.IpAddress) { var response = new ArpPacket(ifc.Nic.MacAddress, packet.IpAddressTarget, packet.MacAddressSender, packet.IpAddressSender); WriteLine(ifc.FullName + " is constructing an ARP response for " + packet.IpAddressSender + "."); output(ifc, packet.MacAddressSender, response); } } }
/// <summary> /// Registers a new network interface with the host. /// </summary> /// <param name="ifc"></param> /// <exception cref="ArgumentNullException">Thrown if any of the /// arguments is null.</exception> public void RegisterInterface(Interface ifc) { ifc.ThrowIfNull("ifc"); Interfaces.Add(ifc.Name, ifc); ifc.Hostname = Hostname; // Delegate the interface's events to the network stack. ifc.DataReceivedEvent += (sender, args) => Network.OnInput(sender as Interface, args.Data, args.Type); ifc.SendFifoEmptyEvent += (sender, args) => Network.OnAvailableToSent(sender as Interface); }
/// <summary> /// Adds a route to the host's routing table. /// </summary> /// <param name="cidrNetworkId">The destination subnet specified in CIDR notation.</param> /// <param name="gateway">The gateway through which the destination network can be /// reached.</param> /// <param name="interface">The local interface through which the gateway can be /// reached.</param> /// <param name="metric">The added cost of using the route.</param> /// <param name="index">The index or row number at which to insert the route into /// the routing table.</param> public void AddRoute(string cidrNetworkId, string gateway, Interface @interface, int metric, int? index = null) { AddRoute(new Route(cidrNetworkId, gateway, @interface, metric)); }