mirror of
https://github.com/livingcomputermuseum/IFS.git
synced 2026-01-13 15:27:25 +00:00
212 lines
7.3 KiB
C#
212 lines
7.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
using PcapDotNet.Base;
|
|
using PcapDotNet.Core;
|
|
using PcapDotNet.Core.Extensions;
|
|
using PcapDotNet.Packets;
|
|
using PcapDotNet.Packets.Ethernet;
|
|
using IFS.Logging;
|
|
|
|
namespace IFS.Transport
|
|
{
|
|
public struct EthernetInterface
|
|
{
|
|
public EthernetInterface(string name, string description, MacAddress macAddress)
|
|
{
|
|
Name = name;
|
|
Description = description;
|
|
MacAddress = macAddress;
|
|
}
|
|
|
|
public static List<EthernetInterface> EnumerateDevices()
|
|
{
|
|
List<EthernetInterface> interfaces = new List<EthernetInterface>();
|
|
|
|
foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
|
|
{
|
|
interfaces.Add(new EthernetInterface(device.Name, device.Description, device.GetMacAddress()));
|
|
}
|
|
|
|
return interfaces;
|
|
}
|
|
|
|
public string Name;
|
|
public string Description;
|
|
public MacAddress MacAddress;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Defines interface "to the metal" (raw ethernet frames) which may wrap the underlying transport (for example, winpcap)
|
|
/// </summary>
|
|
public class Ethernet : IPupPacketInterface
|
|
{
|
|
public Ethernet(EthernetInterface iface)
|
|
{
|
|
AttachInterface(iface);
|
|
|
|
// Set up maps
|
|
_pupToEthernetMap = new Dictionary<byte, MacAddress>(256);
|
|
_ethernetToPupMap = new Dictionary<MacAddress, byte>(256);
|
|
}
|
|
|
|
public void RegisterReceiveCallback(HandlePup callback)
|
|
{
|
|
_callback = callback;
|
|
|
|
// Now that we have a callback we can start receiving stuff.
|
|
Open(false /* not promiscuous */, int.MaxValue);
|
|
BeginReceive();
|
|
}
|
|
|
|
|
|
public void Send(PUP p)
|
|
{
|
|
//
|
|
// Write PUP to ethernet:
|
|
// Get destination network & host address from PUP and route to correct ethernet address.
|
|
// For now, no actual routing (Gateway not implemented yet), everything is on the same 'net.
|
|
// Just look up host address and find the MAC of the host to send it to.
|
|
//
|
|
if (_pupToEthernetMap.ContainsKey(p.DestinationPort.Host))
|
|
{
|
|
MacAddress destinationMac = _pupToEthernetMap[p.SourcePort.Host];
|
|
|
|
// Build the outgoing packet; place the source/dest addresses, type field and the PUP data.
|
|
EthernetLayer ethernetLayer = new EthernetLayer
|
|
{
|
|
Source = _interface.GetMacAddress(),
|
|
Destination = destinationMac,
|
|
EtherType = (EthernetType)_pupFrameType,
|
|
};
|
|
|
|
PayloadLayer payloadLayer = new PayloadLayer
|
|
{
|
|
Data = new Datagram(p.RawData),
|
|
};
|
|
|
|
PacketBuilder builder = new PacketBuilder(ethernetLayer, payloadLayer);
|
|
|
|
// Send it over the 'net!
|
|
_communicator.SendPacket(builder.Build(DateTime.Now));
|
|
}
|
|
else
|
|
{
|
|
// Log error, this should not happen.
|
|
Log.Write(LogLevel.Error, String.Format("PUP destination address {0} is unknown.", p.DestinationPort.Host));
|
|
}
|
|
}
|
|
|
|
private void ReceiveCallback(Packet p)
|
|
{
|
|
//
|
|
// Filter out PUPs, forward them on.
|
|
//
|
|
if ((int)p.Ethernet.EtherType == _pupFrameType)
|
|
{
|
|
PUP pup = new PUP(p.Ethernet.Payload.ToMemoryStream());
|
|
|
|
//
|
|
// Check the network -- if this is not network zero (coming from a host that doesn't yet know what
|
|
// network it's on) or the network we're on, we will ignore it (for now). Once we implement
|
|
// Gateway services we will handle these appropriately (at a higher, as-yet-unimplemented level between this
|
|
// and the Dispatcher).
|
|
//
|
|
if (pup.SourcePort.Network == 0 || pup.SourcePort.Network == DirectoryServices.Instance.LocalHostAddress.Network)
|
|
{
|
|
UpdateMACTable(pup, p);
|
|
_callback(pup);
|
|
}
|
|
else
|
|
{
|
|
// Not for our network.
|
|
Log.Write(LogLevel.DroppedPacket, String.Format("PUP is for network {0}, dropping.", pup.SourcePort.Network));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not a PUP, Discard the packet. We will not log this, so as to keep noise down.
|
|
//Log.Write(LogLevel.DroppedPacket, "Not a PUP. Dropping.");
|
|
}
|
|
}
|
|
|
|
private void AttachInterface(EthernetInterface iface)
|
|
{
|
|
_interface = null;
|
|
|
|
// Find the specified device by name
|
|
foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
|
|
{
|
|
if (device.Name == iface.Name && device.GetMacAddress() == iface.MacAddress)
|
|
{
|
|
_interface = device;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (_interface == null)
|
|
{
|
|
throw new InvalidOperationException("Requested interface not found.");
|
|
}
|
|
}
|
|
|
|
private void Open(bool promiscuous, int timeout)
|
|
{
|
|
_communicator = _interface.Open(0xffff, promiscuous ? PacketDeviceOpenAttributes.Promiscuous : PacketDeviceOpenAttributes.None, timeout);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Begin receiving packets, forever.
|
|
/// </summary>
|
|
private void BeginReceive()
|
|
{
|
|
_communicator.ReceivePackets(-1, ReceiveCallback);
|
|
}
|
|
|
|
private void UpdateMACTable(PUP p, Packet e)
|
|
{
|
|
//
|
|
// See if we already have this entry.
|
|
//
|
|
if (_pupToEthernetMap.ContainsKey(p.SourcePort.Host))
|
|
{
|
|
//
|
|
// We do; ensure that the mac addresses match -- if not we have a duplicate
|
|
// PUP host id on the network.
|
|
//
|
|
if (_pupToEthernetMap[p.SourcePort.Host] != e.Ethernet.Source)
|
|
{
|
|
Log.Write(LogLevel.DuplicateHostNumber,
|
|
String.Format("Duplicate host ID {0} for MAC {1} (currently mapped to MAC {2})",
|
|
p.SourcePort.Host,
|
|
e.Ethernet.Source,
|
|
_pupToEthernetMap[p.SourcePort.Host]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Add a mapping in both directions
|
|
_pupToEthernetMap.Add(p.SourcePort.Host, e.Ethernet.Source);
|
|
_ethernetToPupMap.Add(e.Ethernet.Source, p.SourcePort.Host);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// PUP<->Ethernet address map
|
|
/// </summary>
|
|
private Dictionary<byte, MacAddress> _pupToEthernetMap;
|
|
private Dictionary<MacAddress, byte> _ethernetToPupMap;
|
|
|
|
private LivePacketDevice _interface;
|
|
private PacketCommunicator _communicator;
|
|
private HandlePup _callback;
|
|
|
|
// Constants
|
|
private const ushort _pupFrameType = 512;
|
|
|
|
}
|
|
}
|