blob: 26c00e11cc29999d175f192705d2928375820fa3 [file] [log] [blame]
//
// Copyright (c) 2010-2024 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System;
using System.Linq;
using System.Collections.Generic;
using Antmicro.Renode.Peripherals.Wireless;
using Antmicro.Renode.Core;
using Antmicro.Renode.Peripherals;
using Antmicro.Renode.Peripherals.Network;
using Antmicro.Renode.Exceptions;
using Antmicro.Renode.Tools.Network;
namespace Antmicro.Renode.Plugins.WiresharkPlugin
{
public class Wireshark : IHostMachineElement, IExternal
{
public Wireshark(string sinkName, LinkLayer layer, string wiresharkPath)
{
currentEmulation = EmulationManager.Instance.CurrentEmulation;
EmulationManager.Instance.EmulationChanged += ClearLog;
currentEmulation.MachineRemoved += OnMachineRemoved;
wiresharkSinkName = sinkName;
bleSniffer = new BLESniffer();
wiresharkSender = new WiresharkSender(wiresharkSinkName, (uint)layer, wiresharkPath);
this.layer = layer;
}
public void LogToWireshark(INetworkLog<INetworkInterface> reporter)
{
lock(innerLock)
{
if(IsConfiguredForMediumType(reporter))
{
AddMedium(reporter);
}
}
}
public void LogToWireshark(INetworkLog<INetworkInterface> reporter, INetworkInterface iface)
{
lock(innerLock)
{
if(IsConfiguredForMediumType(reporter))
{
AddInterface(reporter, iface);
}
}
}
public void DetachFrom(INetworkLog<INetworkInterface> reporter)
{
DetachMedium(reporter);
}
public void DetachFrom(INetworkInterface iface)
{
DetachInterface(iface);
}
public void Run()
{
lock(innerLock)
{
if(!wiresharkSender.TryOpenWireshark())
{
throw new RecoverableException("Wireshark is already running.");
}
}
}
private bool IsConfiguredForMediumType(INetworkLog<INetworkInterface> medium)
{
var typeOfInterface = medium.GetType();
lock(innerLock)
{
if(linkLayerToMedium[layer] == typeOfInterface)
{
return true;
}
}
throw new RecoverableException($"Cannot log {typeOfInterface.Name} traffic to {layer}-configured Wireshark.");
}
private void DetachMedium(INetworkLog<INetworkInterface> reporter)
{
lock(innerLock)
{
if(observedMedium.Contains(reporter))
{
observedMedium.Remove(reporter);
reporter.FrameProcessed -= SendProcessedFrame;
}
else
{
throw new RecoverableException("Wireshark doesn't contain this medium");
}
}
}
private void DetachInterface(INetworkInterface iface)
{
var removed = false;
lock(innerLock)
{
foreach(var i in observedInterfaces)
{
if(i.Value.Contains(iface))
{
i.Value.Remove(iface);
removed = true;
}
}
}
if(!removed)
{
throw new RecoverableException("Wireshark doesn't contain this interface");
}
}
private void AddMedium(INetworkLog<INetworkInterface> reporter)
{
lock(innerLock)
{
if(!observedMedium.Contains(reporter))
{
observedMedium.Add(reporter);
wiresharkSender.TryOpenWireshark();
reporter.FrameProcessed += SendProcessedFrame;
}
else
{
if(!wiresharkSender.TryOpenWireshark())
{
throw new RecoverableException("The medium is already being logged in this Wireshark instance.");
}
}
}
}
private void AddInterface(INetworkLog<INetworkInterface> reporter, INetworkInterface iface)
{
lock(innerLock)
{
if(observedInterfaces.ContainsKey(reporter))
{
if(!observedInterfaces[reporter].Contains(iface))
{
observedInterfaces[reporter].Add(iface);
wiresharkSender.TryOpenWireshark();
reporter.FrameTransmitted += SendTransmittedFrame;
reporter.FrameProcessed += SendTransmittedFrame;
}
else
{
if(!wiresharkSender.TryOpenWireshark())
{
throw new RecoverableException("The interface is already being logged in this Wireshark instance.");
}
}
}
else
{
observedInterfaces.Add(reporter, new List<INetworkInterface>() { iface });
wiresharkSender.TryOpenWireshark();
reporter.FrameTransmitted += SendTransmittedFrame;
reporter.FrameProcessed += SendTransmittedFrame;
}
}
}
private void ClearLog()
{
lock(innerLock)
{
observedInterfaces.Clear();
observedMedium.Clear();
wiresharkSender.CloseWireshark();
wiresharkSender.ClearPipe();
}
}
private void OnMachineRemoved(IMachine machine)
{
lock(innerLock)
{
var observedCopy = new Dictionary<IExternal, List<INetworkInterface>>(observedInterfaces);
foreach(var external in observedCopy)
{
foreach(var iface in external.Value.ToList())
{
if(iface is IPeripheral && machine.IsRegistered((IPeripheral)iface))
{
observedInterfaces[external.Key].Remove(iface);
}
}
if(observedInterfaces[external.Key].Count == 0)
{
observedInterfaces.Remove(external.Key);
}
}
if(observedInterfaces.Count == 0 && observedMedium.Count == 0)
{
currentEmulation.MachineRemoved -= OnMachineRemoved;
ClearLog();
currentEmulation.HostMachine.RemoveHostMachineElement(this);
}
}
}
private void SendProcessedFrame(IExternal reporter, INetworkInterface sender, byte[] buffer)
{
lock(innerLock)
{
if(linkLayerToMedium[layer] == typeof(BLEMedium))
{
buffer = bleSniffer.InsertHeaderToPacket((IRadio)sender, buffer);
}
wiresharkSender.SendProcessedFrames(buffer);
}
}
private void SendTransmittedFrame(IExternal reporter, INetworkInterface sender, INetworkInterface receiver, byte[] buffer)
{
lock(innerLock)
{
if(observedInterfaces.ContainsKey(reporter)
&& (observedInterfaces[reporter].Contains(sender) || observedInterfaces[reporter].Contains(receiver)))
{
wiresharkSender.SendReportedFrames(buffer);
}
}
}
private void SendTransmittedFrame(IExternal reporter, INetworkInterface sender, byte[] buffer)
{
lock(innerLock)
{
if(observedInterfaces.ContainsKey(reporter) && observedInterfaces[reporter].Contains(sender))
{
wiresharkSender.SendReportedFrames(buffer);
}
}
}
private readonly object innerLock = new object();
private readonly BLESniffer bleSniffer;
private WiresharkSender wiresharkSender;
private string wiresharkSinkName;
private Dictionary<IExternal, List<INetworkInterface>> observedInterfaces = new Dictionary<IExternal, List<INetworkInterface>>();
private HashSet<IExternal> observedMedium = new HashSet<IExternal>();
private LinkLayer layer;
private Emulation currentEmulation;
private readonly Dictionary<LinkLayer, Type> linkLayerToMedium = new Dictionary<LinkLayer, Type>
{
{LinkLayer.Ethernet, typeof(Switch)},
{LinkLayer.IEEE802_15_4, typeof(IEEE802_15_4Medium)},
{LinkLayer.Bluetooth_LE, typeof(BLEMedium)},
{LinkLayer.CAN, typeof(CANHub)},
};
}
}