mirror of
https://github.com/livingcomputermuseum/IFS.git
synced 2026-04-09 22:28:00 +00:00
Added gateway routing services. Minor cleanup.
This commit is contained in:
@@ -18,10 +18,6 @@
|
||||
using IFS.Gateway;
|
||||
using IFS.Logging;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace IFS
|
||||
@@ -35,7 +31,11 @@ namespace IFS
|
||||
{
|
||||
public BreathOfLife()
|
||||
{
|
||||
Log.Write(LogType.Verbose, LogComponent.BreathOfLife, "Breath Of Life service starting. Broadcast interval is {0} milliseconds.", _bolPacketDelay);
|
||||
Log.Write(LogType.Verbose,
|
||||
LogComponent.BreathOfLife,
|
||||
"Breath Of Life service starting. Broadcast interval is {0} milliseconds.",
|
||||
Configuration.BOLDelay);
|
||||
|
||||
_bolThread = new Thread(BreathOfLifeThread);
|
||||
_bolThread.Start();
|
||||
}
|
||||
@@ -51,7 +51,8 @@ namespace IFS
|
||||
{
|
||||
//
|
||||
// Send BOL
|
||||
//
|
||||
//
|
||||
|
||||
Router.Instance.Send(_bolPacket, DirectoryServices.Instance.LocalHost, _bolAddress, _bolPacketType);
|
||||
|
||||
Log.Write(LogType.Verbose, LogComponent.BreathOfLife, "Breath Of Life packet sent.");
|
||||
@@ -59,7 +60,7 @@ namespace IFS
|
||||
//
|
||||
// Go to sleep.
|
||||
//
|
||||
Thread.Sleep(_bolPacketDelay);
|
||||
Thread.Sleep(Configuration.BOLDelay);
|
||||
|
||||
//
|
||||
// That's it. Go home, do it again.
|
||||
@@ -73,8 +74,6 @@ namespace IFS
|
||||
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 router adds those two words.
|
||||
|
||||
@@ -11,8 +11,11 @@
|
||||
# all numbers are in octal.
|
||||
#
|
||||
|
||||
1# ifs
|
||||
42# Muffin
|
||||
43# Pumpkin
|
||||
44# Frunobulax
|
||||
45# Phydeaux
|
||||
# Local Network
|
||||
1#1# lcm
|
||||
1#100# Thacker
|
||||
1#42# Kay
|
||||
1#43# Boggs
|
||||
1#44# Kaehler
|
||||
1#45# Lampson
|
||||
|
||||
|
||||
@@ -4,17 +4,36 @@
|
||||
# All numbers are in decimal.
|
||||
#
|
||||
|
||||
# Normal configuration
|
||||
# Directory configuration
|
||||
FTPRoot = c:\ifs\ftp
|
||||
CopyDiskRoot = c:\ifs\copydisk
|
||||
BootRoot = c:\ifs\boot
|
||||
MailRoot = c:\ifs\mail
|
||||
|
||||
# InterfaceType defines the type of interface for local networking,
|
||||
# (either RAW or UDP)
|
||||
# and must be the same as other devices or software on the network that
|
||||
# wishes to talk to this server.
|
||||
InterfaceType = raw
|
||||
|
||||
# The name of the network interface to use for local networking.
|
||||
# This is the name reported by "ipconfig"
|
||||
InterfaceName = Ethernet
|
||||
|
||||
# Note that UDPPort is for the local network UDP transport,
|
||||
# not for gateway ports!
|
||||
# (gateway ports are specified in networks.txt)
|
||||
UDPPort = 42424
|
||||
|
||||
# Defines the address for this IFS server.
|
||||
# An entry for this IFS server's network must be
|
||||
# present in networks.txt
|
||||
ServerNetwork = 1
|
||||
ServerHost = 1
|
||||
|
||||
# Delay between BreathOfLife packet broadcasts, in milliseconds.
|
||||
BOLDelay = 5000
|
||||
|
||||
# Debug settings
|
||||
LogTypes = None
|
||||
LogComponents = None
|
||||
LogTypes = All
|
||||
LogComponents = Routing
|
||||
|
||||
20
PUP/Conf/networks.txt
Normal file
20
PUP/Conf/networks.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
# networks.txt:
|
||||
#
|
||||
# This identifies known networks and provides the IPs for their IFS gateway servers.
|
||||
#
|
||||
# Each line in this file is of the format
|
||||
# <inter-network number> <IFS host IP or hostname[:port]>
|
||||
#
|
||||
# for example:
|
||||
# 5# 132.22.110.96
|
||||
# would define network 5's gateway as 132.22.110.96 with the default port of 42425.
|
||||
#
|
||||
# 12# myhostname.net:6666
|
||||
# defines network 12's gateway at myhostname.net, port 6666.
|
||||
#
|
||||
|
||||
#
|
||||
# There must be an entry present for our local network, or else routing will be disabled.
|
||||
#
|
||||
|
||||
# 1# <your IP here>
|
||||
@@ -79,6 +79,7 @@ namespace IFS
|
||||
// Set to default.
|
||||
UDPPort = 42424;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -141,6 +142,11 @@ namespace IFS
|
||||
/// </summary>
|
||||
public static readonly LogType LogTypes;
|
||||
|
||||
/// <summary>
|
||||
/// The delay (in msec) between Breath Of Life packets
|
||||
/// </summary>
|
||||
public static readonly int BOLDelay;
|
||||
|
||||
|
||||
private static void ReadConfiguration()
|
||||
{
|
||||
|
||||
@@ -1178,7 +1178,9 @@ namespace IFS.FTP
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
SendFTPNoResponse(NoCode.AccessDenied, "Invalid username or password.");
|
||||
// Default to guest user.
|
||||
user = UserToken.Guest;
|
||||
//SendFTPNoResponse(NoCode.AccessDenied, "Invalid username or password.");
|
||||
}
|
||||
|
||||
return user;
|
||||
|
||||
@@ -15,13 +15,9 @@
|
||||
along with IFS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using IFS.Gateway;
|
||||
using IFS.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
namespace IFS.Gateway
|
||||
{
|
||||
@@ -40,11 +36,8 @@ namespace IFS.Gateway
|
||||
{
|
||||
public GatewayInformationProtocol()
|
||||
{
|
||||
//
|
||||
// TODO (once routing is implemented):
|
||||
// load host tables, etc.
|
||||
// spin up thread that spits out a GatewayInformation PUP periodically.
|
||||
//
|
||||
_gatewayInfoThread = new Thread(GatewayInformationWorker);
|
||||
_gatewayInfoThread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -59,8 +52,13 @@ namespace IFS.Gateway
|
||||
SendGatewayInformationResponse(p);
|
||||
break;
|
||||
|
||||
case PupType.GatewayInformationResponse:
|
||||
// Currently a no-op.
|
||||
Log.Write(LogComponent.MiscServices, String.Format("Gateway Information handler unimplemented."));
|
||||
break;
|
||||
|
||||
default:
|
||||
Log.Write(LogComponent.MiscServices, String.Format("Unhandled Gateway protocol {0}", p.Type));
|
||||
Log.Write(LogComponent.MiscServices, String.Format("Unhandled Gateway protocol {0} ({1})", p.Type, (int)p.Type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -87,23 +85,93 @@ namespace IFS.Gateway
|
||||
// inaccessible.
|
||||
//
|
||||
|
||||
// Right now, we know of only one network (our own) and we are directly connected to it.
|
||||
//
|
||||
GatewayInformation info = new GatewayInformation();
|
||||
info.TargetNet = DirectoryServices.Instance.LocalNetwork;
|
||||
info.GatewayNet = DirectoryServices.Instance.LocalNetwork;
|
||||
info.GatewayHost = DirectoryServices.Instance.LocalNetwork;
|
||||
info.HopCount = 0;
|
||||
|
||||
byte[] infoArray = GetGatewayInformationArray();
|
||||
|
||||
PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
|
||||
|
||||
// Response must contain our network number; this is used to tell clients what network they're on if they don't already know.
|
||||
PUPPort remotePort = new PUPPort(DirectoryServices.Instance.LocalNetwork, p.SourcePort.Host, p.SourcePort.Socket);
|
||||
|
||||
PUP response = new PUP(PupType.GatewayInformationResponse, p.ID, remotePort, localPort, Serializer.Serialize(info));
|
||||
PUP response = new PUP(PupType.GatewayInformationResponse, p.ID, remotePort, localPort, infoArray);
|
||||
|
||||
Router.Instance.SendPup(response);
|
||||
}
|
||||
|
||||
private static byte[] GetGatewayInformationArray()
|
||||
{
|
||||
//
|
||||
// We build the gateway information response from the RoutingTable that the Router maintains.
|
||||
// Since we do not at this time implement multi-hop routing, all networks known by the Router
|
||||
// are assumed to be directly connected and to have a hop-count of 0.
|
||||
//
|
||||
byte[] knownNetworks = Router.Instance.RoutingTable.GetKnownNetworks();
|
||||
|
||||
byte[] infoArray = new byte[knownNetworks.Length * 4];
|
||||
|
||||
for (int i = 0; i < knownNetworks.Length; i++)
|
||||
{
|
||||
GatewayInformation info = new GatewayInformation();
|
||||
info.TargetNet = knownNetworks[i];
|
||||
info.GatewayNet = DirectoryServices.Instance.LocalNetwork;
|
||||
info.GatewayHost = DirectoryServices.Instance.LocalHost;
|
||||
info.HopCount = 0; // all networks are directly connected
|
||||
|
||||
byte[] entry = Serializer.Serialize(info);
|
||||
|
||||
entry.CopyTo(infoArray, i * 4);
|
||||
}
|
||||
|
||||
return infoArray;
|
||||
}
|
||||
|
||||
private void GatewayInformationWorker()
|
||||
{
|
||||
uint infoPupID = (uint)(new Random().Next());
|
||||
while (true)
|
||||
{
|
||||
//
|
||||
// From gatewayinformation.press:
|
||||
// "Each gateway host must also periodically broadcast Gateway Information Pups, as described above,
|
||||
// on all directly-connected networks. The frequency of this broadcast should be approximately one
|
||||
// every 30 seconds, and immediately whenever the gateway’s own routing table changes (see below).
|
||||
// These Pups should be sent from socket 2 to socket 2."
|
||||
//
|
||||
// At this time, we don't do anything with gateway information PUPs that we receive -- they could
|
||||
// at some point be used as originally intended, to dynamically update routing tables, but it would
|
||||
// require some serious security investments to make sure that the tables don't get poisoned.
|
||||
// However, even though we don't use them, some Alto software does. For example, the PUP libraries
|
||||
// used by Mazewar expect to get periodic updates or eventually it will assume the route is no longer
|
||||
// viable and drop connections.
|
||||
//
|
||||
|
||||
// Delay 30 seconds
|
||||
Thread.Sleep(30000);
|
||||
|
||||
byte[] infoArray = GetGatewayInformationArray();
|
||||
|
||||
// From us, on socket 2
|
||||
PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, 2);
|
||||
|
||||
//
|
||||
// The set of known networks is by default the set of directly-connected networks.
|
||||
//
|
||||
byte[] knownNetworks = Router.Instance.RoutingTable.GetKnownNetworks();
|
||||
|
||||
foreach (byte network in knownNetworks)
|
||||
{
|
||||
// Send a broadcast to the specified network
|
||||
PUPPort remotePort = new PUPPort(network, 0, 2);
|
||||
|
||||
PUP infoPup = new PUP(PupType.GatewayInformationResponse, infoPupID++, remotePort, localPort, infoArray);
|
||||
Router.Instance.SendPup(infoPup);
|
||||
|
||||
Log.Write(LogComponent.MiscServices, "Gateway Information packet sent to network {0}", network);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Thread _gatewayInfoThread;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,17 @@
|
||||
using IFS.Logging;
|
||||
using IFS.Transport;
|
||||
using PcapDotNet.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
namespace IFS.Gateway
|
||||
{
|
||||
public delegate void RoutePupCallback(PUP pup);
|
||||
public delegate void RoutePupCallback(PUP pup, bool route);
|
||||
|
||||
/// <summary>
|
||||
/// Implements gateway services, routing PUPs intended for other networks to
|
||||
@@ -30,13 +36,45 @@ namespace IFS.Gateway
|
||||
/// This is one layer above the physical transport layer (ethernet, udp) and
|
||||
/// is below the protocol layer.
|
||||
///
|
||||
/// The routing is currently a stub implmentation, and only handles PUPs destined for our own network.
|
||||
/// Routed PUPs are transferred over UDP, with the intent to connect other IFS
|
||||
/// gateway servers (and thus the networks they serve) to each other either
|
||||
/// over the Internet or over a local network.
|
||||
/// Since we're sending these routed PUPs via TCP/IP, it makes little sense
|
||||
/// to support multi-hop PUP routing and since I tend to err on the side of simplicity
|
||||
/// in this implementation that's what's been implemented here. Each IFS network
|
||||
/// known to the Router is assumed to be directly connected.
|
||||
/// </summary>
|
||||
public class Router
|
||||
{
|
||||
private Router()
|
||||
{
|
||||
_localProtocolDispatcher = new PUPProtocolDispatcher();
|
||||
_routingTable = new RoutingTable();
|
||||
|
||||
//
|
||||
// Look up our own network in the table and get our port.
|
||||
// If we don't have an entry in the table, disable routing.
|
||||
//
|
||||
RoutingTableEntry ourNetwork = _routingTable.GetAddressForNetworkNumber(DirectoryServices.Instance.LocalNetwork);
|
||||
|
||||
_gatewayUdpClientLock = new ReaderWriterLockSlim();
|
||||
|
||||
if (ourNetwork == null)
|
||||
{
|
||||
Log.Write(LogType.Warning,
|
||||
LogComponent.Routing,
|
||||
"networks.txt does not contain a definition for our network ({0}). Gateway routing disabled.",
|
||||
DirectoryServices.Instance.LocalNetwork);
|
||||
|
||||
_gatewayUdpClient = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_gatewayUdpPort = ourNetwork.Port;
|
||||
|
||||
// Start the external network receiver.
|
||||
BeginExternalReceive();
|
||||
}
|
||||
}
|
||||
|
||||
public static Router Instance
|
||||
@@ -47,10 +85,25 @@ namespace IFS.Gateway
|
||||
}
|
||||
}
|
||||
|
||||
public RoutingTable RoutingTable
|
||||
{
|
||||
get
|
||||
{
|
||||
return _routingTable;
|
||||
}
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
_localProtocolDispatcher.Shutdown();
|
||||
_pupPacketInterface.Shutdown();
|
||||
|
||||
if (_gatewayUdpClient != null)
|
||||
{
|
||||
_gatewayUdpClientLock.EnterWriteLock();
|
||||
_gatewayUdpClient.Close();
|
||||
_gatewayUdpClientLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterRAWInterface(LivePacketDevice iface)
|
||||
@@ -59,7 +112,7 @@ namespace IFS.Gateway
|
||||
|
||||
_pupPacketInterface = enet;
|
||||
_rawPacketInterface = enet;
|
||||
_pupPacketInterface.RegisterRouterCallback(RouteIncomingPacket);
|
||||
_pupPacketInterface.RegisterRouterCallback(RouteIncomingLocalPacket);
|
||||
}
|
||||
|
||||
public void RegisterUDPInterface(NetworkInterface iface)
|
||||
@@ -68,7 +121,7 @@ namespace IFS.Gateway
|
||||
|
||||
_pupPacketInterface = udp;
|
||||
_rawPacketInterface = udp;
|
||||
_pupPacketInterface.RegisterRouterCallback(RouteIncomingPacket);
|
||||
_pupPacketInterface.RegisterRouterCallback(RouteIncomingLocalPacket);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -77,7 +130,7 @@ namespace IFS.Gateway
|
||||
/// <param name="p"></param>
|
||||
public void SendPup(PUP p)
|
||||
{
|
||||
RouteOutgoingPacket(p);
|
||||
RouteOutgoingPacket(p);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -101,30 +154,251 @@ namespace IFS.Gateway
|
||||
/// <param name="p"></param>
|
||||
private void RouteOutgoingPacket(PUP p)
|
||||
{
|
||||
// For now, we send the packet out without performing any routing.
|
||||
_pupPacketInterface.Send(p);
|
||||
//
|
||||
// Check the destination network. If it's 0 (meaning the sender doesn't know
|
||||
// what network it's on, or wants it to go out to whatever network it's currently
|
||||
// connected to) or it's destined for our network, we send it out directly through
|
||||
// the local network interface.
|
||||
//
|
||||
if (p.DestinationPort.Network == 0 ||
|
||||
p.DestinationPort.Network == DirectoryServices.Instance.LocalNetwork)
|
||||
{
|
||||
_pupPacketInterface.Send(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Not for our network -- see if we know what network this is going to.
|
||||
//
|
||||
RoutePacketExternally(p);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Routes a newly received packet to the proper destination host.
|
||||
/// Routes a locally received PUP to the proper destination host.
|
||||
/// </summary>
|
||||
/// <param name="pup"></param>
|
||||
public void RouteIncomingPacket(PUP pup)
|
||||
public void RouteIncomingLocalPacket(PUP pup, bool route)
|
||||
{
|
||||
//
|
||||
// Check the network -- if this is network zero (coming from a host that doesn't yet know what
|
||||
// network it's on, or specifying the current network) or our network, we will pass it on to the protocol suite.
|
||||
// Check the network -- if it specifies network zero (coming from a host that doesn't yet know what
|
||||
// network it's on, or specifying the current network) or our network
|
||||
// we will pass it on to the protocol suite.
|
||||
//
|
||||
if (pup.DestinationPort.Network == 0 || pup.DestinationPort.Network == DirectoryServices.Instance.LocalHostAddress.Network)
|
||||
{
|
||||
_localProtocolDispatcher.ReceivePUP(pup);
|
||||
}
|
||||
else if (route)
|
||||
{
|
||||
//
|
||||
// Not for our network -- see if we know where to route it.
|
||||
//
|
||||
RoutePacketExternally(pup);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not for our network.
|
||||
// For now, we will drop the packet. Once we implement
|
||||
// Gateway services we will handle these appropriately.)
|
||||
Log.Write(LogType.Verbose, LogComponent.Ethernet, "PUP is for network {0}, dropping.", pup.DestinationPort.Network);
|
||||
//
|
||||
// Not local, and we were asked not to route this PUP, so drop it on the floor.
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
private void RoutePacketExternally(PUP p)
|
||||
{
|
||||
RoutingTableEntry destinationNetworkEntry = _routingTable.GetAddressForNetworkNumber(p.DestinationPort.Network);
|
||||
|
||||
if (destinationNetworkEntry != null)
|
||||
{
|
||||
//
|
||||
// Send this out through the external network interface.
|
||||
//
|
||||
if (_gatewayUdpClient != null)
|
||||
{
|
||||
Log.Write(LogType.Verbose,
|
||||
LogComponent.Routing,
|
||||
"-> PUP routed to {0}:{1}, type {2} source {3} destination {4}.",
|
||||
destinationNetworkEntry.HostAddress,
|
||||
destinationNetworkEntry.Port,
|
||||
p.Type,
|
||||
p.SourcePort,
|
||||
p.DestinationPort);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
_gatewayUdpClientLock.EnterWriteLock();
|
||||
_gatewayUdpClient.Send(p.RawData, p.RawData.Length, destinationNetworkEntry.HostAddress, destinationNetworkEntry.Port);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write(LogType.Error,
|
||||
LogComponent.Routing,
|
||||
"Gateway UDP client send failed, error {0}. Continuing.",
|
||||
e.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_gatewayUdpClientLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// We don't know where to send this, drop it instead.
|
||||
//
|
||||
Log.Write(
|
||||
LogType.Verbose,
|
||||
LogComponent.Routing,
|
||||
"Outgoing PUP is for unknown network {0}, dropping.", p.DestinationPort.Network);
|
||||
}
|
||||
}
|
||||
|
||||
private void RouteIncomingExternalPacket(PUP p)
|
||||
{
|
||||
//
|
||||
// Ensure that this is for our network; otherwise this has been misrouted.
|
||||
// (Since we don't do multi-hop routing, any packet coming in through a gateway
|
||||
// interface must be destined for us.)
|
||||
//
|
||||
if (p.DestinationPort.Network == DirectoryServices.Instance.LocalNetwork)
|
||||
{
|
||||
//
|
||||
// Send it out on the local network for anyone to see.
|
||||
//
|
||||
_pupPacketInterface.Send(p);
|
||||
|
||||
//
|
||||
// And if it's intended for us (the IFS server) let our services have a crack at it, too.
|
||||
//
|
||||
if (p.DestinationPort.Host == DirectoryServices.Instance.LocalHostAddress.Host || // us specifically
|
||||
p.DestinationPort.Host == 0) // broadcast
|
||||
{
|
||||
_localProtocolDispatcher.ReceivePUP(p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// This was misrouted. Log it and drop.
|
||||
//
|
||||
Log.Write(LogType.Error,
|
||||
LogComponent.Routing,
|
||||
"PUP was misrouted; intended for network {0}, host {1}",
|
||||
p.DestinationPort.Network,
|
||||
p.DestinationPort.Host);
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginExternalReceive()
|
||||
{
|
||||
CreateGatewayReceiver();
|
||||
|
||||
// Kick off receive thread.
|
||||
_gatewayReceiveThread = new Thread(GatewayReceiveThread);
|
||||
_gatewayReceiveThread.Start();
|
||||
}
|
||||
|
||||
private void CreateGatewayReceiver()
|
||||
{
|
||||
_gatewayUdpClientLock.EnterWriteLock();
|
||||
|
||||
if (_gatewayUdpClient != null)
|
||||
{
|
||||
_gatewayUdpClient.Close();
|
||||
}
|
||||
|
||||
_gatewayUdpClient = new UdpClient(_gatewayUdpPort, AddressFamily.InterNetwork);
|
||||
_gatewayUdpClient.Client.Blocking = true;
|
||||
|
||||
_gatewayUdpClientLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker thread for UDP packet receipt.
|
||||
/// </summary>
|
||||
private void GatewayReceiveThread()
|
||||
{
|
||||
// Just call Receive forever, that's it. This will never return.
|
||||
// (probably need to make this more elegant so we can tear down the thread
|
||||
// properly.)
|
||||
Log.Write(LogComponent.Routing, "Gateway UDP Receiver thread started for port {0}.", _gatewayUdpPort);
|
||||
|
||||
IPEndPoint groupEndPoint = new IPEndPoint(IPAddress.Any, Configuration.UDPPort);
|
||||
|
||||
while (true)
|
||||
{
|
||||
byte[] data = null;
|
||||
try
|
||||
{
|
||||
data = _gatewayUdpClient.Receive(ref groupEndPoint);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//
|
||||
// This can happen on occasion for reasons I don't quite understand.
|
||||
// We will log the failure and attempt to continue.
|
||||
//
|
||||
Log.Write(LogType.Error,
|
||||
LogComponent.Routing,
|
||||
"Gateway UDP client receive failed, error {0}. Continuing.",
|
||||
e.Message);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1) validate that the packet came in on the right port
|
||||
// 2) validate packet
|
||||
// 3) get a PUP out of it
|
||||
// 4) send to RouteIncomingPacket.
|
||||
// 5) do it again.
|
||||
if (groupEndPoint.Port == _gatewayUdpPort)
|
||||
{
|
||||
if (data.Length < PUP.PUP_HEADER_SIZE + PUP.PUP_CHECKSUM_SIZE ||
|
||||
data.Length > PUP.MAX_PUP_SIZE + PUP.PUP_HEADER_SIZE + PUP.PUP_CHECKSUM_SIZE)
|
||||
{
|
||||
Log.Write(LogType.Error, LogComponent.Routing, "External PUP has an invalid size ({0}). Dropping.", data.Length);
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
//
|
||||
// See if we can get a PUP out of this.
|
||||
//
|
||||
PUP externalPUP = new PUP(new MemoryStream(data), data.Length);
|
||||
|
||||
//
|
||||
// TODO: should technically bump the PUP's TransportControl field up;
|
||||
// really need to rewrite the PUP class to make this possible without
|
||||
// building up an entirely new PUP.
|
||||
//
|
||||
RouteIncomingExternalPacket(externalPUP);
|
||||
|
||||
Log.Write(LogType.Verbose,
|
||||
LogComponent.Routing,
|
||||
"<- External PUP received from {0}:{1}, type {2} source {3} destination {4}. Routing to local network.",
|
||||
groupEndPoint.Address,
|
||||
groupEndPoint.Port,
|
||||
externalPUP.Type,
|
||||
externalPUP.SourcePort,
|
||||
externalPUP.DestinationPort);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write(LogType.Error, LogComponent.Routing, "Error handling external PUP: {0}", e.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Write(LogType.Verbose,
|
||||
LogComponent.Routing,
|
||||
"Packet from {0} received on wrong port ({1}), expected {2}.",
|
||||
groupEndPoint.Address,
|
||||
groupEndPoint.Port,
|
||||
_gatewayUdpPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,8 +412,241 @@ namespace IFS.Gateway
|
||||
/// </summary>
|
||||
private IRawPacketInterface _rawPacketInterface;
|
||||
|
||||
/// <summary>
|
||||
/// Our UdpClient for sending PUPs to external networks.
|
||||
/// </summary>
|
||||
private UdpClient _gatewayUdpClient;
|
||||
|
||||
/// <summary>
|
||||
/// Used to ensure thread-safety for the UDP client (which is not thread-safe)
|
||||
/// </summary>
|
||||
private ReaderWriterLockSlim _gatewayUdpClientLock;
|
||||
|
||||
/// <summary>
|
||||
/// Thread to watch for incoming external PUPs
|
||||
/// </summary>
|
||||
private Thread _gatewayReceiveThread;
|
||||
|
||||
/// <summary>
|
||||
/// The UDP port we use for our gateway, as specified in networks.txt
|
||||
/// </summary>
|
||||
private int _gatewayUdpPort;
|
||||
|
||||
private RoutingTable _routingTable;
|
||||
|
||||
private static Router _router = new Router();
|
||||
|
||||
private PUPProtocolDispatcher _localProtocolDispatcher;
|
||||
}
|
||||
|
||||
public class RoutingTableEntry
|
||||
{
|
||||
public RoutingTableEntry(string hostAddress, int port)
|
||||
{
|
||||
HostAddress = hostAddress;
|
||||
Port = port;
|
||||
}
|
||||
|
||||
public string HostAddress;
|
||||
public int Port;
|
||||
}
|
||||
|
||||
public class RoutingTable
|
||||
{
|
||||
public RoutingTable()
|
||||
{
|
||||
LoadRoutingTables();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the host address of the gateway server for the specified inter-
|
||||
/// network number. Returns null if no address is defined.
|
||||
/// </summary>
|
||||
/// <param name="networkNumber"></param>
|
||||
/// <returns></returns>
|
||||
public RoutingTableEntry GetAddressForNetworkNumber(byte networkNumber)
|
||||
{
|
||||
if (_addressTable.ContainsKey(networkNumber))
|
||||
{
|
||||
return _addressTable[networkNumber];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the inter-network number for the network served by the given
|
||||
/// host address. Returns 0 (undefined network) if no number is defined
|
||||
/// for the given address.
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <returns></returns>
|
||||
public byte GetNetworkNumberForAddress(RoutingTableEntry address)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array containing the numbers of networks that have been defined.
|
||||
/// This is used by Gateway Services to return routing information.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public byte[] GetKnownNetworks()
|
||||
{
|
||||
byte[] networks = new byte[_addressTable.Keys.Count];
|
||||
_addressTable.Keys.CopyTo(networks, 0);
|
||||
|
||||
return networks;
|
||||
}
|
||||
|
||||
private void LoadRoutingTables()
|
||||
{
|
||||
_addressTable = new Dictionary<byte, RoutingTableEntry>();
|
||||
//
|
||||
// Read in the routing tables from Conf\networks.txt.
|
||||
//
|
||||
using (StreamReader sr = new StreamReader(Path.Combine("Conf", "networks.txt")))
|
||||
{
|
||||
int lineNumber = 0;
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
lineNumber++;
|
||||
|
||||
//
|
||||
// A line is either:
|
||||
// '#' followed by comment to EOL
|
||||
// <inter-network name> <hostname>
|
||||
// Any whitespace is ignored
|
||||
//
|
||||
// Format for Inter-Network name expressions for a network is:
|
||||
// network# (to specify hosts on another network)
|
||||
//
|
||||
string line = sr.ReadLine().Trim().ToLowerInvariant();
|
||||
|
||||
if (line.StartsWith("#") || String.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
// Comment or empty, just ignore
|
||||
continue;
|
||||
}
|
||||
|
||||
// Tokenize on whitespace
|
||||
string[] tokens = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// We need exactly two tokens (inter-network name and one hostname)
|
||||
if (tokens.Length != 2)
|
||||
{
|
||||
// Log warning and continue.
|
||||
Log.Write(LogType.Warning, LogComponent.Routing,
|
||||
"networks.txt line {0}: Invalid syntax.", lineNumber);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// First token should be an inter-network name, which should end with '#'.
|
||||
if (!tokens[0].EndsWith("#"))
|
||||
{
|
||||
// Log warning and continue.
|
||||
Log.Write(LogType.Warning, LogComponent.Routing,
|
||||
"networks.txt line {0}: Improperly formed inter-network name '{1}'.", lineNumber, tokens[0]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// tokenize on '#'
|
||||
string[] networkTokens = tokens[0].Split(new char[] { '#' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
byte networkNumber = 0;
|
||||
// 1 token means a network name, anything else is invalid here
|
||||
if (networkTokens.Length == 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
networkNumber = Convert.ToByte(networkTokens[0], 8);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Log warning and continue.
|
||||
Log.Write(LogType.Warning, LogComponent.Routing,
|
||||
"hosts.txt line {0}: Invalid network number in inter-network address '{1}'.", lineNumber, tokens[0]);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Log warning and continue.
|
||||
Log.Write(LogType.Warning, LogComponent.Routing,
|
||||
"networks.txt line {0}: Invalid network number in inter-network address '{1}'.", lineNumber, tokens[0]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_addressTable.ContainsKey(networkNumber))
|
||||
{
|
||||
// Log warning and continue.
|
||||
Log.Write(LogType.Warning, LogComponent.Routing,
|
||||
"networks.txt line {0}: Duplicate network entry '{1}'.", lineNumber, networkNumber);
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// The 2nd token contains the hostname for the network gateway.
|
||||
// This could be a domain name or an IP address (V4 only for the moment)
|
||||
// with or without port.
|
||||
//
|
||||
string hostName = tokens[1];
|
||||
|
||||
string[] hostNameParts = hostName.Split(':');
|
||||
|
||||
string address = String.Empty;
|
||||
int port = 0;
|
||||
|
||||
if (hostNameParts.Length == 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Hostname + port
|
||||
address = hostNameParts[0];
|
||||
port = int.Parse(hostNameParts[1]);
|
||||
|
||||
if (port <= 0 || port > 65535)
|
||||
{
|
||||
throw new InvalidOperationException("Port number is out of range.");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Write(LogType.Warning, LogComponent.Routing,
|
||||
"networks.txt line {0}: Invalid hostname specification '{1}'.", lineNumber, hostName);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (hostNameParts.Length == 1)
|
||||
{
|
||||
address = hostNameParts[0];
|
||||
port = DefaultUDPPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Write(LogType.Warning, LogComponent.Routing,
|
||||
"networks.txt line {0}: Invalid hostname specification '{1}'.", lineNumber, hostName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add entry to the table.
|
||||
_addressTable.Add(networkNumber, new RoutingTableEntry(address, port));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default UDP port for external networks.
|
||||
/// </summary>
|
||||
private static readonly int DefaultUDPPort = 42425;
|
||||
|
||||
private Dictionary<byte, RoutingTableEntry> _addressTable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -136,6 +136,9 @@
|
||||
<Content Include="Conf\bootdirectory.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Conf\networks.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="readme.txt" />
|
||||
<None Include="Conf\ifs.cfg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
||||
@@ -42,8 +42,9 @@ namespace IFS.Logging
|
||||
BootServer = 0x400,
|
||||
UDP = 0x800,
|
||||
Mail = 0x1000,
|
||||
Routing = 0x2000,
|
||||
|
||||
Configuration = 0x1000,
|
||||
Configuration = 0x4000,
|
||||
All = 0x7fffffff
|
||||
}
|
||||
|
||||
@@ -99,7 +100,7 @@ namespace IFS.Logging
|
||||
{
|
||||
//
|
||||
// My log has something to tell you...
|
||||
Console.WriteLine(component.ToString() + ": " + message, args);
|
||||
Console.WriteLine("[" + DateTime.Now + "] " + component.ToString() + ": " + message, args);
|
||||
|
||||
if (_logStream != null)
|
||||
{
|
||||
|
||||
35
PUP/PUP.cs
35
PUP/PUP.cs
@@ -164,7 +164,10 @@ namespace IFS
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("Net {0} Host {1} Socket {2}", Network, Host, Socket);
|
||||
return String.Format("Net {0} Host {1} Socket {2}",
|
||||
Helpers.ToOctal(Network),
|
||||
Helpers.ToOctal(Host),
|
||||
Helpers.ToOctal((int)Socket));
|
||||
}
|
||||
|
||||
public byte Network;
|
||||
@@ -188,7 +191,7 @@ namespace IFS
|
||||
/// TODO: Update to use Serialization code rather than packing bytes by hand.
|
||||
/// </param>
|
||||
///
|
||||
public PUP(PupType type, UInt32 id, PUPPort destination, PUPPort source, byte[] contents, bool contentsContainsGarbageByte)
|
||||
public PUP(PupType type, byte transportControl, UInt32 id, PUPPort destination, PUPPort source, byte[] contents, bool contentsContainsGarbageByte)
|
||||
{
|
||||
_rawData = null;
|
||||
|
||||
@@ -208,7 +211,7 @@ namespace IFS
|
||||
throw new InvalidOperationException("Odd content length with garbage byte specified.");
|
||||
}
|
||||
|
||||
TransportControl = 0;
|
||||
TransportControl = transportControl;
|
||||
Type = type;
|
||||
ID = id;
|
||||
DestinationPort = destination;
|
||||
@@ -246,6 +249,21 @@ namespace IFS
|
||||
Helpers.WriteUShort(ref _rawData, Checksum, _rawData.Length - 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that assumes a transport control of 0
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="destination"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="contents"></param>
|
||||
/// <param name="contentsContainsGarbageByte"></param>
|
||||
public PUP(PupType type, UInt32 id, PUPPort destination, PUPPort source, byte[] contents, bool contentsContainsGarbageByte) :
|
||||
this(type, 0, id, destination, source, contents, contentsContainsGarbageByte)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as above, no garbage byte.
|
||||
/// </summary>
|
||||
@@ -359,16 +377,16 @@ namespace IFS
|
||||
|
||||
|
||||
return (ushort)sum;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly ushort Length;
|
||||
public readonly byte TransportControl;
|
||||
public readonly PupType Type;
|
||||
public readonly UInt32 ID;
|
||||
public readonly PUPPort DestinationPort;
|
||||
public readonly PUPPort SourcePort;
|
||||
public readonly PUPPort SourcePort;
|
||||
public readonly byte[] Contents;
|
||||
public readonly ushort Checksum;
|
||||
public readonly ushort Checksum;
|
||||
|
||||
private byte[] _rawData;
|
||||
|
||||
@@ -475,5 +493,10 @@ namespace IFS
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string ToOctal(int i)
|
||||
{
|
||||
return Convert.ToString(i, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace IFS.Transport
|
||||
|
||||
// addressing
|
||||
encapsulatedFrame[2] = p.DestinationPort.Host;
|
||||
encapsulatedFrame[3] = p.SourcePort.Host;
|
||||
encapsulatedFrame[3] = p.SourcePort.Host;
|
||||
|
||||
// frame type
|
||||
encapsulatedFrame[4] = (byte)(_pupFrameType >> 8);
|
||||
@@ -180,12 +180,17 @@ namespace IFS.Transport
|
||||
// Read the type and switch on it
|
||||
int etherType3mbit = ((packetStream.ReadByte() << 8) | (packetStream.ReadByte()));
|
||||
|
||||
if (etherType3mbit == _pupFrameType)
|
||||
//
|
||||
// Ensure this is a packet we're interested in.
|
||||
//
|
||||
if (etherType3mbit == _pupFrameType && // it's a PUP
|
||||
(destination == DirectoryServices.Instance.LocalHost || // for us, or...
|
||||
destination == 0)) // broadcast
|
||||
{
|
||||
try
|
||||
{
|
||||
PUP pup = new PUP(packetStream, length);
|
||||
_routerCallback(pup);
|
||||
PUP pup = new PUP(packetStream, length);
|
||||
_routerCallback(pup, destination != 0);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
||||
@@ -69,15 +69,15 @@ namespace IFS.Transport
|
||||
// Try to set up UDP client.
|
||||
try
|
||||
{
|
||||
_udpClient = new UdpClient(Configuration.UDPPort, AddressFamily.InterNetwork);
|
||||
_udpClient.Client.Blocking = true;
|
||||
_udpClient = new UdpClient(Configuration.UDPPort, AddressFamily.InterNetwork);
|
||||
_udpClient.Client.Blocking = true;
|
||||
_udpClient.EnableBroadcast = true;
|
||||
_udpClient.MulticastLoopback = false;
|
||||
|
||||
//
|
||||
// Grab the broadcast address for the interface so that we know what broadcast address to use
|
||||
// for our UDP datagrams.
|
||||
//
|
||||
//
|
||||
IPInterfaceProperties props = iface.GetIPProperties();
|
||||
|
||||
foreach (UnicastIPAddressInformation unicast in props.UnicastAddresses)
|
||||
@@ -214,12 +214,17 @@ namespace IFS.Transport
|
||||
// Read the type and switch on it
|
||||
int etherType3mbit = ((packetStream.ReadByte() << 8) | (packetStream.ReadByte()));
|
||||
|
||||
if (etherType3mbit == _pupFrameType)
|
||||
//
|
||||
// Ensure this is a packet we're interested in.
|
||||
//
|
||||
if (etherType3mbit == _pupFrameType && // it's a PUP
|
||||
(destination == DirectoryServices.Instance.LocalHost || // for us, or...
|
||||
destination == 0)) // broadcast
|
||||
{
|
||||
try
|
||||
{
|
||||
PUP pup = new PUP(packetStream, length);
|
||||
_routerCallback(pup);
|
||||
_routerCallback(pup, destination != 0);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
||||
193
PUP/readme.txt
193
PUP/readme.txt
@@ -1,4 +1,4 @@
|
||||
Readme.txt for IFS v1.1:
|
||||
Readme.txt for IFS v1.2:
|
||||
|
||||
1. Introduction and Overview
|
||||
============================
|
||||
@@ -16,21 +16,23 @@ emulator over either Raw Ethernet packets or UDP broadcasts.
|
||||
|
||||
It provides the following IFS services:
|
||||
|
||||
- BreathOfLife: Provides the "Breath Of Life" packet needed to bootstrap
|
||||
an Alto over the network.
|
||||
- EFTP/Boot: Provides boot files over the network.
|
||||
- FTP: File Transfer Protocol.
|
||||
- CopyDisk: Allows imaging and restoring of Alto disk packs over the
|
||||
network.
|
||||
- BreathOfLife: Provides the "Breath Of Life" packet needed to bootstrap
|
||||
an Alto over the network.
|
||||
- EFTP/Boot: Provides boot files over the network.
|
||||
- FTP: File Transfer Protocol.
|
||||
- CopyDisk: Allows imaging and restoring of Alto disk packs over the
|
||||
network.
|
||||
- Misc. Services: Provides network name lookup, time and other miscellaneous
|
||||
services.
|
||||
- Mail: Delivers mail to other users.
|
||||
- Gateway: Routes PUPs to other sites on the Internet
|
||||
- Mail: Delivers mail to other users. (Currently only on the same
|
||||
network, mail is not routed.)
|
||||
|
||||
|
||||
The following services are not yet provided, but are planned:
|
||||
|
||||
- EFTP/Printing: Provides print services to networked Altos
|
||||
- Gateway: Routes PUPs to other Alto networks across the Internet
|
||||
- EFTP/Printing: Provides print services to networked Altos
|
||||
- Mail routing: Sending mail to other sites over the Internet.
|
||||
|
||||
If you have questions, or run into issues or have feature requests, please
|
||||
feel free to e-mail me at joshd@livingcomputers.org.
|
||||
@@ -62,10 +64,10 @@ in mind. All files (even those in user directories) are globally readable.
|
||||
IFS uses a set of files in the "Conf" subdirectory to configure the server.
|
||||
These include:
|
||||
|
||||
- accounts.txt: Defines the set of user accounts
|
||||
- bootdirectory.txt: Maps boot numbers to boot files for network boot
|
||||
- hosts.txt: Maps Inter-network numbers to names
|
||||
- ifs.cfg: General configuration for the IFS server
|
||||
- accounts.txt: Defines the set of user accounts
|
||||
- bootdirectory.txt: Maps boot numbers to boot files for network boot
|
||||
- hosts.txt: Maps Inter-network numbers to names
|
||||
- ifs.cfg: General configuration for the IFS server
|
||||
|
||||
2.1 ifs.cfg:
|
||||
------------
|
||||
@@ -74,33 +76,33 @@ ifs.cfg contains general configuration details for the server. It specifies
|
||||
configuration for the network transport, directory paths and debugging options.
|
||||
|
||||
Directory configuration:
|
||||
- FTPRoot: Specifies the path for the root of the FTP directory tree.
|
||||
- CopyDiskRoot: Specifies the path for the directory to store CopyDisk
|
||||
images.
|
||||
- BootRoot: Specifies the path for boot images.
|
||||
- MailRoot: Specifies the path for the root of the Mail directory tree.
|
||||
(User mail folders are placed in this directory.)
|
||||
- FTPRoot: Specifies the path for the root of the FTP directory tree.
|
||||
- CopyDiskRoot: Specifies the path for the directory to store CopyDisk
|
||||
images.
|
||||
- BootRoot: Specifies the path for boot images.
|
||||
- MailRoot: Specifies the path for the root of the Mail directory tree.
|
||||
(User mail folders are placed in this directory.)
|
||||
|
||||
Interface configuration:
|
||||
- InterfaceType: "RAW" or "UDP". Specifies the transport to use for
|
||||
communication.
|
||||
- InterfaceName: The name of the host network adapter to use for
|
||||
communication.
|
||||
- InterfaceType: "RAW" or "UDP". Specifies the transport to use for
|
||||
communication.
|
||||
- InterfaceName: The name of the host network adapter to use for
|
||||
communication.
|
||||
|
||||
- UDPPort: The port number (decimal) to use for the UDP transport.
|
||||
- UDPPort: The port number (decimal) to use for the UDP transport.
|
||||
|
||||
Network configuration:
|
||||
- ServerNetwork: The IFS server's network number.
|
||||
- ServerHost: The IFS server's host number.
|
||||
- ServerNetwork: The IFS server's network number.
|
||||
- ServerHost: The IFS server's host number.
|
||||
|
||||
Debugging configuration:
|
||||
- LogTypes: The level of verbosity for logging. One of:
|
||||
None, Normal, Warning, Error, Verbose, or All
|
||||
- LogTypes: The level of verbosity for logging. One of:
|
||||
None, Normal, Warning, Error, Verbose, or All
|
||||
|
||||
- LogComponent: The components to log details about. One of:
|
||||
None, Ethernet, RTP, BSP, MiscServices, CopyDisk,
|
||||
DirectoryServices, PUP, FTP, BreathOfLife, EFTP,
|
||||
BootServer, UDP, Mail, Configuration, or All
|
||||
- LogComponent: The components to log details about. One of:
|
||||
None, Ethernet, RTP, BSP, MiscServices, CopyDisk,
|
||||
DirectoryServices, PUP, FTP, BreathOfLife, EFTP,
|
||||
BootServer, UDP, Mail, Configuration, or All
|
||||
|
||||
2.2 hosts.txt:
|
||||
--------------
|
||||
@@ -124,18 +126,44 @@ A Hostname is an alphanumeric sequence that must begin with a letter.
|
||||
A hosts.txt entry for our Alto on network 5 with host number 72 providing said
|
||||
system with name "alan" would thus look like:
|
||||
|
||||
5#72# alan
|
||||
5#72# alan
|
||||
|
||||
Or optionally, if our IFS server is on network 5:
|
||||
|
||||
72# alan
|
||||
72# alan
|
||||
|
||||
It is a good idea to provide an entry for the IFS server itself so that the
|
||||
server can easily be reached by name. By default (unless ifs.cfg has been
|
||||
changed), the IFS server's inter-network name is 1#1# (network 1, host 1).
|
||||
|
||||
|
||||
2.3 bootdirectory.txt
|
||||
2.3 networks.txt
|
||||
----------------
|
||||
|
||||
networks.txt identifies known networks and provides the address and port for
|
||||
their IFS gateway servers. See Section 5 for more details on gateways.
|
||||
This file is processed when IFS starts.
|
||||
|
||||
Each line in this file is of the format
|
||||
<inter-network number> <IFS host IP or hostname>[:port]
|
||||
|
||||
For example:
|
||||
5# 192.168.1.137
|
||||
would define network 5's gateway as 192.168.1.137 with the default port.
|
||||
|
||||
12# myhostname.net:6666
|
||||
defines network 12's gateway at myhostname.net, port 6666.
|
||||
|
||||
If no port number is specified for a given network entry, the entry will
|
||||
default to 42425.
|
||||
|
||||
networks.txt must contain an entry for the local IFS server itself if you
|
||||
want to enable routing through the gateway. In order for the IFS gateway
|
||||
to be able to talk to the outside world, the port specified for the local
|
||||
IFS server must be opened. (You may need to enable port forwarding if you
|
||||
are going to be routing PUPs over the Internet, for example.)
|
||||
|
||||
2.4 bootdirectory.txt
|
||||
---------------------
|
||||
|
||||
bootdirectory.txt maps boot numbers to the bootfile they correspond to. The
|
||||
@@ -156,7 +184,7 @@ Note that the IFS server does not include the actual boot files -- see Section
|
||||
6.0 for details on where to find these files to populate your BootRoot
|
||||
directory.
|
||||
|
||||
2.4 accounts.txt
|
||||
2.5 accounts.txt
|
||||
----------------
|
||||
|
||||
accounts.txt defines user accounts for the IFS system.
|
||||
@@ -178,18 +206,18 @@ console to add, remove, or change user accounts.
|
||||
Each user definition is a line in the format:
|
||||
<username>:<password hash>:<privileges>:<full user name>:<home directory>
|
||||
|
||||
- username: an alphanumeric sequence starting with a letter. This
|
||||
define's the user's login name.
|
||||
- password hash: an encoded version of the user's password. This can be
|
||||
edited, but is generally not advisable. See Section
|
||||
3.0 for details on setting and changing user passwords.
|
||||
- privileges: Either Admin (administrative privileges) or User (normal
|
||||
user privileges). See section 4.0 for details.
|
||||
- full user name: Self explanatory; the full name (i.e. Alan Kay) of the
|
||||
user.
|
||||
- home directory: The user's directory (which is placed under the FTPRoot
|
||||
directory). See Section 4.0 for details on user
|
||||
directories.
|
||||
- username: an alphanumeric sequence starting with a letter. This
|
||||
define's the user's login name.
|
||||
- password hash: an encoded version of the user's password. This can be
|
||||
edited, but is generally not advisable. See Section
|
||||
3.0 for details on setting and changing user passwords.
|
||||
- privileges: Either Admin (administrative privileges) or User (normal
|
||||
user privileges). See section 4.0 for details.
|
||||
- full user name: Self explanatory; the full name (i.e. Alan Kay) of the
|
||||
user.
|
||||
- home directory: The user's directory (which is placed under the FTPRoot
|
||||
directory). See Section 4.0 for details on user
|
||||
directories.
|
||||
|
||||
Changes made to this file while IFS is running will not take effect until IFS
|
||||
is restarted. (This is another reason to use the Console to make changes --
|
||||
@@ -210,24 +238,23 @@ synopses and descriptions.
|
||||
|
||||
Here is a rundown of the basic command set:
|
||||
|
||||
show users - Displays the current user database (See Section 4.0)
|
||||
show users - Displays the current user database (See Section 4.0)
|
||||
|
||||
show user <username> - Displays information for the specified user
|
||||
(See Section 4.0)
|
||||
show user <username> - Displays information for the specified user
|
||||
(See Section 4.0)
|
||||
set password <username> - Sets the password for the specified user
|
||||
(See Section 4.0)
|
||||
|
||||
set password <username> - Sets the password for the specified user
|
||||
(See Section 4.0)
|
||||
add user <username> <password> [User|Admin] <full name> <home directory>
|
||||
- Adds a new user account (See section 4.0 for details)
|
||||
|
||||
add user <username> <password> [User|Admin] <full name> <home directory>
|
||||
- Adds a new user account (See section 4.0 for details)
|
||||
remove user <username> - Removes an existing user account (See Section 4.0)
|
||||
|
||||
remove user <username> - Removes an existing user account (See Section 4.0)
|
||||
|
||||
show active servers - Displays active server statistics.
|
||||
show active servers - Displays active server statistics.
|
||||
|
||||
quit - Terminates the IFS process
|
||||
quit - Terminates the IFS process
|
||||
|
||||
show commands - Shows console commands and their descriptions.
|
||||
show commands - Shows console commands and their descriptions.
|
||||
|
||||
|
||||
4.0 User Accounts, Authentication, and Security
|
||||
@@ -305,6 +332,50 @@ usage.
|
||||
You cannot run both the IFS server and a ContrAlto emulator on the same machine
|
||||
if they are configured to use UDP as the transport.
|
||||
|
||||
5.1 Gateways and Routing
|
||||
------------------------
|
||||
|
||||
The original IFS at PARC provided Gateway services for routing PUPs across
|
||||
multiple networks via various transports (ethernet, serial, modems and even
|
||||
experimental wireless networks). It supported multi-hop routing that was
|
||||
in many ways similar to the modern Internet.
|
||||
|
||||
The LCM+L IFS server provides single-hop routing via UDP. This allows
|
||||
the connection of multiple Alto networks together, either on a local network
|
||||
or over the global Internet.
|
||||
|
||||
The networks involved are defined in the networks.txt configuration file
|
||||
(see Section 2.3) and specify what IP address corresponds to the network in
|
||||
question. When the local IFS server receives a packet destined for another
|
||||
network, it uses networks.txt to figure out what IP to send it to. Similarly,
|
||||
the local IFS server listens for incoming packets from other IFS servers and
|
||||
routes them onto the local network.
|
||||
|
||||
Unlike the original IFS, routing is statically defined by networks.txt at
|
||||
startup and cannot be changed at runtime. If in the future the advantages
|
||||
of supporting a dynamic routing scheme outweigh the disadvantages (complication,
|
||||
security, etc.) this may be added.
|
||||
|
||||
Additionally, routing is single-hop only. The assumption is made that any
|
||||
site on a TCP/IP network can reach any other via the Internet or local
|
||||
networking. In effect, the real routing is done by TCP/IP, not IFS. Multi-
|
||||
hop routing would be an interesting exercise but seems superfluous and as usual
|
||||
the decision was to err on the side of simplicity.
|
||||
|
||||
Important things to keep in mind when configuring routing:
|
||||
- Ensure all sites have unique network numbers: make sure each IFS
|
||||
server has a unique network number in ifs.cfg
|
||||
- Ensure all IFS servers have entries in networks.txt (including the
|
||||
local IFS server!)
|
||||
- Ensure the port you have specified for the local network in
|
||||
networks.txt is open, and is accessible by other IFS servers.
|
||||
- It is useful (but not necessary) to have entries for IFS servers
|
||||
and Alto hosts in hosts.txt
|
||||
|
||||
If you need to debug routing, you can set "LogComponents" to "Routing" and
|
||||
LogTypes to "All" in ifs.cfg. This will cause incoming and outgoing PUPs to
|
||||
be logged to the console as they are processed.
|
||||
|
||||
6.0 Where to Find Alto Files
|
||||
============================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user