public new EthernetPort this[string name] { get { if (!_switchPortInfos.ContainsKey(name)) { _switchPortInfos[name] = new SwitchPortInfo { Mode = AccessMode.ACCESS, Vlan = Vlan.Get(1).Get() }; } return(base[name]); } }
public void SetPort(string port, AccessMode mode, Option <ushort> vlan) { Log.Info(Hostname, $"Setting port {port} to {mode.ToString().ToLower()} mode"); if (mode == AccessMode.TRUNK) { if (vlan != null) { throw new Exception("Can't set VLAN for a trunking port!"); } } if (vlan != null) { Log.Debug(Hostname, $"Accessing VLAN {vlan.Get()} on port {port}"); } else { vlan = Vlan.Get(1); } _switchPortInfos[port] = new SwitchPortInfo { Mode = mode, Vlan = vlan.Get() }; }
protected virtual async Task Main() { Log.SetLevel(Log.Level.TRACE, Log.Groups.SHOW); Vlan.Register(1, "DEFAULT"); Global.SetDeviceAutoStartup(true); //Global.SetPortAutoInit(true); /* * PC1 --- eth0/1 * | * | fa0/1 * SW1 * | fa0/2 * | * | fa0/1 * SW2 * | fa0/1 * | * PC2 --- eth0/1 */ Log.Group("Initialize devices"); var pc1 = new EthernetDevice("pc1"); var pc2 = new EthernetDevice("pc2"); var sw1 = new EthernetSwitch("sw1"); var sw2 = new EthernetSwitch("sw2"); Log.Group("Initialize ports"); //pc ports pc1[ETH01].Init(); pc2[ETH01].Init(); //Connection from sw1 to pc1 sw1[FA01].Init(); //Connection from sw2 to pc2 sw2[FA01].Init(); //Connection from sw1 to sw2 sw1[FA02].Init(); sw2[FA02].Init(); Log.Group("Connect ports"); //Connect the pcs to the switches pc1[ETH01].ConnectTo(sw1[FA01]); pc2[ETH01].ConnectTo(sw2[FA01]); //Connect the switches to each other sw1[FA02].ConnectTo(sw2[FA02]); Log.Group("Set switchport modes"); //Set the ports from pc to switch to access vlan 1 sw1.SetPort(FA01, EthernetSwitch.AccessMode.ACCESS, Vlan.Get(1)); sw2.SetPort(FA01, EthernetSwitch.AccessMode.ACCESS, Vlan.Get(1)); //Set the ports from switch to switch to trunk sw1.SetPort(FA02, EthernetSwitch.AccessMode.TRUNK, null); sw2.SetPort(FA02, EthernetSwitch.AccessMode.TRUNK, null); //Log.Group("Current state"); //Log.PrintState(); //Learn MAC Addresses Log.Group("Learn MAC Addresses"); /* * The API can be used with constructors (like this) */ pc1[ETH01].SendSync(new EthernetFrame(Constants.ETHERNET_BROADCAST_PORT, pc1[ETH01], Vlan.Get(1), new RawPacket(new byte[100]))); //Wait for all sending operations to be finished (you don't HAVE to wait...I just prefer doing so, cause the log is more readable) //This is necessary cause even tho you send this frame synchronously, all the connected devices create new tasks for incoming frames await Global.WaitForOperationsFinished(); /* * Or like this (with a static methods and a scapy-esque construction method) */ pc2[ETH01].SendSync(Ethernet(Constants.ETHERNET_BROADCAST_ADDRESS, pc2[ETH01]) < -/*Yes...this is indeed valid C#*/ Dot1Q(Vlan.Get(1)) < -RawPacket(new byte[100])); await Global.WaitForOperationsFinished(); Log.Group("Send Ethernet frame over learned ports"); pc1[ETH01].SendAsync(Ethernet(pc2[ETH01], pc1[ETH01]) | Dot1Q(Vlan.Get(1)) | RawPacket(new byte[100])); await Global.WaitForOperationsFinished(); pc1[ETH01].Disconnect(); pc2[ETH01].Disconnect(); pc1.Shutdown(); pc2.Shutdown(); Log.PrintState(); //Console.ReadKey(); }
public EthernetSwitch(string name) : base(name, null) { Log.Info(Hostname, "Initializing switch..."); OnReceive = (frame, port) => { if (frame.Data.Header.EtherType <= 1500) { //Ok...so apparently, an Ethernet 2 frame is constructed the same way a normal Ethernet Frame is //The only difference is in the Type field //In Ethernet 2 this is 2 bytes which, in decimal, is >= 1536 //And in Ethernet <= 1500 //We also expect this frame to be untagged //https://networkengineering.stackexchange.com/questions/5300/what-is-the-difference-between-ethernet-ii-and-802-3-ethernet //TODO: Handle STP BPDU return; } //If an untagged frame comes in, tag it if (!(frame.Data.Payload is Dot1QPDU)) { var type = frame.Data.Header.EtherType; var payload = frame.Data.Payload; var dot1q = new Dot1QPDU { Header = new Dot1QHeader { Type = type, VlanID = _switchPortInfos[port.Name].Vlan == 0 ? Vlan.Get(1).Get() : _switchPortInfos[port.Name].Vlan }, Payload = payload }; frame.Data.Payload = dot1q; frame.Data.Header.EtherType = 0x8100; frame.Data.FCS = Util.GetFCS(frame); } lock (MACTable) { if (!(MACTable.Any(a => a.Key == ((Dot1QPDU)frame.Data.Payload).Header.VlanID) && MACTable .Where(a => a.Key == ((Dot1QPDU)frame.Data.Payload).Header.VlanID) .Select(a => a.Value).FirstOr(new Dictionary <MACAddress, string>()) .Any(a => a.Key == frame.Data.Header.Src))) { Log.Warn(Hostname, $"Unknown MAC Address {frame.Data.Header.Src} for VLAN {((Dot1QPDU) frame.Data.Payload).Header.VlanID.ToMACAddressString()}"); Log.Debug(Hostname, $"Adding MAC Address {frame.Data.Header.Src} to MAC Address table for VLAN {((Dot1QPDU) frame.Data.Payload).Header.VlanID.ToMACAddressString()}..."); if (MACTable.All(a => a.Key != ((Dot1QPDU)frame.Data.Payload).Header.VlanID)) { MACTable[((Dot1QPDU)frame.Data.Payload).Header.VlanID] = new Dictionary <MACAddress, string>(); } var id = MACTable.Where(a => a.Key == ((Dot1QPDU)frame.Data.Payload).Header.VlanID).Select(a => a.Key).First(); MACTable[id].Add(frame.Data.Header.Src, port.Name); } } var dstPort = string.Empty; if (frame.Data.Header.Dst != Constants.ETHERNET_BROADCAST_ADDRESS.Get()) { dstPort = MACTable.Where(a => a.Key == ((Dot1QPDU)frame.Data.Payload).Header.VlanID && a.Value.Any(b => b.Key == frame.Data.Header.Dst)).Select(a => a.Value.Where(b => b.Key == frame.Data.Header.Dst).Select(b => b.Value).FirstOr(null)).FirstOr(null); } //Flooding if (string.IsNullOrEmpty(dstPort)) { //Send to all ports except for source port //Send to all access ports in the same VLAN //Send to all trunk ports var dstPorts = _switchPortInfos.Where(a => a.Key != port.Name && (a.Value.Vlan == ((Dot1QPDU)frame.Data.Payload).Header.VlanID || a.Value.Mode == AccessMode.TRUNK)).Select(a => a.Key).ToList(); if (!dstPorts.Any()) { Log.Error(Hostname, "Can't send a frame to any possible port (Possible VLAN mismatch)! Dropping!"); return; } foreach (var s in dstPorts) { base[s].Send(frame, true); } } else { base[dstPort].Send(frame, true); } }; PostConstruct(); }