From c671b04ee85ec653302f5ded92c6a80367dab012 Mon Sep 17 00:00:00 2001 From: Josh Dersch Date: Tue, 13 Jun 2017 11:19:25 -0700 Subject: [PATCH] Switched from PCap.net to SharpPcap for ethernet encapsulation; SharpPcap is cross-platform, this allows raw ethernet encapsulation to work on Linux and OS X. --- Contralto/Configuration.cs | 33 +++- Contralto/Contralto.csproj | 24 +-- Contralto/IO/EthernetController.cs | 5 +- Contralto/IO/HostEthernetEncapsulation.cs | 195 +++++++++++----------- Contralto/Logging/Log.cs | 12 +- Contralto/Program.cs | 29 +--- Contralto/SdlUI/SdlConsole.cs | 6 +- Contralto/UI/SystemOptions.cs | 24 ++- Contralto/packages.config | 2 +- Contralto/readme-mono.txt | 28 +++- Contralto/readme.txt | 22 +-- ContraltoSetup/Product.wxs | 48 +++--- 12 files changed, 214 insertions(+), 214 deletions(-) diff --git a/Contralto/Configuration.cs b/Contralto/Configuration.cs index 1024d49..be6f8f0 100644 --- a/Contralto/Configuration.cs +++ b/Contralto/Configuration.cs @@ -111,6 +111,9 @@ namespace Contralto break; } + // See if PCap is available. + TestPCap(); + ReadConfiguration(); // Special case: On first startup, AlternateBoot will come back as "None" which @@ -201,6 +204,17 @@ namespace Contralto /// public static string AudioDACCapturePath; + /// + /// The components to enable debug logging for. + /// + public static LogComponent LogComponents; + + /// + /// The types of logging to enable. + /// + public static LogType LogTypes; + + public static string GetAltoIRomPath(string romFileName) { return Path.Combine("ROM", "AltoI", romFileName); @@ -262,7 +276,7 @@ namespace Contralto private static void ReadConfigurationWindows() { - Properties.Settings.Default.AudioDACCapturePath = Properties.Settings.Default.AudioDACCapturePath; + AudioDACCapturePath = Properties.Settings.Default.AudioDACCapturePath; Drive0Image = Properties.Settings.Default.Drive0Image; Drive1Image = Properties.Settings.Default.Drive1Image; SystemType = (SystemType)Properties.Settings.Default.SystemType; @@ -462,6 +476,23 @@ namespace Contralto } } } + + private static void TestPCap() + { + // Just try enumerating interfaces, if this fails for any reason we assume + // PCap is not properly installed. + try + { + SharpPcap.CaptureDeviceList devices = SharpPcap.CaptureDeviceList.Instance; + Configuration.HostRawEthernetInterfacesAvailable = true; + } + catch + { + Configuration.HostRawEthernetInterfacesAvailable = false; + } + } + + } } diff --git a/Contralto/Contralto.csproj b/Contralto/Contralto.csproj index 843d199..df2439c 100644 --- a/Contralto/Contralto.csproj +++ b/Contralto/Contralto.csproj @@ -101,29 +101,15 @@ ..\packages\NAudio.1.8.0\lib\net35\NAudio.dll True - - False - ..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Base.dll - True - - - False - ..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Core.dll - True - - - False - ..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Core.Extensions.dll - True - - - False - ..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Packets.dll - True + + ..\packages\Sharp_Pcap.4.2.0\lib\PacketDotNet.dll ..\packages\SDL2-CS.dll.2.0.0.0\lib\net20\SDL2-CS.dll + + ..\packages\Sharp_Pcap.4.2.0\lib\SharpPcap.dll + diff --git a/Contralto/IO/EthernetController.cs b/Contralto/IO/EthernetController.cs index 6bba325..43613c1 100644 --- a/Contralto/IO/EthernetController.cs +++ b/Contralto/IO/EthernetController.cs @@ -62,9 +62,10 @@ namespace Contralto.IO break; } } - catch - { + catch(Exception e) + { _hostInterface = null; + Log.Write(LogComponent.HostNetworkInterface, "Unable to configure network interface. Error {0}", e.Message); } // More words than the Alto will ever send. diff --git a/Contralto/IO/HostEthernetEncapsulation.cs b/Contralto/IO/HostEthernetEncapsulation.cs index 2e86bf1..48c2308 100644 --- a/Contralto/IO/HostEthernetEncapsulation.cs +++ b/Contralto/IO/HostEthernetEncapsulation.cs @@ -15,20 +15,18 @@ along with ContrAlto. If not, see . */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using SharpPcap; +using SharpPcap.WinPcap; +using SharpPcap.LibPcap; +using SharpPcap.AirPcap; +using PacketDotNet; + +using System; +using System.Net.NetworkInformation; + -using PcapDotNet.Base; -using PcapDotNet.Core; -using PcapDotNet.Core.Extensions; -using PcapDotNet.Packets; -using PcapDotNet.Packets.Ethernet; -using System.IO; using Contralto.Logging; -using System.Threading; + namespace Contralto.IO { @@ -39,29 +37,17 @@ namespace Contralto.IO { public EthernetInterface(string name, string description) { - Name = name; - Description = description; - } - - public static List EnumerateDevices() - { - List interfaces = new List(); - - foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine) - { - interfaces.Add(new EthernetInterface(device.Name, device.Description)); - } - - return interfaces; - } + Name = name; + Description = description; + } public override string ToString() { return String.Format("{0} ({1})", Name, Description); } - public string Name; - public string Description; + public string Name; + public string Description; } /// @@ -73,20 +59,35 @@ namespace Contralto.IO public class HostEthernetEncapsulation : IPacketEncapsulation { public HostEthernetEncapsulation(string name) - { + { // Find the specified device by name - foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine) + foreach (ICaptureDevice device in CaptureDeviceList.Instance) { - if (device.GetNetworkInterface().Name.ToLowerInvariant() == Configuration.HostPacketInterfaceName.ToLowerInvariant()) + if (device is WinPcapDevice) { - AttachInterface(device); - break; + // + // We use the friendly name to make it easier to specify in config files. + // + if (((WinPcapDevice)device).Interface.FriendlyName.ToLowerInvariant() == name.ToLowerInvariant()) + { + AttachInterface(device); + break; + } + } + else + { + if (device.Name.ToLowerInvariant() == name.ToLowerInvariant()) + { + AttachInterface(device); + break; + } } } if (_interface == null) { - throw new InvalidOperationException("Specified ethernet interface does not exist or is not compatible with WinPCAP."); + Log.Write(LogComponent.HostNetworkInterface, "Specified ethernet interface does not exist or is not compatible with ContrAlto."); + throw new InvalidOperationException("Specified ethernet interface does not exist or is not compatible with ContrAlto."); } } @@ -95,20 +96,31 @@ namespace Contralto.IO _callback = callback; // Now that we have a callback we can start receiving stuff. - Open(false /* not promiscuous */, int.MaxValue); + Open(false /* not promiscuous */, 0); BeginReceive(); } public void Shutdown() { - if (_communicator != null) + if (_interface != null) { - _communicator.Break(); - } - - if (_receiveThread != null) - { - _receiveThread.Abort(); + try + { + if (_interface.Started) + { + _interface.StopCapture(); + } + } + catch + { + // Eat exceptions. The Pcap libs seem to throw on StopCapture on + // Unix platforms, we don't really care about them (since we're shutting down anyway) + // but this prevents debug spew from appearing on the console. + } + finally + { + _interface.Close(); + } } } @@ -159,50 +171,48 @@ namespace Contralto.IO Conversion.ToOctal(destinationHost), length); - MacAddress destinationMac = new MacAddress(_10mbitBroadcast); - MacAddress sourceMac = new MacAddress((UInt48)(_10mbitMACPrefix | Configuration.HostAddress)); + _10mbitMACPrefix[5] = Configuration.HostAddress; // Stuff our current Alto host address into the 10mbit MAC - // Build the outgoing packet; place the source/dest addresses, type field and the raw data. - EthernetLayer ethernetLayer = new EthernetLayer - { - Source = sourceMac, - Destination = destinationMac, - EtherType = (EthernetType)_3mbitFrameType, - }; + EthernetPacket p = new EthernetPacket( + new PhysicalAddress(_10mbitMACPrefix), // Source address + _10mbitBroadcast, // Destnation (broadcast) + (EthernetPacketType)_3mbitFrameType); - PayloadLayer payloadLayer = new PayloadLayer - { - Data = new Datagram(packetBytes), - }; - - PacketBuilder builder = new PacketBuilder(ethernetLayer, payloadLayer); + p.PayloadData = packetBytes; // Send it over the 'net! - _communicator.SendPacket(builder.Build(DateTime.Now)); + _interface.SendPacket(p); Log.Write(LogComponent.HostNetworkInterface, "Encapsulated 3mbit packet sent."); } - private void ReceiveCallback(Packet p) + private void ReceiveCallback(object sender, CaptureEventArgs e) { // // Filter out packets intended for the emulator, forward them on, drop everything else. // - if ((int)p.Ethernet.EtherType == _3mbitFrameType && // encapsulated 3mbit frames - (p.Ethernet.Source.ToValue() != (UInt48)(_10mbitMACPrefix | Configuration.HostAddress))) // and not sent by this emulator + if (e.Packet.LinkLayerType == PacketDotNet.LinkLayers.Ethernet) { - Log.Write(LogComponent.HostNetworkInterface, "Received encapsulated 3mbit packet."); - _callback(p.Ethernet.Payload.ToMemoryStream()); - } - else - { - // Not for us, discard the packet. + EthernetPacket packet = (EthernetPacket)PacketDotNet.Packet.ParsePacket(PacketDotNet.LinkLayers.Ethernet, e.Packet.Data); + + _10mbitMACPrefix[5] = Configuration.HostAddress; + + if ((int)packet.Type == _3mbitFrameType && // encapsulated 3mbit frames + (packet.SourceHwAddress != new System.Net.NetworkInformation.PhysicalAddress(_10mbitMACPrefix))) // and not sent by this emulator + { + Log.Write(LogComponent.HostNetworkInterface, "Received encapsulated 3mbit packet."); + _callback(new System.IO.MemoryStream(packet.PayloadData)); + } + else + { + // Not for us, discard the packet. + } } } - private void AttachInterface(LivePacketDevice iface) + private void AttachInterface(ICaptureDevice iface) { - _interface = iface; + _interface = iface; if (_interface == null) { @@ -214,10 +224,18 @@ namespace Contralto.IO private void Open(bool promiscuous, int timeout) { - _communicator = _interface.Open(65536, promiscuous ? PacketDeviceOpenAttributes.MaximumResponsiveness | PacketDeviceOpenAttributes.Promiscuous : PacketDeviceOpenAttributes.MaximumResponsiveness, timeout); - - // Set this to 1 so we'll get packets as soon as they arrive, no buffering. - _communicator.SetKernelMinimumBytesToCopy(1); + if (_interface is WinPcapDevice) + { + ((WinPcapDevice)_interface).Open(promiscuous ? OpenFlags.MaxResponsiveness | OpenFlags.Promiscuous : OpenFlags.MaxResponsiveness, timeout); + } + else if (_interface is LibPcapLiveDevice) + { + ((LibPcapLiveDevice)_interface).Open(promiscuous ? DeviceMode.Promiscuous : DeviceMode.Normal, timeout); + } + else if (_interface is AirPcapDevice) + { + ((AirPcapDevice)_interface).Open(promiscuous ? OpenFlags.MaxResponsiveness | OpenFlags.Promiscuous : OpenFlags.MaxResponsiveness, timeout); + } Log.Write(LogComponent.HostNetworkInterface, "Host interface opened and receiving packets."); } @@ -227,38 +245,23 @@ namespace Contralto.IO /// private void BeginReceive() { - // Kick off receive thread. - _receiveThread = new Thread(ReceiveThread); - _receiveThread.Start(); + // Kick off receiver. + _interface.OnPacketArrival += ReceiveCallback; + _interface.StartCapture(); } - private void ReceiveThread() - { - // Just call ReceivePackets, 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.HostNetworkInterface, "Receiver thread started."); - - _communicator.ReceivePackets(-1, ReceiveCallback); - } - - private LivePacketDevice _interface; - private PacketCommunicator _communicator; + private ICaptureDevice _interface; private ReceivePacketDelegate _callback; - - // Thread used for receive - private Thread _receiveThread; - private const int _3mbitFrameType = 0xbeef; // easy to identify, ostensibly unused by anything of any import /// /// On output, these bytes are prepended to the Alto's 3mbit (1 byte) address to form a full /// 6 byte Ethernet MAC. - /// On input, ethernet frames are checked for this prefix + /// On input, ethernet frames are checked for this prefix. /// - private UInt48 _10mbitMACPrefix = 0x0000aa010200; // 00-00-AA is the Xerox vendor code, used just to be cute. + private byte[] _10mbitMACPrefix = { 0x00, 0x00, 0xaa, 0x01, 0x02, 0x00 }; // 00-00-AA is the Xerox vendor code, used just to be cute. - private UInt48 _10mbitBroadcast = (UInt48)0xffffffffffff; + private PhysicalAddress _10mbitBroadcast = new PhysicalAddress(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }); } } diff --git a/Contralto/Logging/Log.cs b/Contralto/Logging/Log.cs index c803337..db43779 100644 --- a/Contralto/Logging/Log.cs +++ b/Contralto/Logging/Log.cs @@ -75,11 +75,9 @@ namespace Contralto.Logging { static Log() { - // TODO: make configurable - _components = LogComponent.DAC; - _type = LogType.Normal | LogType.Warning | LogType.Error | LogType.Verbose; + _components = Configuration.LogComponents; + _type = Configuration.LogTypes; - //_logStream = new StreamWriter("log.txt"); } public static LogComponent LogComponents @@ -110,11 +108,6 @@ namespace Contralto.Logging // My log has something to tell you... // TODO: color based on type, etc. Console.WriteLine(component.ToString() + ": " + message, args); - - if (_logStream != null) - { - _logStream.WriteLine(component.ToString() + ": " + message, args); - } } } #else @@ -132,6 +125,5 @@ namespace Contralto.Logging private static LogComponent _components; private static LogType _type; - private static StreamWriter _logStream; } } diff --git a/Contralto/Program.cs b/Contralto/Program.cs index e552b55..d878630 100644 --- a/Contralto/Program.cs +++ b/Contralto/Program.cs @@ -40,10 +40,7 @@ namespace Contralto } // Handle command-line args - PrintHerald(); - - // See if WinPCap is installed and working - TestPCap(); + PrintHerald(); _system = new AltoSystem(); @@ -133,29 +130,7 @@ namespace Contralto Console.WriteLine("Bug reports to joshd@livingcomputers.org"); Console.WriteLine(); } - - private static void TestPCap() - { - if (Configuration.Platform == PlatformType.Windows) - { - // Just try enumerating interfaces, if this fails for any reason we assume - // PCap is not properly installed. - try - { - List interfaces = EthernetInterface.EnumerateDevices(); - Configuration.HostRawEthernetInterfacesAvailable = true; - } - catch - { - Configuration.HostRawEthernetInterfacesAvailable = false; - } - } - else - { - Configuration.HostRawEthernetInterfacesAvailable = false; - } - } - + private static AltoSystem _system; } } diff --git a/Contralto/SdlUI/SdlConsole.cs b/Contralto/SdlUI/SdlConsole.cs index 1fd1af8..3afc195 100644 --- a/Contralto/SdlUI/SdlConsole.cs +++ b/Contralto/SdlUI/SdlConsole.cs @@ -154,7 +154,7 @@ namespace Contralto.SdlUI { if (_controller.IsRunning) { - Console.WriteLine("Alto is already running. Use 'stop' to stop the Alto first."); + _controller.Reset(AlternateBootType.Disk); } else { @@ -169,10 +169,10 @@ namespace Contralto.SdlUI { if (_controller.IsRunning) { - Console.WriteLine("Alto is already running. Use 'stop' to stop the Alto first."); + _controller.Reset(AlternateBootType.Ethernet); } else - { + { _controller.StartExecution(AlternateBootType.Ethernet); } diff --git a/Contralto/UI/SystemOptions.cs b/Contralto/UI/SystemOptions.cs index c58f801..7d4af17 100644 --- a/Contralto/UI/SystemOptions.cs +++ b/Contralto/UI/SystemOptions.cs @@ -16,10 +16,9 @@ */ using Contralto.IO; -using PcapDotNet.Core; -using PcapDotNet.Core.Extensions; +using SharpPcap; +using SharpPcap.WinPcap; using System; -using System.Collections.Generic; using System.IO; using System.Net.NetworkInformation; using System.Windows.Forms; @@ -79,7 +78,7 @@ namespace Contralto.UI if (!Configuration.HostRawEthernetInterfacesAvailable) { - // If PCAP isn't installed, the RAW Ethernet option is not available. + // If PCAP isn't installed, the RAW Ethernet option is not available. RawEthernetRadioButton.Enabled = false; // Ensure the option isn't set in the configuration. @@ -102,7 +101,7 @@ namespace Contralto.UI case PacketInterfaceType.None: NoEncapsulationRadioButton.Checked = true; break; - } + } PopulateNetworkAdapterList(Configuration.HostPacketInterfaceType); @@ -153,22 +152,21 @@ namespace Contralto.UI case PacketInterfaceType.EthernetEncapsulation: if (Configuration.HostRawEthernetInterfacesAvailable) { - foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine) - { - EthernetInterfaceListBox.Items.Add(new EthernetInterface(device.GetNetworkInterface().Name, device.GetNetworkInterface().Description)); - } - } + foreach (WinPcapDevice device in CaptureDeviceList.Instance) + { + EthernetInterfaceListBox.Items.Add(new EthernetInterface(device.Interface.FriendlyName, device.Interface.Description)); + } + } break; case PacketInterfaceType.None: // Add nothing. break; - } - + } // // Select the one that is already selected (if any) - // + // EthernetInterfaceListBox.SelectedIndex = 0; if (!string.IsNullOrEmpty(Configuration.HostPacketInterfaceName)) diff --git a/Contralto/packages.config b/Contralto/packages.config index 0d96e65..400781d 100644 --- a/Contralto/packages.config +++ b/Contralto/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/Contralto/readme-mono.txt b/Contralto/readme-mono.txt index 7e92b76..41571fa 100644 --- a/Contralto/readme-mono.txt +++ b/Contralto/readme-mono.txt @@ -282,8 +282,8 @@ Drive0Image and Drive1Image: Specifies a disk image to be loaded into the HostPacketInterfaceType: Specifies the type of interface to be used on the host for Ethernet emulation. One of: - UDPEncapsulation: Transmits Alto Ethernet packets over UDP broadcasts - - RAWEncapsulation: Transmits Alto Ethernet packets over raw Ethernet packets. - (not yet available on Unix / OS X platforms.) + - EthernetEncapsulation: Transmits Alto Ethernet packets over raw Ethernet packets. + (See Section 4.1 for configuration details) - None: No packet encapsulation. HostPacketInterfaceName: Specifies the name of the host network interface @@ -295,6 +295,24 @@ BootFile: The file number to use with a Keyboard Net Boot (again, Section 5.0 AlternateBootType: The type of boot to default to (Section 5.0) +4.1 Ethernet Encapsulation Setup +================================ + +Encapsulation of Alto (3mbit) Ethernet packets in Ethernet broadcasts is supported +on Linux and OS X using libpcap. While it is tested and works well, it may require some extra +configuration on your system before it will work properly for you. + +- Ensure that the latest libpcap libraries are installed. These should be present by default + on OS X; on other platforms check your distribution's documentation for details. + +- On many systems, libpcap requires additional privileges in order to capture packets. + You can either run ContrAlto as root, or setuid ContrAlto to root. Depending on + your operating system, there may be other options. See (for example) + http://www.tcpdump.org/manpages/pcap.3pcap.html. + +- You may need to modify SharpPcap.dll.config to point to the specific libpcap + version you have installed on your system. + 5.0 Console Interface ===================== @@ -388,7 +406,7 @@ At the moment, the following issues are known and being worked on. If you find an issue not listed here, see section 7.0 to report a new bug. - Smalltalk-80 does not run. -- Audio and RAW ethernet packets are not available on Unix / OS X. +- Audio is not available on Unix / OS X. 7.0 Reporting Bugs @@ -424,6 +442,9 @@ Contributions are welcome! ContrAlto would not have been possible without the amazing preservation work of the Computer History Museum. +Ethernet encapsulation is provided courtesy of SharpPcap, a WinPcap/LibPcap wrapper. +See: https://github.com/chmorgan/sharppcap. + Audio output and capture on Windows is provided using the NAudio libraries, see: https://github.com/naudio/NAudio. @@ -442,6 +463,7 @@ V1.2 - Initial implementation of Orbit rasterization device; Dover ROS is implemented but not working properly. - Added ability to load a configuration file at startup +- Switched to cross-platform SharpPcap library for Ethernet encapsulation. V1.1 ---- diff --git a/Contralto/readme.txt b/Contralto/readme.txt index 54725a1..b8763d9 100644 --- a/Contralto/readme.txt +++ b/Contralto/readme.txt @@ -247,19 +247,9 @@ encapsulation. ContrAlto can encapsulate the Alto's 3mbit ("experimental") Ethernet packets in either UDP datagrams or raw Ethernet packets on a network interface on the "host" computer (the computer running ContrAlto). -Raw packet encapsulation requires WinPCAP and the Microsoft Visual C++ 2010 -redistributable to be installed; these can be acquired from: +Raw packet encapsulation requires WinPCAP libraries to be installed. See: +http://www.winpcap.org/. -http://www.winpcap.org/ -and -http://www.microsoft.com/en-us/download/details.aspx?id=5555 - -ContrAlto uses binaries from the Pcap.NET project to expose WinPCAP -functionality to the emulator: - -https://github.com/PcapDotNet/Pcap.Net - -Pcap.NET is released under the BSD license. 4.2.1 Host Address ------------------ @@ -443,8 +433,8 @@ CPU Registers: General Registers: Shows the contents of the 32 R and 32 S registers (in octal). - (The extra 7 sets of R and S registers on 3K CRAM machines are not yet - displayed.) + (The extra 7 sets of R and S registers on 3K CRAM machines are not yet + displayed.) Reserved Memory: Shows the contents of most "well known" memory locations. See the @@ -493,6 +483,9 @@ Contributions are welcome! ContrAlto would not have been possible without the amazing preservation work of the Computer History Museum. +Ethernet encapsulation is provided courtesy of SharpPcap, a WinPcap/LibPcap wrapper. +See: https://github.com/chmorgan/sharppcap. + Audio output and capture on Windows is provided using the NAudio libraries, see: https://github.com/naudio/NAudio. @@ -511,6 +504,7 @@ V1.2 - Initial implementation of Orbit rasterization device; Dover ROS is implemented but not working properly. - Added ability to load a configuration file at startup +- Switched to cross-platform SharpPcap library for Ethernet encapsulation. V1.1 ---- diff --git a/ContraltoSetup/Product.wxs b/ContraltoSetup/Product.wxs index a51a29b..6349803 100644 --- a/ContraltoSetup/Product.wxs +++ b/ContraltoSetup/Product.wxs @@ -17,18 +17,18 @@ --> - - + + + Minimum="1.2.0" IncludeMinimum="yes" + Maximum="1.2.0" IncludeMaximum="yes" /> + Minimum="1.2.0" IncludeMinimum="no" /> - + @@ -38,8 +38,8 @@ - - + + @@ -47,7 +47,7 @@ --> - + @@ -58,13 +58,13 @@ - + - - - + + + - + @@ -72,7 +72,7 @@ - + @@ -90,12 +90,12 @@ --> - + - - - + + + - - - - + + @@ -120,7 +118,7 @@ - + @@ -225,6 +223,6 @@ - + \ No newline at end of file