From bf7aa94d4d92eb420fcd6291544e5715267aa1d0 Mon Sep 17 00:00:00 2001 From: Josh Dersch Date: Mon, 15 Feb 2016 11:01:23 -0800 Subject: [PATCH] Implemented BreathOfLife, begun EFTP. --- PUP/BSP/BSPChannel.cs | 12 ++-- PUP/Boot/BootServer.cs | 24 ++++++++ PUP/Boot/BreathOfLife.cs | 95 ++++++++++++++++++++++++++++++++ PUP/BreathOfLife.cs | 64 --------------------- PUP/EFTP/EFTPServer.cs | 37 +++++++++++++ PUP/Entrypoint.cs | 10 +++- PUP/IFS.csproj | 4 +- PUP/Logging/Log.cs | 1 + PUP/MiscServicesProtocol.cs | 25 +++++++-- PUP/PUP.cs | 6 ++ PUP/PUPProtocolBase.cs | 3 +- PUP/PUPProtocolDispatcher.cs | 17 +++++- PUP/Transport/Ethernet.cs | 79 +++++++++++++++++++++++++- PUP/Transport/PacketInterface.cs | 13 +++++ 14 files changed, 309 insertions(+), 81 deletions(-) create mode 100644 PUP/Boot/BootServer.cs create mode 100644 PUP/Boot/BreathOfLife.cs delete mode 100644 PUP/BreathOfLife.cs create mode 100644 PUP/EFTP/EFTPServer.cs diff --git a/PUP/BSP/BSPChannel.cs b/PUP/BSP/BSPChannel.cs index 2bf857c..67a5195 100644 --- a/PUP/BSP/BSPChannel.cs +++ b/PUP/BSP/BSPChannel.cs @@ -636,8 +636,6 @@ namespace IFS.BSP } // Nope. Request another ACK. - // TODO: should probably error out of this if the client never becomes ready again... - RequestClientStats(); } // @@ -671,6 +669,7 @@ namespace IFS.BSP Log.Write(LogType.Error, LogComponent.BSP, "Client lost more than a window of data, BSP connection is broken. Aborting."); SendAbort("Fatal BSP synchronization error."); BSPManager.DestroyChannel(this); + _outputWindowLock.ExitUpgradeableReadLock(); return; } @@ -684,8 +683,9 @@ namespace IFS.BSP _outputWindow.RemoveRange(0, _outputWindowIndex); _outputWindowIndex = 0; _outputWindowLock.ExitWriteLock(); - _outputReadyEvent.Set(); - break; + _outputReadyEvent.Set(); + + // Note: we don't break from the loop here; there may still be PUPs left in _outputWindow that need to be sent. } } } @@ -720,6 +720,10 @@ namespace IFS.BSP } } + /// + /// Waits for an ACK from the client, "pinging" the client periodically. Will retry a number of times, if no + /// ACK is received the channel is shut down. + /// private void WaitForAck() { // diff --git a/PUP/Boot/BootServer.cs b/PUP/Boot/BootServer.cs new file mode 100644 index 0000000..a3e06a5 --- /dev/null +++ b/PUP/Boot/BootServer.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IFS.Boot +{ + public class BootServerProtocol : PUPProtocolBase + { + public BootServerProtocol() + { + + } + + /// + /// Called by dispatcher to send incoming data destined for this protocol + /// + /// + public override void RecvData(PUP p) + { + } + } +} diff --git a/PUP/Boot/BreathOfLife.cs b/PUP/Boot/BreathOfLife.cs new file mode 100644 index 0000000..145383b --- /dev/null +++ b/PUP/Boot/BreathOfLife.cs @@ -0,0 +1,95 @@ +using IFS.Logging; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; + +namespace IFS +{ + /// + /// Implements the BreathOfLife services. + /// It spins up a worker thread that wakes up every few seconds and broadcasts + /// a BreathOfLife packet. + /// + public class BreathOfLife + { + public BreathOfLife() + { + Log.Write(LogType.Verbose, LogComponent.BreathOfLife, "Breath Of Life service starting. Broadcast interval is {0} milliseconds.", _bolPacketDelay); + _bolThread = new Thread(BreathOfLifeThread); + _bolThread.Start(); + } + + private void BreathOfLifeThread() + { + while (true) + { + // + // Send BOL + // + PUPProtocolDispatcher.Instance.Send(_bolPacket, DirectoryServices.Instance.LocalHost, _bolAddress, _bolPacketType); + + Log.Write(LogType.Verbose, LogComponent.BreathOfLife, "Breath Of Life packet sent."); + + // + // Go to sleep. + // + Thread.Sleep(_bolPacketDelay); + + // + // That's it. Go home, do it again. + // + } + } + + + private Thread _bolThread; + + private const ushort _bolPacketType = 0x182; // 602B + private const byte _bolAddress = 0xff; // 377B (boot address) + + private const int _bolPacketDelay = 5000; // 5 seconds + + /// + /// The gold-standard BOL packet, containing the Alto ethernet bootstrap code. + /// Note that this does not contain padding for the ethernet header, the dispatcher adds those two words. + /// + private byte[] _bolPacket = + { + 0x25, 0x7c, 0x80, 0x00, 0x41, 0x1f, 0x84, 0x00, 0x39, 0x19, 0xe8, 0x00, + 0x62, 0x05, 0x85, 0x30, 0x29, 0x77, 0x39, 0x7a, 0x62, 0x06, 0x29, 0x75, 0x39, 0x78, 0x62, 0x06, + 0x29, 0x74, 0x39, 0x76, 0x62, 0x06, 0x21, 0x75, 0x39, 0x75, 0x62, 0x05, 0xaa, 0x90, 0x4d, 0x7b, + 0x21, 0x7e, 0x62, 0x04, 0xa7, 0x00, 0x31, 0x69, 0x42, 0x89, 0x09, 0x0e, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x16, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0x5a, 0x88, 0x2b, 0x00, 0x8e, 0x00, 0x4b, 0x00, 0x43, 0x09, + 0x21, 0x67, 0x42, 0x87, 0x21, 0x64, 0x62, 0x04, 0x0d, 0x59, 0x39, 0x4f, 0x31, 0x64, 0x53, 0x86, + 0x21, 0x48, 0x43, 0x85, 0x45, 0x56, 0x85, 0x30, 0x43, 0x81, 0x21, 0x58, 0x62, 0x04, 0x23, 0x81, + 0x82, 0x0c, 0x01, 0x0c, 0x62, 0x10, 0x19, 0xdf, 0x01, 0xfb, 0x19, 0x4f, 0x01, 0xec, 0x21, 0x4f, + 0x62, 0x04, 0x31, 0x02, 0x05, 0x02, 0x00, 0x00, 0x01, 0xda, 0x39, 0x4c, 0xe5, 0x0c, 0x01, 0xe6, + 0x22, 0x01, 0x29, 0x49, 0x8d, 0x0c, 0x01, 0xe2, 0x22, 0x03, 0xe7, 0x00, 0x3a, 0x05, 0x29, 0x41, + 0x8d, 0x0d, 0xfa, 0x0c, 0x01, 0xdb, 0x22, 0x00, 0x45, 0x35, 0x39, 0x2f, 0x5d, 0x25, 0x5b, 0xfd, + 0x5b, 0xfe, 0x82, 0xc0, 0x43, 0x00, 0x21, 0x37, 0x43, 0x01, 0x21, 0x1a, 0x43, 0x02, 0xa3, 0x00, + 0x43, 0x03, 0x22, 0x06, 0x43, 0x09, 0x22, 0x07, 0x43, 0x0a, 0x22, 0x08, 0x43, 0x0b, 0x22, 0x09, + 0x43, 0x06, 0x22, 0x0a, 0x43, 0x07, 0x22, 0x0b, 0x43, 0x08, 0x1b, 0x0c, 0x0d, 0x17, 0x15, 0x1c, + 0x0d, 0x16, 0x21, 0x11, 0x29, 0x1f, 0xb8, 0x00, 0x31, 0x0f, 0x05, 0x12, 0xfe, 0x1d, 0x00, 0x16, + 0x01, 0x0d, 0x01, 0x17, 0x01, 0x77, 0x01, 0x87, 0x01, 0xff, 0xff, 0xe9, 0xff, 0xa1, 0xff, 0x80, + 0x00, 0x95, 0xff, 0x94, 0x02, 0x0b, 0x01, 0xf4, 0x01, 0x19, 0x01, 0xca, 0x01, 0x9c, 0x01, 0xdd, + 0x01, 0x89, 0x01, 0x77, 0x01, 0x74, 0x01, 0x76, 0x00, 0x1e, 0x00, 0x02, 0x00, 0x03, 0x00, 0x0d, + 0x00, 0x18, 0x00, 0xff, 0x02, 0x00, 0xff, 0xf4, 0x01, 0x1e, 0x59, 0xd9, 0x51, 0xe8, 0x21, 0xeb, + 0x41, 0xe5, 0x85, 0x30, 0x41, 0xdf, 0x21, 0xf2, 0x62, 0x04, 0x21, 0xdc, 0x8a, 0xc4, 0x01, 0x06, + 0x21, 0xe3, 0x82, 0x0c, 0x19, 0xe1, 0x01, 0xfa, 0x05, 0xca, 0x39, 0xec, 0xef, 0xc0, 0x9d, 0x0d, + 0x29, 0xea, 0x22, 0x01, 0x8d, 0x0c, 0x01, 0xec, 0x22, 0x00, 0x29, 0xc3, 0x8d, 0x0c, 0x01, 0xe8, + 0x22, 0x03, 0xe7, 0x00, 0x29, 0xde, 0x8d, 0x05, 0x01, 0x04, 0xa3, 0x00, 0x83, 0x04, 0x01, 0xe0, + 0x49, 0xcc, 0x22, 0x05, 0x45, 0xda, 0x29, 0xb4, 0xa5, 0x05, 0x01, 0x04, 0x83, 0x05, 0x09, 0x04, + 0x01, 0xd7, 0x11, 0xae, 0x39, 0xac, 0x29, 0x03, 0xa1, 0x40, 0x83, 0x04, 0x01, 0xff, 0x41, 0xb2, + 0x41, 0xb4, 0x41, 0xb4, 0x85, 0x50, 0x62, 0x04, 0x21, 0xad, 0x82, 0x0d, 0x01, 0xfe, 0x8d, 0x0d, + 0x03, 0x00, 0x19, 0x9c, 0x01, 0xf1, 0x21, 0xbb, 0x62, 0x04, 0x01, 0x00, 0x62, 0x05, 0x84, 0x00, + 0xc6, 0x00, 0x39, 0xba, 0x29, 0xb7, 0xed, 0x00, 0x62, 0x05, 0x09, 0xb8, 0x21, 0xb3, 0x39, 0xb4, + 0xe8, 0x00, 0xce, 0x00, 0x62, 0x05, 0x29, 0xf2, 0xb5, 0x0c, 0xb6, 0x00, 0x21, 0x9e, 0x39, 0x0e, + 0x82, 0x0d, 0xdd, 0x0d, 0xb2, 0x01, 0x01, 0xec, 0x29, 0xa3, 0x49, 0x95, 0x82, 0x0d, 0x01, 0xe8, + 0x19, 0x93, 0x09, 0xa4, 0x21, 0x9c, 0x62, 0x04, 0x04, 0x00, 0xfd, 0xf4, 0x03, 0x01, 0x42, 0x02, + }; + } +} diff --git a/PUP/BreathOfLife.cs b/PUP/BreathOfLife.cs deleted file mode 100644 index 0bc09aa..0000000 --- a/PUP/BreathOfLife.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace IFS -{ - /// - /// Implements the BreathOfLife services. - /// It spins up a worker thread that wakes up every few seconds and broadcasts - /// a BreathOfLife packet. - /// - public class BreathOfLife - { - public BreathOfLife() - { - - } - - - /* - ushort[] _bolPacket = - { - 0,0, - - - - - 0022574, 0100000, 0040437, 0102000, 0034431, 0164000, - 0061005, 0102460, 0024567, 0034572, 0061006, 0024565, 0034570, 0061006, - 0024564, 0034566, 0061006, 0020565, 0034565, 0061005, 0125220, 0046573, - 0020576, 0061004, 0123400, 0030551, 0041211, 0004416, 0000000, 0001000, - 0000026, 0000244, 0000000, 0000000, 0000000, 0000000, 0000004, 0000000, - 0000000, 0000020, 0177777, 0055210, 0025400, 0107000, 0045400, 0041411, - 0020547, 0041207, 0020544, 0061004, 0006531, 0034517, 0030544, 0051606, - 0020510, 0041605, 0042526, 0102460, 0041601, 0020530, 0061004, 0021601, - 0101014, 0000414, 0061020, 0014737, 0000773, 0014517, 0000754, 0020517, - 0061004, 0030402, 0002402, 0000000, 0000732, 0034514, 0162414, 0000746, - 0021001, 0024511, 0106414, 0000742, 0021003, 0163400, 0035005, 0024501, - 0106415, 0175014, 0000733, 0021000, 0042465, 0034457, 0056445, 0055775, - 0055776, 0101300, 0041400, 0020467, 0041401, 0020432, 0041402, 0121400, - 0041403, 0021006, 0041411, 0021007, 0041412, 0021010, 0041413, 0021011, - 0041406, 0021012, 0041407, 0021013, 0041410, 0015414, 0006427, 0012434, - 0006426, 0020421, 0024437, 0134000, 0030417, 0002422, 0177035, 0000026, - 0000415, 0000427, 0000567, 0000607, 0000777, 0177751, 0177641, 0177600, - 0000225, 0177624, 0001013, 0000764, 0000431, 0000712, 0000634, 0000735, - 0000611, 0000567, 0000564, 0000566, 0000036, 0000002, 0000003, 0000015, - 0000030, 0000377, 0001000, 0177764, 0000436, 0054731, 0050750, 0020753, - 0040745, 0102460, 0040737, 0020762, 0061004, 0020734, 0105304, 0000406, - 0020743, 0101014, 0014741, 0000772, 0002712, 0034754, 0167700, 0116415, - 0024752, 0021001, 0106414, 0000754, 0021000, 0024703, 0106414, 0000750, - 0021003, 0163400, 0024736, 0106405, 0000404, 0121400, 0101404, 0000740, - 0044714, 0021005, 0042732, 0024664, 0122405, 0000404, 0101405, 0004404, - 0000727, 0010656, 0034654, 0024403, 0120500, 0101404, 0000777, 0040662, - 0040664, 0040664, 0102520, 0061004, 0020655, 0101015, 0000776, 0106415, - 0001400, 0014634, 0000761, 0020673, 0061004, 0000400, 0061005, 0102000, - 0143000, 0034672, 0024667, 0166400, 0061005, 0004670, 0020663, 0034664, - 0164000, 0147000, 0061005, 0024762, 0132414, 0133000, 0020636, 0034416, - 0101015, 0156415, 0131001, 0000754, 0024643, 0044625, 0101015, 0000750, - 0014623, 0004644, 0020634, 0061004, 0002000, 0176764, 0001401, 0041002 - }; */ - } -} diff --git a/PUP/EFTP/EFTPServer.cs b/PUP/EFTP/EFTPServer.cs new file mode 100644 index 0000000..a2bda93 --- /dev/null +++ b/PUP/EFTP/EFTPServer.cs @@ -0,0 +1,37 @@ +using IFS.BSP; +using IFS.Logging; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.IO; + +namespace IFS.EFTP +{ + /// + /// EFTP: It's like a really limited version of BSP. + /// This is not a standalone server like FTP but provides routines for sending / receiving data + /// via FTP, so that actual servers (Boot, Printing, etc.) can serve their clients. + /// + public class EFTPServer : PUPProtocolBase + { + /// + /// Called by dispatcher to send incoming data destined for this protocol. + /// + /// + public override void RecvData(PUP p) + { + + } + } + + public class EFTPWorker + { + public EFTPWorker(BSPChannel channel) + { + + } + } +} diff --git a/PUP/Entrypoint.cs b/PUP/Entrypoint.cs index a362ff8..f8a7578 100644 --- a/PUP/Entrypoint.cs +++ b/PUP/Entrypoint.cs @@ -1,4 +1,5 @@ -using IFS.CopyDisk; +using IFS.Boot; +using IFS.CopyDisk; using IFS.FTP; using IFS.Transport; using System; @@ -27,13 +28,18 @@ namespace IFS PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Gateway Information", 2, ConnectionType.Connectionless, new GatewayInformationProtocol())); PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Misc Services", 0x4, ConnectionType.Connectionless, new MiscServicesProtocol())); PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Echo", 0x5, ConnectionType.Connectionless, new EchoProtocol())); + PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Boot", 0x10, ConnectionType.Connectionless, new BootServerProtocol())); // RTP/BSP based: PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("CopyDisk", 0x15 /* 25B */, ConnectionType.BSP, new CopyDiskServer())); PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("FTP", 0x3, ConnectionType.BSP, new FTPServer())); + // Breath Of Life + BreathOfLife breathOfLifeServer = new BreathOfLife(); + + // TODO: MAKE THIS CONFIGURABLE. - PUPProtocolDispatcher.Instance.RegisterInterface(ifaces[2]); + PUPProtocolDispatcher.Instance.RegisterInterface(ifaces[2]); while (true) { diff --git a/PUP/IFS.csproj b/PUP/IFS.csproj index e422147..a8bbbef 100644 --- a/PUP/IFS.csproj +++ b/PUP/IFS.csproj @@ -73,7 +73,8 @@ - + + @@ -81,6 +82,7 @@ + diff --git a/PUP/Logging/Log.cs b/PUP/Logging/Log.cs index c92c46d..824caf5 100644 --- a/PUP/Logging/Log.cs +++ b/PUP/Logging/Log.cs @@ -20,6 +20,7 @@ namespace IFS.Logging DirectoryServices = 0x20, PUP = 0x40, FTP = 0x80, + BreathOfLife = 0x100, All = 0x7fffffff } diff --git a/PUP/MiscServicesProtocol.cs b/PUP/MiscServicesProtocol.cs index 74252a1..20998f0 100644 --- a/PUP/MiscServicesProtocol.cs +++ b/PUP/MiscServicesProtocol.cs @@ -76,7 +76,11 @@ namespace IFS case PupType.NameLookupRequest: SendNameLookupReply(p); - break; + break; + + case PupType.SendBootFileRequest: + SendBootFile(p); + break; default: Log.Write(LogComponent.MiscServices, String.Format("Unhandled misc. protocol {0}", p.Type)); @@ -107,9 +111,7 @@ namespace IFS } private void SendAltoTimeReply(PUP p) - { - - + { // So the Alto epoch is 1/1/1901. For the time being to keep things simple we're assuming // GMT and no DST at all. TODO: make this take into account our TZ, etc. // @@ -134,8 +136,6 @@ namespace IFS UInt32 altoTime = (UInt32)timeSinceAltoEpoch.TotalSeconds; - - // Build the response data AltoTime time = new AltoTime(); time.DateTime = altoTime; @@ -237,5 +237,18 @@ namespace IFS PUPProtocolDispatcher.Instance.SendPup(errorReply); } } + + private void SendBootFile(PUP p) + { + // + // The request PUP contains the file number in the lower-order 16-bits of the pup ID. + // Assuming the number is a valid bootfile, we start sending it to the client's port via EFTP. + // + uint fileNumber = p.ID & 0xffff; + + Log.Write(LogType.Verbose, LogComponent.MiscServices, "Boot file request is for file {0}.", fileNumber); + + + } } } diff --git a/PUP/PUP.cs b/PUP/PUP.cs index 1e20a7d..e0ed88b 100644 --- a/PUP/PUP.cs +++ b/PUP/PUP.cs @@ -33,6 +33,12 @@ namespace IFS InterruptReply = 21, AMark = 22, + // EFTP types + EFTPData = 24, + EFTPAck = 25, + EFTPEnd = 26, + EFTPAbort = 27, + // Misc. Services types StringTimeRequest = 128, StringTimeReply = 129, diff --git a/PUP/PUPProtocolBase.cs b/PUP/PUPProtocolBase.cs index 9a6e747..0eeebfa 100644 --- a/PUP/PUPProtocolBase.cs +++ b/PUP/PUPProtocolBase.cs @@ -10,6 +10,7 @@ namespace IFS { Connectionless, /* echo, name resolution, etc. */ BSP, /* FTP, Telnet, CopyDisk, etc. */ + EFTP, /* EFTP-based (boot, printing) */ } public struct PUPProtocolEntry @@ -33,7 +34,7 @@ namespace IFS public UInt32 Socket; /// - /// Indicates the type of connection (connectionless or BSP-based) + /// Indicates the type of connection (connectionless, BSP-based or EFTP) /// public ConnectionType ConnectionType; diff --git a/PUP/PUPProtocolDispatcher.cs b/PUP/PUPProtocolDispatcher.cs index 37975b6..5eeacca 100644 --- a/PUP/PUPProtocolDispatcher.cs +++ b/PUP/PUPProtocolDispatcher.cs @@ -37,7 +37,9 @@ namespace IFS { // TODO: support multiple interfaces (for gateway routing, for example.) // Also, this should not be ethernet-specific. - _pupPacketInterface = new Ethernet(i); + Ethernet enet = new Ethernet(i); + _pupPacketInterface = enet as IPupPacketInterface; + _rawPacketInterface = enet as IRawPacketInterface; _pupPacketInterface.RegisterReceiveCallback(OnPupReceived); } @@ -63,6 +65,14 @@ namespace IFS _pupPacketInterface.Send(p); } + public void Send(byte[] data, byte source, byte destination, ushort frameType) + { + if (_rawPacketInterface != null) + { + _rawPacketInterface.Send(data, source, destination, frameType); + } + } + private void OnPupReceived(PUP pup) { // @@ -117,6 +127,11 @@ namespace IFS /// private IPupPacketInterface _pupPacketInterface; + /// + /// Our interface to a facility that can transmit raw Ethernet frames + /// + private IRawPacketInterface _rawPacketInterface; + /// /// Map from socket to protocol implementation /// diff --git a/PUP/Transport/Ethernet.cs b/PUP/Transport/Ethernet.cs index 9a12b49..87d48c8 100644 --- a/PUP/Transport/Ethernet.cs +++ b/PUP/Transport/Ethernet.cs @@ -43,7 +43,7 @@ namespace IFS.Transport /// /// Defines interface "to the metal" (raw ethernet frames) which may wrap the underlying transport (for example, winpcap) /// - public class Ethernet : IPupPacketInterface + public class Ethernet : IPupPacketInterface, IRawPacketInterface { public Ethernet(EthernetInterface iface) { @@ -63,7 +63,6 @@ namespace IFS.Transport BeginReceive(); } - public void Send(PUP p) { // @@ -125,6 +124,75 @@ namespace IFS.Transport } } + public void Send(byte[] data, byte source, byte destination, ushort frameType) + { + // Build the outgoing data; this is: + // 1st word: length of data following + // 2nd word: 3mbit destination / source bytes + // 3rd word: frame type (PUP) + byte[] encapsulatedFrame = new byte[6 + data.Length]; + + // 3mbit Packet length + encapsulatedFrame[0] = (byte)((data.Length / 2 + 2) >> 8); + encapsulatedFrame[1] = (byte)(data.Length / 2 + 2); + + // addressing + encapsulatedFrame[2] = destination; + encapsulatedFrame[3] = source; + + // frame type + encapsulatedFrame[4] = (byte)(frameType >> 8); + encapsulatedFrame[5] = (byte)frameType; + + // Actual data + data.CopyTo(encapsulatedFrame, 6); + + // Byte swap + encapsulatedFrame = ByteSwap(encapsulatedFrame); + + MacAddress destinationMac; + if (destination != 0xff) + { + if (_pupToEthernetMap.ContainsKey(destination)) + { + // + // Use the existing map. + // + destinationMac = _pupToEthernetMap[destination]; + } + else + { + // + // Nothing mapped for this PUP, do our best with it. + // + destinationMac = new MacAddress((UInt48)(_10mbitMACPrefix | destination)); + } + } + else + { + // 3mbit broadcast becomes 10mbit broadcast + destinationMac = new MacAddress(_10mbitBroadcast); + } + + // 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)_3mbitFrameType, + }; + + PayloadLayer payloadLayer = new PayloadLayer + { + Data = new Datagram(encapsulatedFrame), + }; + + PacketBuilder builder = new PacketBuilder(ethernetLayer, payloadLayer); + + // Send it over the 'net! + _communicator.SendPacket(builder.Build(DateTime.Now)); + } + private void ReceiveCallback(Packet p) { // @@ -292,5 +360,12 @@ namespace IFS.Transport // The type used for 3mbit frames encapsulated in 10mb frames private readonly int _3mbitFrameType = 0xbeef; // easy to identify, ostensibly unused by anything of any import + // 5 byte prefix for 3mbit->10mbit addresses when sending raw frames; this is the convention ContrAlto uses. + // TODO: this should be configurable. + private UInt48 _10mbitMACPrefix = 0x0000aa010200; // 00-00-AA is the Xerox vendor code, used just to be cute. + + // 10mbit broadcast address + private UInt48 _10mbitBroadcast = (UInt48)0xffffffffffff; + } } diff --git a/PUP/Transport/PacketInterface.cs b/PUP/Transport/PacketInterface.cs index dd638bf..6476745 100644 --- a/PUP/Transport/PacketInterface.cs +++ b/PUP/Transport/PacketInterface.cs @@ -20,4 +20,17 @@ namespace IFS.Transport void RegisterReceiveCallback(HandlePup callback); } + + /// + /// IPupPacketInterface provides an abstraction over a transport (Ethernet, IP, Carrier Pigeon) + /// which can provide encapsulation for raw Ethernet frames. + /// + /// For the time being, this exists only to provide support for BreathOfLife packets (the only non-PUP + /// Ethernet Packet the IFS suite deals with). This only requires being able to send packets, so no + /// receive is implemented. + /// + public interface IRawPacketInterface + { + void Send(byte[] data, byte source, byte destination, ushort frameType); + } }