1
0
mirror of https://github.com/livingcomputermuseum/IFS.git synced 2026-03-08 03:49:22 +00:00

Implemented BreathOfLife, begun EFTP.

This commit is contained in:
Josh Dersch
2016-02-15 11:01:23 -08:00
parent 602de14881
commit bf7aa94d4d
14 changed files with 309 additions and 81 deletions

View File

@@ -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
}
}
/// <summary>
/// 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.
/// </summary>
private void WaitForAck()
{
//

24
PUP/Boot/BootServer.cs Normal file
View File

@@ -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()
{
}
/// <summary>
/// Called by dispatcher to send incoming data destined for this protocol
/// </summary>
/// <param name="p"></param>
public override void RecvData(PUP p)
{
}
}
}

95
PUP/Boot/BreathOfLife.cs Normal file
View File

@@ -0,0 +1,95 @@
using IFS.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace IFS
{
/// <summary>
/// Implements the BreathOfLife services.
/// It spins up a worker thread that wakes up every few seconds and broadcasts
/// a BreathOfLife packet.
/// </summary>
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
/// <summary>
/// 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.
/// </summary>
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,
};
}
}

View File

@@ -1,64 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IFS
{
/// <summary>
/// Implements the BreathOfLife services.
/// It spins up a worker thread that wakes up every few seconds and broadcasts
/// a BreathOfLife packet.
/// </summary>
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
}; */
}
}

37
PUP/EFTP/EFTPServer.cs Normal file
View File

@@ -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
{
/// <summary>
/// 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.
/// </summary>
public class EFTPServer : PUPProtocolBase
{
/// <summary>
/// Called by dispatcher to send incoming data destined for this protocol.
/// </summary>
/// <param name="p"></param>
public override void RecvData(PUP p)
{
}
}
public class EFTPWorker
{
public EFTPWorker(BSPChannel channel)
{
}
}
}

View File

@@ -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)
{

View File

@@ -73,7 +73,8 @@
</ItemGroup>
<ItemGroup>
<Compile Include="BCPLString.cs" />
<Compile Include="BreathOfLife.cs" />
<Compile Include="Boot\BootServer.cs" />
<Compile Include="Boot\BreathOfLife.cs" />
<Compile Include="BSP\BSPChannel.cs" />
<Compile Include="BSP\BSPManager.cs" />
<Compile Include="Configuration.cs" />
@@ -81,6 +82,7 @@
<Compile Include="CopyDisk\DiabloPack.cs" />
<Compile Include="DirectoryServices.cs" />
<Compile Include="EchoProtocol.cs" />
<Compile Include="EFTP\EFTPServer.cs" />
<Compile Include="Entrypoint.cs" />
<Compile Include="FTP\FTPServer.cs" />
<Compile Include="FTP\PropertyList.cs" />

View File

@@ -20,6 +20,7 @@ namespace IFS.Logging
DirectoryServices = 0x20,
PUP = 0x40,
FTP = 0x80,
BreathOfLife = 0x100,
All = 0x7fffffff
}

View File

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

View File

@@ -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,

View File

@@ -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;
/// <summary>
/// Indicates the type of connection (connectionless or BSP-based)
/// Indicates the type of connection (connectionless, BSP-based or EFTP)
/// </summary>
public ConnectionType ConnectionType;

View File

@@ -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
/// </summary>
private IPupPacketInterface _pupPacketInterface;
/// <summary>
/// Our interface to a facility that can transmit raw Ethernet frames
/// </summary>
private IRawPacketInterface _rawPacketInterface;
/// <summary>
/// Map from socket to protocol implementation
/// </summary>

View File

@@ -43,7 +43,7 @@ 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 : 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;
}
}

View File

@@ -20,4 +20,17 @@ namespace IFS.Transport
void RegisterReceiveCallback(HandlePup callback);
}
/// <summary>
/// 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.
/// </summary>
public interface IRawPacketInterface
{
void Send(byte[] data, byte source, byte destination, ushort frameType);
}
}