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