blob: 7eeb80396a6ecb68c569fb90e86212f663415783 [file] [log] [blame]
//
// Copyright (c) 2010-2020 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Collections.Generic;
using PacketDotNet;
using Antmicro.Renode.Core;
using Antmicro.Renode.Core.Structure;
using Antmicro.Renode.Peripherals.Network;
using Antmicro.Renode.Logging;
using Antmicro.Renode.Exceptions;
using System.Linq;
namespace Antmicro.Renode.Network
{
public static class NetworkServerExtensions
{
public static void CreateNetworkServer(this Emulation emulation, string name, string ipAddress)
{
emulation.ExternalsManager.AddExternal(new NetworkServer(ipAddress), name);
}
}
public class NetworkServer : IExternal, IMACInterface, IHasChildren<IServerModule>
{
public NetworkServer(string ipAddress, string macAddress = null)
{
if(!IPAddress.TryParse(ipAddress, out var parsedIP))
{
new ConstructionException($"Invalid IP address: {ipAddress}");
}
if(macAddress != null)
{
if(!MACAddress.TryParse(macAddress, out var parsedMAC))
{
new ConstructionException($"Invalid MAC address: {macAddress}");
}
MAC = parsedMAC;
}
else
{
MAC = new MACAddress(0xdeadbeef);
}
IP = parsedIP;
arpTable = new Dictionary<IPAddress, PhysicalAddress>();
modules = new Dictionary<int, IServerModule>();
modulesNames = new Dictionary<string, int>();
this.Log(LogLevel.Info, "Network server started at IP {0}", IP);
}
public IEnumerable<string> GetNames()
{
return modulesNames.Keys;
}
public IServerModule TryGetByName(string name, out bool success)
{
if(!modulesNames.TryGetValue(name, out var port))
{
success = false;
return null;
}
success = true;
return modules[port];
}
public bool RegisterModule(IServerModule module, int port, string name)
{
if(modules.ContainsKey(port))
{
this.Log(LogLevel.Error, "Couldn't register module on port {0} as it's already used", port);
return false;
}
if(modulesNames.ContainsKey(name))
{
this.Log(LogLevel.Error, "Couldn't register module by name {0} as it's already used", name);
return false;
}
this.Log(LogLevel.Noisy, "Registering module on port {0}", port);
modules[port] = module;
modulesNames[name] = port;
return true;
}
public void ReceiveFrame(EthernetFrame frame)
{
var ethernetPacket = frame.UnderlyingPacket;
this.Log(LogLevel.Noisy, "Ethernet packet details: {0}", frame);
#if DEBUG_PACKETS
this.Log(LogLevel.Noisy, Misc.PrettyPrintCollectionHex(frame.Bytes));
#endif
switch(ethernetPacket.Type)
{
case EthernetPacketType.Arp:
if(TryHandleArp((ARPPacket)ethernetPacket.PayloadPacket, out var arpResponse))
{
var ethernetResponse = new EthernetPacket((PhysicalAddress)MAC, ethernetPacket.SourceHwAddress, EthernetPacketType.None);
ethernetResponse.PayloadPacket = arpResponse;
this.Log(LogLevel.Noisy, "Sending response: {0}", ethernetResponse);
EthernetFrame.TryCreateEthernetFrame(ethernetResponse.Bytes, true, out var response);
FrameReady?.Invoke(response);
}
break;
case EthernetPacketType.IpV4:
var ipv4Packet = (IPv4Packet)ethernetPacket.PayloadPacket;
arpTable[ipv4Packet.SourceAddress] = ethernetPacket.SourceHwAddress;
HandleIPv4(ipv4Packet);
break;
default:
this.Log(LogLevel.Warning, "Unsupported packet type: {0}", ethernetPacket.Type);
break;
}
}
public MACAddress MAC { get; set; }
public IPAddress IP { get; set; }
public event Action<EthernetFrame> FrameReady;
private void HandleIPv4(IPv4Packet packet)
{
this.Log(LogLevel.Noisy, "Handling IPv4 packet: {0}", PacketToString(packet));
switch(packet.Protocol)
{
case PacketDotNet.IPProtocolType.UDP:
HandleUdp((UdpPacket)packet.PayloadPacket);
break;
default:
this.Log(LogLevel.Warning, "Unsupported protocol: {0}", packet.Protocol);
break;
}
}
private void HandleUdp(UdpPacket packet)
{
this.Log(LogLevel.Noisy, "Handling UDP packet: {0}", PacketToString(packet));
if(!modules.TryGetValue(packet.DestinationPort, out var module))
{
this.Log(LogLevel.Warning, "Received UDP packet on port {0}, but no service is active", packet.DestinationPort);
return;
}
var src = new IPEndPoint(((IPv4Packet)packet.ParentPacket).SourceAddress, packet.SourcePort);
module.HandleUdp(src, packet, (s, r) => HandleUdpResponse(s, r));
}
private void HandleUdpResponse(IPEndPoint source, UdpPacket response)
{
var ipPacket = new IPv4Packet(IP, source.Address);
var ethernetPacket = new EthernetPacket((PhysicalAddress)MAC, arpTable[source.Address], EthernetPacketType.None);
ipPacket.PayloadPacket = response;
ethernetPacket.PayloadPacket = ipPacket;
response.UpdateCalculatedValues();
this.Log(LogLevel.Noisy, "Sending UDP response: {0}", response);
EthernetFrame.TryCreateEthernetFrame(ethernetPacket.Bytes, true, out var ethernetFrame);
ethernetFrame.FillWithChecksums(new EtherType[] { EtherType.IpV4 }, new [] { IPProtocolType.UDP });
FrameReady?.Invoke(ethernetFrame);
}
private bool TryHandleArp(ARPPacket packet, out ARPPacket response)
{
response = null;
var packetString = PacketToString(packet);
this.Log(LogLevel.Noisy, "Handling ARP packet: {0}", packetString);
if(packet.Operation != ARPOperation.Request)
{
this.Log(LogLevel.Warning, "Unsupported ARP packet: {0}", packetString);
return false;
}
if(!packet.TargetProtocolAddress.Equals(IP))
{
this.Log(LogLevel.Noisy, "This ARP packet is not directed to me. Ignoring");
return false;
}
response = new ARPPacket(
ARPOperation.Response,
packet.SenderHardwareAddress,
packet.SenderProtocolAddress,
(PhysicalAddress)MAC,
IP);
this.Log(LogLevel.Noisy, "Sending ARP response");
return true;
}
private string PacketToString(Packet packet)
{
try
{
return packet.ToString();
}
catch
{
return "<failed to decode packet>";
}
}
private readonly Dictionary<int, IServerModule> modules;
private readonly Dictionary<string, int> modulesNames;
private readonly Dictionary<IPAddress, PhysicalAddress> arpTable;
}
}