1
0
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:
Josh Dersch 2015-10-16 11:59:06 -07:00
parent f2cc2130ee
commit 2badecda6d
4 changed files with 215 additions and 154 deletions

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}