1 //
2 // Copyright (c) 2010-2023 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using System.Collections.Generic;
10 using System.Linq;
11 using System.Net;
12 using Antmicro.Renode.Core.Structure;
13 using PacketDotNet;
14 using PacketDotNet.Utils;
15 
16 namespace Antmicro.Renode.Network
17 {
18     public class EthernetFrame
19     {
TryCreateEthernetFrame(byte[] data, bool addCrc, out EthernetFrame frame)20         public static bool TryCreateEthernetFrame(byte[] data, bool addCrc, out EthernetFrame frame)
21         {
22             return TryCreateEthernetFrame(data, addCrc ? CRCMode.Add : CRCMode.NoOperation, out frame);
23         }
24 
TryCreateEthernetFrame(byte[] data, CRCMode crcMode, out EthernetFrame frame)25         public static bool TryCreateEthernetFrame(byte[] data, CRCMode crcMode, out EthernetFrame frame)
26         {
27             frame = null;
28             switch(crcMode)
29             {
30                 case CRCMode.NoOperation:
31                     if(data.Length >= MinFrameSizeWithoutCRC)
32                     {
33                         frame = new EthernetFrame(data);
34                         return true;
35                     }
36                     return false;
37                 case CRCMode.Add:
38                 case CRCMode.Replace:
39                 case CRCMode.Keep:
40                     if(data.Length >= MinFrameSizeWithoutCRC + CRCLength)
41                     {
42                         var noCrcData = crcMode == CRCMode.Add ? data : data.Take(data.Length - CRCLength).ToArray();
43                         var crc = (crcMode == CRCMode.Keep ? data.Skip(data.Length - CRCLength) : ComputeCRC(noCrcData)).ToArray();
44                         frame = new EthernetFrame(noCrcData, crc);
45                         return true;
46                     }
47                     return false;
48                 default:
49                     throw new ArgumentException("Illegal value", "crcMode");
50             }
51         }
52 
CheckCRC(byte[] data)53         public static bool CheckCRC(byte[] data)
54         {
55             return CompareCRC(GetCRCFromPacket(data), CalculateCRCFromPayload(data));
56         }
57 
FillWithChecksums(EtherType[] supportedEtherTypes, IPProtocolType[] supportedIpProtocolTypes, bool updateEthernetCrc = true)58         public void FillWithChecksums(EtherType[] supportedEtherTypes, IPProtocolType[] supportedIpProtocolTypes, bool updateEthernetCrc = true)
59         {
60             var packetNetIpProtocols = supportedIpProtocolTypes.Select(x => (PacketDotNet.IPProtocolType)x).ToArray();
61             var packetNetEtherTypes = supportedEtherTypes.Select(x => (EthernetPacketType)x).ToArray();
62             UnderlyingPacket.RecursivelyUpdateCalculatedValues(packetNetEtherTypes, packetNetIpProtocols);
63 
64             if(updateEthernetCrc)
65             {
66                 var data = UnderlyingPacket.Bytes.ToArray();
67                 crc = ComputeCRC(data).ToArray();
68             }
69         }
70 
Clone()71         public EthernetFrame Clone()
72         {
73             return new EthernetFrame(UnderlyingPacket.Bytes.ToArray(), crc?.ToArray());
74         }
75 
ToString()76         public override string ToString()
77         {
78             try
79             {
80                 return UnderlyingPacket.ToString();
81             }
82             catch
83             {
84                 return "<failed to decode frame>";
85             }
86         }
87 
88         public EthernetPacket UnderlyingPacket { get; }
89 
90         public byte[] Bytes
91         {
92             get
93             {
94                 return (crc != null) ? UnderlyingPacket.Bytes.Concat(crc).ToArray() : UnderlyingPacket.Bytes.ToArray();
95             }
96         }
97 
98         public int Length
99         {
100             get
101             {
102                 return UnderlyingPacket.BytesHighPerformance.Length;
103             }
104         }
105 
106         public MACAddress SourceMAC => (MACAddress)UnderlyingPacket.SourceHwAddress;
107 
108         public MACAddress DestinationMAC => (MACAddress)UnderlyingPacket.DestinationHwAddress;
109 
110         public IPAddress SourceIP
111         {
112             get
113             {
114                 var ip = (IpPacket)UnderlyingPacket.Extract(typeof(IpPacket));
115                 return ip != null ? ip.SourceAddress : null;
116             }
117         }
118 
119         public IPAddress DestinationIP
120         {
121             get
122             {
123                 var ip = (IpPacket)UnderlyingPacket.Extract(typeof(IpPacket));
124                 return ip != null ? ip.DestinationAddress : null;
125             }
126         }
127 
128         // note: the length 18 covers only:
129         // * mac destination (6)
130         // * mac source (6)
131         // * 802.1Q tag (4)
132         // * ether type or length (2)
133         // and is chosen so that Packet .NET doesn't crash
134         // when parsing the packet;
135         // according to the ethernet specs the packet must
136         // be at least 64 bits long, but since not all
137         // ethernet models in Renode support automatic
138         // padding the selected value is a compromise
139         public static int MinFrameSizeWithoutCRC = 18;
140         public static int CRCLength = 4;
141         // 1500 byte upper layer IP packet with 14 byte frame header and 4 byte frame trailer
142         public static readonly int MaximumFrameSize = 1518;
143         public static readonly int RuntPacketMaximumSize = 63;
144 
EthernetFrame(byte[] data, byte[] crc = null)145         private EthernetFrame(byte[] data, byte[] crc = null)
146         {
147             this.crc = crc;
148             this.UnderlyingPacket = (EthernetPacket)Packet.ParsePacket(LinkLayers.Ethernet, data);
149         }
150 
ComputeCRC(byte[] data, int? lenght = null)151         private static IEnumerable<byte> ComputeCRC(byte[] data, int? lenght = null)
152         {
153             var computedCRC = lenght.HasValue? Crc32.Compute(data, 0, lenght.Value) : Crc32.Compute(data);
154             var result = BitConverter.GetBytes(computedCRC);
155             return result.Reverse();
156         }
157 
CalculateCRCFromPayload(byte[] data)158         private static IEnumerable<byte> CalculateCRCFromPayload(byte[] data)
159         {
160             return ComputeCRC(data, data.Length - CRCLength);
161         }
162 
GetCRCFromPacket(byte[] data)163         private static IEnumerable<byte> GetCRCFromPacket(byte[] data)
164         {
165             return data.Skip(data.Length - CRCLength);
166         }
167 
CompareCRC(IEnumerable<byte> receivedCrc, IEnumerable<byte> computedCrc)168         private static bool CompareCRC(IEnumerable<byte> receivedCrc, IEnumerable<byte> computedCrc)
169         {
170             return receivedCrc.SequenceEqual(computedCrc);
171         }
172 
173         private byte[] crc;
174     }
175 }
176