mirror of
https://github.com/livingcomputermuseum/IFS.git
synced 2026-01-13 07:19:54 +00:00
Cleanup, refactoring things into more appropriate protocol layers.
This commit is contained in:
parent
f2cc2130ee
commit
2badecda6d
@ -18,7 +18,7 @@ namespace IFS
|
||||
|
||||
public class BSPChannel
|
||||
{
|
||||
public BSPChannel(PUP rfcPup)
|
||||
public BSPChannel(PUP rfcPup, UInt32 socketID)
|
||||
{
|
||||
_inputLock = new ReaderWriterLockSlim();
|
||||
_outputLock = new ReaderWriterLockSlim();
|
||||
@ -30,6 +30,13 @@ namespace IFS
|
||||
// TODO: init IDs, etc. based on RFC PUP
|
||||
_start_pos = _recv_pos = _send_pos = rfcPup.ID;
|
||||
|
||||
// Set up socket addresses.
|
||||
// The client sends the connection port it prefers to use
|
||||
// in the RFC pup.
|
||||
_clientConnectionPort = new PUPPort(rfcPup.Contents, 0);
|
||||
|
||||
// We create our connection port using a unique socket address.
|
||||
_serverConnectionPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, socketID);
|
||||
}
|
||||
|
||||
public PUPPort ClientPort
|
||||
@ -152,6 +159,10 @@ namespace IFS
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the client sends an ACK
|
||||
/// </summary>
|
||||
/// <param name="ackPUP"></param>
|
||||
public void Ack(PUP ackPUP)
|
||||
{
|
||||
// Update receiving end stats (max PUPs, etc.)
|
||||
@ -216,7 +227,14 @@ namespace IFS
|
||||
{
|
||||
static BSPManager()
|
||||
{
|
||||
|
||||
//
|
||||
// Initialize the socket ID counter; we start with a
|
||||
// number beyond the range of well-defined sockets.
|
||||
// For each new BSP channel that gets opened, we will
|
||||
// increment this counter to ensure that each channel gets
|
||||
// a unique ID. (Well, until we wrap around...)
|
||||
//
|
||||
_nextSocketID = _startingSocketID;
|
||||
}
|
||||
|
||||
|
||||
@ -235,9 +253,14 @@ namespace IFS
|
||||
switch (type)
|
||||
{
|
||||
case PupType.RFC:
|
||||
{
|
||||
BSPChannel newChannel = new BSPChannel(p);
|
||||
_activeChannels.Add(newChannel.ClientPort.Socket, newChannel);
|
||||
{
|
||||
BSPChannel newChannel = new BSPChannel(p, GetNextSocketID());
|
||||
_activeChannels.Add(newChannel.ServerPort.Socket, newChannel);
|
||||
|
||||
// Send RFC response to complete the rendezvous.
|
||||
PUP rfcResponse = new PUP(PupType.RFC, p.ID, newChannel.ClientPort, newChannel.ServerPort, newChannel.ServerPort.ToArray());
|
||||
|
||||
Dispatcher.Instance.SendPup(rfcResponse);
|
||||
|
||||
return newChannel;
|
||||
}
|
||||
@ -301,6 +324,33 @@ namespace IFS
|
||||
return null;
|
||||
}
|
||||
|
||||
private static UInt32 GetNextSocketID()
|
||||
{
|
||||
UInt32 next = _nextSocketID;
|
||||
|
||||
_nextSocketID++;
|
||||
|
||||
//
|
||||
// Handle the wrap around case (which we're very unlikely to
|
||||
// ever hit, but why not do the right thing).
|
||||
// Start over at the initial ID. This is very unlikely to
|
||||
// collide with any pending channels.
|
||||
//
|
||||
if(_nextSocketID < _startingSocketID)
|
||||
{
|
||||
_nextSocketID = _startingSocketID;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Map from socket address to BSP channel
|
||||
/// </summary>
|
||||
private static Dictionary<UInt32, BSPChannel> _activeChannels;
|
||||
|
||||
private static UInt32 _nextSocketID;
|
||||
private static readonly UInt32 _startingSocketID = 0x1000;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,16 +5,11 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using IFS.Transport;
|
||||
using PcapDotNet.Packets;
|
||||
using PcapDotNet.Packets.Ethernet;
|
||||
using IFS.Logging;
|
||||
using PcapDotNet.Base;
|
||||
|
||||
namespace IFS
|
||||
{
|
||||
|
||||
|
||||
|
||||
{
|
||||
/// <summary>
|
||||
/// Dispatches incoming PUPs to the right protocol handler; sends outgoing PUPs over the network.
|
||||
/// </summary>
|
||||
@ -40,11 +35,13 @@ namespace IFS
|
||||
{
|
||||
// TODO: support multiple interfaces (for gateway routing, for example.)
|
||||
// Also, this should not be ethernet-specific.
|
||||
_packetInterface = new Ethernet(i, OnPacketReceived);
|
||||
_pupPacketInterface = new Ethernet(i);
|
||||
|
||||
_pupPacketInterface.RegisterReceiveCallback(OnPupReceived);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new protocol with the dispatcher
|
||||
/// Registers a new protocol with the dispatcher.
|
||||
/// </summary>
|
||||
/// <param name="reg"></param>
|
||||
/// <param name="impl"></param>
|
||||
@ -61,129 +58,36 @@ namespace IFS
|
||||
|
||||
public void SendPup(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), everthing is on the same 'net.
|
||||
// Just look up host address and find the MAC of the host to send it to.
|
||||
// TODO: this translation should be at the Interface level, not here (we shouldn't be doing
|
||||
// ethernet-specific things here -- at the moment it doesn't matter since we're only speaking
|
||||
// ethernet, but this should be fixed.)
|
||||
//
|
||||
if (_pupToEthernetMap.ContainsKey(p.DestinationPort.Host))
|
||||
_pupPacketInterface.Send(p);
|
||||
}
|
||||
|
||||
private void OnPupReceived(PUP pup)
|
||||
{
|
||||
//
|
||||
// Forward PUP on to registered endpoints.
|
||||
//
|
||||
if (_dispatchMap.ContainsKey(pup.DestinationPort.Socket))
|
||||
{
|
||||
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 = (MacAddress)_packetInterface.GetDeviceAddress(),
|
||||
Destination = destinationMac,
|
||||
EtherType = (EthernetType)512, // PUP type (TODO: move to constant)
|
||||
};
|
||||
|
||||
PayloadLayer payloadLayer = new PayloadLayer
|
||||
{
|
||||
Data = new Datagram(p.RawData),
|
||||
};
|
||||
|
||||
PacketBuilder builder = new PacketBuilder(ethernetLayer, payloadLayer);
|
||||
|
||||
_packetInterface.SendPacket(builder.Build(DateTime.Now));
|
||||
PUPProtocolEntry entry = _dispatchMap[pup.DestinationPort.Socket];
|
||||
entry.ProtocolImplementation.RecvData(pup);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Log error, this should not happen.
|
||||
Log.Write(LogLevel.Error, String.Format("PUP destination address {0} is unknown.", p.DestinationPort.Host));
|
||||
}
|
||||
|
||||
// Not a protocol we handle; log it.
|
||||
Log.Write(LogLevel.UnhandledProtocol | LogLevel.DroppedPacket, String.Format("Unhandled PUP protocol, socket {0}, dropped packet.", pup.DestinationPort.Socket));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPacketReceived(Packet p)
|
||||
{
|
||||
// filter out PUPs, discard all other packets. Forward PUP on to registered endpoints.
|
||||
//
|
||||
if ((int)p.Ethernet.EtherType == 512) // 512 = pup type
|
||||
{
|
||||
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.
|
||||
//
|
||||
if (pup.SourcePort.Network == 0 || pup.SourcePort.Network == DirectoryServices.Instance.LocalHostAddress.Network)
|
||||
{
|
||||
UpdateMACTable(pup, p);
|
||||
|
||||
if (_dispatchMap.ContainsKey(pup.DestinationPort.Socket))
|
||||
{
|
||||
PUPProtocolEntry entry = _dispatchMap[pup.DestinationPort.Socket];
|
||||
entry.ProtocolImplementation.RecvData(pup);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a protocol we handle; TODO: log it.
|
||||
Log.Write(LogLevel.UnhandledProtocol | LogLevel.DroppedPacket, String.Format("Unhandled PUP protocol, socket {0}, dropped packet.", pup.DestinationPort.Socket));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not for our network; eventually we will look into routing...
|
||||
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 to keep noise down.
|
||||
//Log.Write(LogLevel.DroppedPacket, "Not a PUP. Dropping.");
|
||||
}
|
||||
}
|
||||
|
||||
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 id on the network.
|
||||
//
|
||||
if (_pupToEthernetMap[p.SourcePort.Host] != e.Ethernet.Source)
|
||||
{
|
||||
Log.Write(LogLevel.DuplicateHostNumber,
|
||||
String.Format("Duplicate host number {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>
|
||||
/// Our interface to some kind of network
|
||||
/// Our interface to a facility that can transmit/receive PUPs
|
||||
/// </summary>
|
||||
private IPacketInterface _packetInterface;
|
||||
private IPupPacketInterface _pupPacketInterface;
|
||||
|
||||
/// <summary>
|
||||
/// Map from socket to protocol implementation
|
||||
/// </summary>
|
||||
private Dictionary<UInt32, PUPProtocolEntry> _dispatchMap;
|
||||
|
||||
/// <summary>
|
||||
/// PUP<->Ethernet address map
|
||||
/// </summary>
|
||||
private Dictionary<byte, MacAddress> _pupToEthernetMap;
|
||||
private Dictionary<MacAddress, byte> _ethernetToPupMap;
|
||||
|
||||
private static Dispatcher _instance = new Dispatcher();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,10 +4,12 @@ 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
|
||||
{
|
||||
@ -40,43 +42,95 @@ namespace IFS.Transport
|
||||
/// <summary>
|
||||
/// Defines interface "to the metal" (raw ethernet frames) which may wrap the underlying transport (for example, winpcap)
|
||||
/// </summary>
|
||||
public class Ethernet : IPacketInterface
|
||||
public class Ethernet : IPupPacketInterface
|
||||
{
|
||||
public Ethernet(EthernetInterface iface, HandlePacket callback)
|
||||
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;
|
||||
|
||||
Open(false, int.MaxValue);
|
||||
// Now that we have a callback we can start receiving stuff.
|
||||
Open(false /* not promiscuous */, int.MaxValue);
|
||||
BeginReceive();
|
||||
}
|
||||
|
||||
|
||||
public void Open(bool promiscuous, int timeout)
|
||||
public void Send(PUP p)
|
||||
{
|
||||
_communicator = _interface.Open(0xffff, promiscuous ? PacketDeviceOpenAttributes.Promiscuous : PacketDeviceOpenAttributes.None, timeout);
|
||||
}
|
||||
//
|
||||
// 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];
|
||||
|
||||
/// <summary>
|
||||
/// Begin receiving packets, forever.
|
||||
/// </summary>
|
||||
public void BeginReceive()
|
||||
{
|
||||
_communicator.ReceivePackets(-1, ReceiveCallback);
|
||||
}
|
||||
// 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,
|
||||
};
|
||||
|
||||
public void SendPacket(Packet p)
|
||||
{
|
||||
_communicator.SendPacket(p);
|
||||
}
|
||||
PayloadLayer payloadLayer = new PayloadLayer
|
||||
{
|
||||
Data = new Datagram(p.RawData),
|
||||
};
|
||||
|
||||
public object GetDeviceAddress()
|
||||
{
|
||||
return (object)_interface.GetMacAddress();
|
||||
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)
|
||||
{
|
||||
_callback(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)
|
||||
@ -99,9 +153,59 @@ namespace IFS.Transport
|
||||
}
|
||||
}
|
||||
|
||||
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 HandlePacket _callback;
|
||||
private HandlePup _callback;
|
||||
|
||||
// Constants
|
||||
private const ushort _pupFrameType = 512;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,14 +7,17 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace IFS.Transport
|
||||
{
|
||||
/// <summary>
|
||||
/// PacketInterface provides an abstraction over a transport (Ethernet, IP, Carrier Pigeon)
|
||||
/// which can provide raw packets.
|
||||
/// </summary>
|
||||
public interface IPacketInterface
|
||||
{
|
||||
void SendPacket(Packet p);
|
||||
|
||||
object GetDeviceAddress();
|
||||
public delegate void HandlePup(PUP pup);
|
||||
|
||||
/// <summary>
|
||||
/// IPupPacketInterface provides an abstraction over a transport (Ethernet, IP, Carrier Pigeon)
|
||||
/// which can provide encapsulation for PUPs.
|
||||
/// </summary>
|
||||
public interface IPupPacketInterface
|
||||
{
|
||||
void Send(PUP p);
|
||||
|
||||
void RegisterReceiveCallback(HandlePup callback);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user