1 //
2 // Copyright (c) 2010-2020 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 
8 using System;
9 using System.IO;
10 using System.Net;
11 using System.Net.NetworkInformation;
12 using System.Collections.Generic;
13 
14 using PacketDotNet;
15 
16 using Antmicro.Renode.Core;
17 using Antmicro.Renode.Core.Structure;
18 using Antmicro.Renode.Peripherals.Network;
19 using Antmicro.Renode.Logging;
20 using Antmicro.Renode.Exceptions;
21 
22 using System.Linq;
23 
24 namespace Antmicro.Renode.Network
25 {
26     public static class NetworkServerExtensions
27     {
CreateNetworkServer(this Emulation emulation, string name, string ipAddress)28         public static void CreateNetworkServer(this Emulation emulation, string name, string ipAddress)
29         {
30             emulation.ExternalsManager.AddExternal(new NetworkServer(ipAddress), name);
31         }
32     }
33 
34     public class NetworkServer : IExternal, IMACInterface, IHasChildren<IServerModule>
35     {
NetworkServer(string ipAddress, string macAddress = null)36         public NetworkServer(string ipAddress, string macAddress = null)
37         {
38             if(!IPAddress.TryParse(ipAddress, out var parsedIP))
39             {
40                 new ConstructionException($"Invalid IP address: {ipAddress}");
41             }
42 
43             if(macAddress != null)
44             {
45                 if(!MACAddress.TryParse(macAddress, out var parsedMAC))
46                 {
47                     new ConstructionException($"Invalid MAC address: {macAddress}");
48                 }
49                 MAC = parsedMAC;
50             }
51             else
52             {
53                 MAC = new MACAddress(0xdeadbeef);
54             }
55 
56 
57             IP = parsedIP;
58 
59             arpTable = new Dictionary<IPAddress, PhysicalAddress>();
60             modules = new Dictionary<int, IServerModule>();
61             modulesNames = new Dictionary<string, int>();
62 
63             this.Log(LogLevel.Info, "Network server started at IP {0}", IP);
64         }
65 
GetNames()66         public IEnumerable<string> GetNames()
67         {
68             return modulesNames.Keys;
69         }
70 
TryGetByName(string name, out bool success)71         public IServerModule TryGetByName(string name, out bool success)
72         {
73             if(!modulesNames.TryGetValue(name, out var port))
74             {
75                 success = false;
76                 return null;
77             }
78 
79             success = true;
80             return modules[port];
81         }
82 
RegisterModule(IServerModule module, int port, string name)83         public bool RegisterModule(IServerModule module, int port, string name)
84         {
85             if(modules.ContainsKey(port))
86             {
87                 this.Log(LogLevel.Error, "Couldn't register module on port {0} as it's already used", port);
88                 return false;
89             }
90 
91             if(modulesNames.ContainsKey(name))
92             {
93                 this.Log(LogLevel.Error, "Couldn't register module by name {0} as it's already used", name);
94                 return false;
95             }
96 
97             this.Log(LogLevel.Noisy, "Registering module on port {0}", port);
98             modules[port] = module;
99             modulesNames[name] = port;
100             return true;
101         }
102 
ReceiveFrame(EthernetFrame frame)103         public void ReceiveFrame(EthernetFrame frame)
104         {
105             var ethernetPacket = frame.UnderlyingPacket;
106 
107             this.Log(LogLevel.Noisy, "Ethernet packet details: {0}", frame);
108 #if DEBUG_PACKETS
109             this.Log(LogLevel.Noisy, Misc.PrettyPrintCollectionHex(frame.Bytes));
110 #endif
111 
112             switch(ethernetPacket.Type)
113             {
114                 case EthernetPacketType.Arp:
115                     if(TryHandleArp((ARPPacket)ethernetPacket.PayloadPacket, out var arpResponse))
116                     {
117                         var ethernetResponse = new EthernetPacket((PhysicalAddress)MAC, ethernetPacket.SourceHwAddress, EthernetPacketType.None);
118                         ethernetResponse.PayloadPacket = arpResponse;
119 
120                         this.Log(LogLevel.Noisy, "Sending response: {0}", ethernetResponse);
121                         EthernetFrame.TryCreateEthernetFrame(ethernetResponse.Bytes, true, out var response);
122                         FrameReady?.Invoke(response);
123                     }
124                     break;
125 
126                 case EthernetPacketType.IpV4:
127                     var ipv4Packet = (IPv4Packet)ethernetPacket.PayloadPacket;
128                     arpTable[ipv4Packet.SourceAddress] = ethernetPacket.SourceHwAddress;
129                     HandleIPv4(ipv4Packet);
130                     break;
131 
132                 default:
133                     this.Log(LogLevel.Warning, "Unsupported packet type: {0}", ethernetPacket.Type);
134                     break;
135             }
136         }
137 
138         public MACAddress MAC { get; set; }
139         public IPAddress IP { get; set; }
140 
141         public event Action<EthernetFrame> FrameReady;
142 
HandleIPv4(IPv4Packet packet)143         private void HandleIPv4(IPv4Packet packet)
144         {
145             this.Log(LogLevel.Noisy, "Handling IPv4 packet: {0}", PacketToString(packet));
146 
147             switch(packet.Protocol)
148             {
149                 case PacketDotNet.IPProtocolType.UDP:
150                     HandleUdp((UdpPacket)packet.PayloadPacket);
151                     break;
152 
153                 default:
154                     this.Log(LogLevel.Warning, "Unsupported protocol: {0}", packet.Protocol);
155                     break;
156             }
157         }
158 
HandleUdp(UdpPacket packet)159         private void HandleUdp(UdpPacket packet)
160         {
161             this.Log(LogLevel.Noisy, "Handling UDP packet: {0}", PacketToString(packet));
162 
163             if(!modules.TryGetValue(packet.DestinationPort, out var module))
164             {
165                 this.Log(LogLevel.Warning, "Received UDP packet on port {0}, but no service is active", packet.DestinationPort);
166                 return;
167             }
168 
169             var src = new IPEndPoint(((IPv4Packet)packet.ParentPacket).SourceAddress, packet.SourcePort);
170             module.HandleUdp(src, packet, (s, r) => HandleUdpResponse(s, r));
171         }
172 
HandleUdpResponse(IPEndPoint source, UdpPacket response)173         private void HandleUdpResponse(IPEndPoint source, UdpPacket response)
174         {
175             var ipPacket = new IPv4Packet(IP, source.Address);
176             var ethernetPacket = new EthernetPacket((PhysicalAddress)MAC, arpTable[source.Address], EthernetPacketType.None);
177 
178             ipPacket.PayloadPacket = response;
179             ethernetPacket.PayloadPacket = ipPacket;
180             response.UpdateCalculatedValues();
181 
182             this.Log(LogLevel.Noisy, "Sending UDP response: {0}", response);
183 
184             EthernetFrame.TryCreateEthernetFrame(ethernetPacket.Bytes, true, out var ethernetFrame);
185             ethernetFrame.FillWithChecksums(new EtherType[] { EtherType.IpV4 }, new [] { IPProtocolType.UDP });
186 
187             FrameReady?.Invoke(ethernetFrame);
188         }
189 
TryHandleArp(ARPPacket packet, out ARPPacket response)190         private bool TryHandleArp(ARPPacket packet, out ARPPacket response)
191         {
192             response = null;
193             var packetString = PacketToString(packet);
194             this.Log(LogLevel.Noisy, "Handling ARP packet: {0}", packetString);
195 
196             if(packet.Operation != ARPOperation.Request)
197             {
198                 this.Log(LogLevel.Warning, "Unsupported ARP packet: {0}", packetString);
199                 return false;
200             }
201 
202             if(!packet.TargetProtocolAddress.Equals(IP))
203             {
204                 this.Log(LogLevel.Noisy, "This ARP packet is not directed to me. Ignoring");
205                 return false;
206             }
207 
208             response = new ARPPacket(
209                 ARPOperation.Response,
210                 packet.SenderHardwareAddress,
211                 packet.SenderProtocolAddress,
212                 (PhysicalAddress)MAC,
213                 IP);
214 
215             this.Log(LogLevel.Noisy, "Sending ARP response");
216             return true;
217         }
218 
PacketToString(Packet packet)219         private string PacketToString(Packet packet)
220         {
221             try
222             {
223                 return packet.ToString();
224             }
225             catch
226             {
227                 return "<failed to decode packet>";
228             }
229         }
230 
231         private readonly Dictionary<int, IServerModule> modules;
232         private readonly Dictionary<string, int> modulesNames;
233         private readonly Dictionary<IPAddress, PhysicalAddress> arpTable;
234     }
235 }
236