1 // 2 // Copyright (c) 2010-2018 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 Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure; 11 using Antmicro.Renode.Peripherals.Network; 12 using System.Linq; 13 using Antmicro.Renode.Network; 14 using Antmicro.Renode.Logging; 15 using System.Collections.Generic; 16 using Antmicro.Renode.Exceptions; 17 using Antmicro.Renode.Peripherals; 18 using Antmicro.Renode.Time; 19 20 namespace Antmicro.Renode.Tools.Network 21 { 22 public static class SwitchExtensions 23 { CreateSwitch(this Emulation emulation, string name)24 public static void CreateSwitch(this Emulation emulation, string name) 25 { 26 emulation.ExternalsManager.AddExternal(new Switch(), name); 27 } 28 } 29 30 public class Switch : IExternal, IHasOwnLife, IConnectable<IMACInterface>, INetworkLogSwitch 31 { AttachTo(IMACInterface iface)32 public void AttachTo(IMACInterface iface) 33 { 34 AttachTo(iface, null); 35 } 36 AttachTo(IMACInterface iface, IMachine machine)37 public void AttachTo(IMACInterface iface, IMachine machine) 38 { 39 lock(innerLock) 40 { 41 if(ifaces.Any(x => x.Interface == iface)) 42 { 43 throw new RecoverableException("Cannot attach to the provided MAC interface as it is already registered in this switch."); 44 } 45 46 var ifaceDescriptor = new InterfaceDescriptor 47 { 48 Interface = iface, 49 Delegate = f => ForwardToReceiver(f, iface) 50 }; 51 52 // this is to handle TAPInterfaces that are not peripherals 53 if(iface is IPeripheral peripheralInterface) 54 { 55 ifaceDescriptor.Machine = machine ?? peripheralInterface.GetMachine(); 56 } 57 iface.FrameReady += ifaceDescriptor.Delegate; 58 ifaces.Add(ifaceDescriptor); 59 this.Log(LogLevel.Info, "Interface {0} attached", iface.MAC); 60 } 61 } 62 DetachFrom(IMACInterface iface)63 public void DetachFrom(IMACInterface iface) 64 { 65 lock(innerLock) 66 { 67 var descriptor = ifaces.SingleOrDefault(x => x.Interface == iface); 68 if(descriptor == null) 69 { 70 this.Log(LogLevel.Warning, "Detaching mac interface that is currently not attached: {0}", iface.MAC); 71 return; 72 } 73 74 ifaces.Remove(descriptor); 75 iface.FrameReady -= descriptor.Delegate; 76 foreach(var m in macMapping.Where(x => x.Value == iface).ToArray()) 77 { 78 macMapping.Remove(m.Key); 79 } 80 this.Log(LogLevel.Info, "Interface {0} detached", iface.MAC); 81 } 82 } 83 EnablePromiscuousMode(IMACInterface iface)84 public void EnablePromiscuousMode(IMACInterface iface) 85 { 86 lock(innerLock) 87 { 88 var descriptor = ifaces.SingleOrDefault(x => x.Interface == iface); 89 if(descriptor == null) 90 { 91 throw new RecoverableException("The interface is not registered, you must connect it in order to change promiscuous mode settings"); 92 } 93 descriptor.PromiscuousMode = true; 94 this.Log(LogLevel.Info, "Promiscuous mode enabled for interace {0}", iface.MAC); 95 } 96 } 97 DisablePromiscuousMode(IMACInterface iface)98 public void DisablePromiscuousMode(IMACInterface iface) 99 { 100 lock(innerLock) 101 { 102 var descriptor = ifaces.SingleOrDefault(x => x.Interface == iface); 103 if(descriptor == null) 104 { 105 throw new RecoverableException("The interface is not registered, you must connect it in order to change promiscuous mode settings"); 106 } 107 if(!descriptor.PromiscuousMode) 108 { 109 throw new RecoverableException("The interface is not in promiscuous mode"); 110 } 111 descriptor.PromiscuousMode = false; 112 this.Log(LogLevel.Info, "Promiscuous mode disabled for interace {0}", iface.MAC); 113 } 114 } 115 Start()116 public void Start() 117 { 118 Resume(); 119 } 120 Pause()121 public void Pause() 122 { 123 started = false; 124 } 125 Resume()126 public void Resume() 127 { 128 started = true; 129 } 130 131 public bool IsPaused => !started; 132 133 public event Action<IExternal, IMACInterface, IMACInterface, byte[]> FrameTransmitted; 134 public event Action<IExternal, IMACInterface, byte[]> FrameProcessed; 135 ForwardToReceiver(EthernetFrame frame, IMACInterface sender)136 private void ForwardToReceiver(EthernetFrame frame, IMACInterface sender) 137 { 138 this.Log(LogLevel.Noisy, "Received frame from interface {0}", sender.MAC); 139 140 FrameProcessed?.Invoke(this, sender, frame.Bytes); 141 142 if(!started) 143 { 144 return; 145 } 146 lock(innerLock) 147 { 148 var interestingIfaces = macMapping.TryGetValue(frame.DestinationMAC, out var destIface) 149 ? ifaces.Where(x => (x.PromiscuousMode && x.Interface != sender) || x.Interface == destIface) 150 : ifaces.Where(x => x.Interface != sender); 151 152 if(!TimeDomainsManager.Instance.TryGetVirtualTimeStamp(out var vts)) 153 { 154 // it happens when sending from tap interface 155 vts = new TimeStamp(default(TimeInterval), EmulationManager.ExternalWorld); 156 } 157 158 foreach(var iface in interestingIfaces) 159 { 160 this.Log(LogLevel.Noisy, "Forwarding frame to interface {0}", iface.Interface.MAC); 161 162 if(iface.Machine == null) 163 { 164 iface.Interface.ReceiveFrame(frame.Clone()); 165 continue; 166 } 167 168 iface.Machine.HandleTimeDomainEvent(iface.Interface.ReceiveFrame, frame.Clone(), vts, () => 169 { 170 FrameTransmitted?.Invoke(this, sender, iface.Interface, frame.Bytes); 171 }); 172 } 173 } 174 175 // at the same we will potentially add current MAC address assigned to the source 176 lock(innerLock) 177 { 178 macMapping[frame.SourceMAC] = sender; 179 } 180 } 181 182 private bool started = true; 183 184 private readonly object innerLock = new object(); 185 private readonly HashSet<InterfaceDescriptor> ifaces = new HashSet<InterfaceDescriptor>(); 186 private readonly Dictionary<MACAddress, IMACInterface> macMapping = new Dictionary<MACAddress, IMACInterface>(); 187 188 private class InterfaceDescriptor 189 { 190 public IMachine Machine; 191 public IMACInterface Interface; 192 public bool PromiscuousMode; 193 public Action<EthernetFrame> Delegate; 194 GetHashCode()195 public override int GetHashCode() 196 { 197 return Interface.GetHashCode(); 198 } 199 Equals(object obj)200 public override bool Equals(object obj) 201 { 202 var objAsInterfaceDescriptor = obj as InterfaceDescriptor; 203 return objAsInterfaceDescriptor != null && Interface.Equals(objAsInterfaceDescriptor.Interface); 204 } 205 } 206 } 207 } 208 209