From 2badecda6d462231da6d8f73bd0c0011e1d5236a Mon Sep 17 00:00:00 2001 From: Josh Dersch Date: Fri, 16 Oct 2015 11:59:06 -0700 Subject: [PATCH] Cleanup, refactoring things into more appropriate protocol layers. --- PUP/BSPManager.cs | 60 +++++++++++-- PUP/Dispatcher.cs | 140 +++++------------------------ PUP/Transport/Ethernet.cs | 150 ++++++++++++++++++++++++++----- PUP/Transport/PacketInterface.cs | 19 ++-- 4 files changed, 215 insertions(+), 154 deletions(-) diff --git a/PUP/BSPManager.cs b/PUP/BSPManager.cs index 3e976b1..557fb9f 100644 --- a/PUP/BSPManager.cs +++ b/PUP/BSPManager.cs @@ -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 } + /// + /// Invoked when the client sends an ACK + /// + /// 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; + } + + + /// + /// Map from socket address to BSP channel + /// private static Dictionary _activeChannels; + + private static UInt32 _nextSocketID; + private static readonly UInt32 _startingSocketID = 0x1000; } } diff --git a/PUP/Dispatcher.cs b/PUP/Dispatcher.cs index 4b29e42..a2b6b5c 100644 --- a/PUP/Dispatcher.cs +++ b/PUP/Dispatcher.cs @@ -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 -{ - - - +{ /// /// Dispatches incoming PUPs to the right protocol handler; sends outgoing PUPs over the network. /// @@ -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); } /// - /// Registers a new protocol with the dispatcher + /// Registers a new protocol with the dispatcher. /// /// /// @@ -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); - } - } - + /// - /// Our interface to some kind of network + /// Our interface to a facility that can transmit/receive PUPs /// - private IPacketInterface _packetInterface; + private IPupPacketInterface _pupPacketInterface; /// /// Map from socket to protocol implementation /// private Dictionary _dispatchMap; - /// - /// PUP<->Ethernet address map - /// - private Dictionary _pupToEthernetMap; - private Dictionary _ethernetToPupMap; - private static Dispatcher _instance = new Dispatcher(); } } diff --git a/PUP/Transport/Ethernet.cs b/PUP/Transport/Ethernet.cs index 151e4c1..9a10278 100644 --- a/PUP/Transport/Ethernet.cs +++ b/PUP/Transport/Ethernet.cs @@ -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 /// /// Defines interface "to the metal" (raw ethernet frames) which may wrap the underlying transport (for example, winpcap) /// - 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(256); + _ethernetToPupMap = new Dictionary(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]; - /// - /// Begin receiving packets, forever. - /// - 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); + } + + /// + /// Begin receiving packets, forever. + /// + 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); + } + } + + /// + /// PUP<->Ethernet address map + /// + private Dictionary _pupToEthernetMap; + private Dictionary _ethernetToPupMap; + private LivePacketDevice _interface; private PacketCommunicator _communicator; - private HandlePacket _callback; + private HandlePup _callback; + + // Constants + private const ushort _pupFrameType = 512; } } diff --git a/PUP/Transport/PacketInterface.cs b/PUP/Transport/PacketInterface.cs index 183dc03..dd638bf 100644 --- a/PUP/Transport/PacketInterface.cs +++ b/PUP/Transport/PacketInterface.cs @@ -7,14 +7,17 @@ using System.Threading.Tasks; namespace IFS.Transport { - /// - /// PacketInterface provides an abstraction over a transport (Ethernet, IP, Carrier Pigeon) - /// which can provide raw packets. - /// - public interface IPacketInterface - { - void SendPacket(Packet p); - object GetDeviceAddress(); + public delegate void HandlePup(PUP pup); + + /// + /// IPupPacketInterface provides an abstraction over a transport (Ethernet, IP, Carrier Pigeon) + /// which can provide encapsulation for PUPs. + /// + public interface IPupPacketInterface + { + void Send(PUP p); + + void RegisterReceiveCallback(HandlePup callback); } }