From 073cba135efb2e8b89c81d0346b4801bba897eda Mon Sep 17 00:00:00 2001
From: Tim Young <tim.young@lightsys.org>
Date: Thu, 7 Mar 2019 17:02:49 +0300
Subject: [PATCH] Basic renumbering working.  Will blow up on a machine with
 multiple network interfaces.

---
 EduNetworkBuilder/IPAddress.cs      |   2 +-
 EduNetworkBuilder/NBRenumberData.cs | 255 +++++++++++++++++++++++++++-
 EduNetworkBuilder/NetworkBuilder.cs |  65 ++++++-
 EduNetworkBuilder/NetworkDevice.cs  |  64 +++++--
 4 files changed, 365 insertions(+), 21 deletions(-)

diff --git a/EduNetworkBuilder/IPAddress.cs b/EduNetworkBuilder/IPAddress.cs
index 3451e5a..42e0d81 100644
--- a/EduNetworkBuilder/IPAddress.cs
+++ b/EduNetworkBuilder/IPAddress.cs
@@ -242,7 +242,7 @@ namespace EduNetworkBuilder
         }
 
         /// <summary>
-        /// Return the number of hosts in the subnet.
+        /// Return the list of hosts in the subnet.
         /// </summary>
         /// <returns></returns>
         public IEnumerable<UInt32> Hosts()
diff --git a/EduNetworkBuilder/NBRenumberData.cs b/EduNetworkBuilder/NBRenumberData.cs
index d048d67..8399a39 100644
--- a/EduNetworkBuilder/NBRenumberData.cs
+++ b/EduNetworkBuilder/NBRenumberData.cs
@@ -6,6 +6,180 @@ using System.Threading.Tasks;
 
 namespace EduNetworkBuilder
 {
+    public class NBRenumberer
+    {
+        private bool renumbered = false;
+        public List<NBRenumberData> ListOfItems = new List<NBRenumberData>();
+
+        public void Count(string what)
+        {
+            NBRenumberData entry = DataFromName(what);
+            if(entry != null)
+            {
+                entry.ActiveCount++;
+                return;
+            }
+            //If we get here, we have not found it yet.
+            NBRenumberData One = new NBRenumberData(what, ListOfItems.Count);
+            One.ActiveCount++;
+            ListOfItems.Add(One);
+        }
+        public void SetAsGateway(string what)
+        {
+            NBRenumberData GW = DataFromName(what);
+            if (GW == null) {
+                GW = new NBRenumberData(what, ListOfItems.Count);
+                ListOfItems.Add(GW);
+            }
+            GW.isTheGateway = true;
+            GW.UpdateSortOrder();
+            //We can only have one gateway.  Make sure all the rest are set not to be the GW
+            foreach(NBRenumberData one in ListOfItems)
+            {
+                if (one != GW) {
+                    one.isTheGateway = false;
+                    one.UpdateSortOrder();
+                }
+
+            }
+        }
+
+        public NBRenumberData DataFromName(string hostname)
+        {
+            foreach (NBRenumberData NBRD in ListOfItems)
+            {
+                if (NBRD.Hostname == hostname)
+                {
+                    return NBRD;
+                }
+            }
+            return null;
+        }
+
+        public bool TryRenumbering(NB_IPAddress Network, NB_IPAddress Gateway)
+        {
+            renumbered = true;
+            //Separate out the devices by type
+            //  Find the gateway too
+            int biggestcount = 0;
+            int spacing = 10;
+            bool HasGateway = false;
+            foreach (NBRenumberData NBRD in ListOfItems)
+            {
+                if (NBRD.ActiveCount > biggestcount) biggestcount = NBRD.ActiveCount;
+            }
+            //now, we can prune off everything that is not part of biggestcount.
+            for(int i=ListOfItems.Count-1; i>=0; i--)
+            {
+                if (ListOfItems[i].ActiveCount != biggestcount)
+                    ListOfItems.RemoveAt(i);
+                else
+                {
+                    if (ListOfItems[i].isTheGateway) HasGateway = true;
+                    ListOfItems[i].SuggestedIP = "";
+                }
+            }
+            if (!HasGateway)
+                for (int i = ListOfItems.Count - 1; i >= 0; i--)
+                {
+                    if (ListOfItems[i].Device.HasIPAddress(Gateway))
+                        SetAsGateway(ListOfItems[i].Hostname);
+                }
+
+            int switches = 0;
+            int servers = 0;
+            int printers = 0;
+            int wireless = 0;
+            int clients = 0;
+            foreach(NBRenumberData NBRD in ListOfItems)
+            {                
+                switch(NBRD.TypeString())
+                {
+                    case "client":
+                        clients++;
+                        break;
+
+                    case "printer":
+                        printers++;
+                        break;
+
+                    case "switch":
+                        switches++;
+                        break;
+
+                    case "server":
+                        servers++;
+                        break;
+                    case "wireless":
+                        wireless++;
+                        break;
+                }
+            }
+            //Decide on which way we want to do this.
+            biggestcount = 0;
+            if (servers > biggestcount) biggestcount = servers;
+            if (switches > biggestcount) biggestcount = switches;
+            if (printers > biggestcount) biggestcount = printers;
+            if (wireless > biggestcount) biggestcount = wireless;
+            if (clients > biggestcount) biggestcount = clients;
+
+            ListOfItems.Sort((p, q) => p.sortorder.CompareTo(q.sortorder));
+
+            //If it works to do 10 for each bunch, do that.  Otherwise, see what the smallest bunch that works is.
+            if (biggestcount < 10)
+            {
+                spacing = 10;
+            }
+            if (biggestcount > 10)
+                spacing = 99; //Don't bother with anything fancy.  Punt if it does not fit the first time
+            //Fallback is to simply count up from the gateway.
+            if (Network.NumberOfHosts < ListOfItems.Count)
+                return false;
+            if(spacing * 5 > Network.NumberOfHosts)
+            {
+                renumberlist(Network, 1, "");
+            }
+            else
+            {
+                //We can do this in parts
+                renumberlist(Network, 1, "Gateway");
+                renumberlist(Network, 2, "server");
+                renumberlist(Network, spacing * 1, "switch");
+                renumberlist(Network, spacing * 2, "wireless");
+                renumberlist(Network, spacing * 3, "printer");
+                renumberlist(Network, spacing * 4, "client");
+            }
+            return true;
+        }
+
+        private void renumberlist(NB_IPAddress Network, int Base, string filter)
+        {
+            int count = Base;
+            for (int i = 0; i < ListOfItems.Count; i++)
+            {
+                if (ListOfItems[i].SuggestedIP != "") continue;
+                if (filter == "" || ListOfItems[i].TypeString() == filter || filter == "Gateway" && ListOfItems[i].isTheGateway)
+                {
+                    NB_IPAddress next = new NB_IPAddress(Network);
+                    next._ip += (UInt32)count;
+                    ListOfItems[i].SuggestedIP = next.GetIPString;
+                    count++;
+                }
+            }
+        }
+
+        public void DoRenumbering(NB_IPAddress Network, NB_IPAddress Gateway, int vlanID)
+        {
+            if (!renumbered) TryRenumbering(Network, Gateway);
+
+            //loop through and set all the IPs and the gateway IP if not DHCPS
+            for(int i = 0; i < ListOfItems.Count; i++)
+            {
+                ListOfItems[i].Device.RenumberIP(ListOfItems[i].SuggestedIP, Gateway, vlanID);
+            }
+        }
+    }
+
     public class NBRenumberData
     {
         public string Hostname = "";
@@ -19,7 +193,12 @@ namespace EduNetworkBuilder
         public int ActiveCount=0;
         public string nicname;
         public string SuggestedIP;
-        
+        public int sortorder = 0;
+        private int _index = 0;
+        public NetworkComponentType mytype = NetworkComponentType.none;
+
+        public bool isTheGateway = false;
+
         private bool _isRouter = false;
         /// <summary>
         /// Return true if the Device specified by hostname does routing.
@@ -36,5 +215,79 @@ namespace EduNetworkBuilder
                 return false;
             }
         }
+
+        public NBRenumberData(string hostname, int index)
+        {
+            Hostname = hostname;
+            NetworkDevice nd = Device;
+            mytype = nd.myType;
+            _index = index;
+            UpdateSortOrder();
+        }
+
+        public void UpdateSortOrder()
+        {
+            switch (TypeString())
+            {
+                case "client":
+                    sortorder=5000;
+                    break;
+
+                case "printer":
+                    sortorder = 4000;
+                    break;
+
+                case "switch":
+                    sortorder=2000;
+                    break;
+
+                case "server":
+                    sortorder=1000;
+                    break;
+                case "wireless":
+                    sortorder=3000;
+                    break;
+            }
+            sortorder = sortorder + _index;
+            if (isTheGateway) sortorder = 0;
+        }
+
+        public string TypeString()
+        {
+            string what = "";
+            switch (mytype)
+            {
+                case NetworkComponentType.cellphone:
+                case NetworkComponentType.ip_phone:
+                case NetworkComponentType.laptop:
+                case NetworkComponentType.pc:
+                case NetworkComponentType.tablet:
+                    what = "client";
+                    break;
+
+                case NetworkComponentType.printer:
+                case NetworkComponentType.copier:
+                    what = "printer";
+                    break;
+
+                case NetworkComponentType.net_hub:
+                case NetworkComponentType.net_switch:
+                    what = "switch";
+                    break;
+
+                case NetworkComponentType.firewall:
+                case NetworkComponentType.router:
+                case NetworkComponentType.server:
+                    what = "server";
+                    break;
+                case NetworkComponentType.wap:
+                case NetworkComponentType.wbridge:
+                case NetworkComponentType.wrepeater:
+                case NetworkComponentType.wrouter:
+                    what = "wireless";
+                    break;
+            }
+            return what;
+        }
     }
 }
diff --git a/EduNetworkBuilder/NetworkBuilder.cs b/EduNetworkBuilder/NetworkBuilder.cs
index 45b93bb..0967b0d 100644
--- a/EduNetworkBuilder/NetworkBuilder.cs
+++ b/EduNetworkBuilder/NetworkBuilder.cs
@@ -1155,6 +1155,13 @@ namespace EduNetworkBuilder
             //do it
             if (ItemsSelected.Count > 0)
             {
+                //This next entry should never happen.
+                if (ItemsSelected.Count < 2)
+                {
+                    MessageBox.Show("You need to have more items selected");
+                    return;
+                }
+
                 int ChosenVlanID = 1;
                 if(myNetwork != null && myNetwork.VLANsEnabled)
                 {
@@ -1177,13 +1184,6 @@ namespace EduNetworkBuilder
 
                 IPAddressEntry IPE = new IPAddressEntry(prompted, null, this, false);
 
-                //This next entry should never happen.
-                if (ItemsSelected.Count < 2)
-                {
-                    MessageBox.Show("You need to have more items selected");
-                    return;
-                }
-
                 //Read in the settings we will use
                 IPE.ShowAsSubnetGateway();
 
@@ -1222,10 +1222,59 @@ namespace EduNetworkBuilder
                 }
                 if (gwDevice == null) return; //No gateway device selected
 
+
+                NBRenumberer Renumberer = new NBRenumberer();
+
                 //If we get here, we have a gateway and a list of items to renumber.
-                for (int i = ItemsSelected.Count - 1; i >= 0; i--)
+                //Loop through all the items
+                //Find find all connected items
+                //loop through all connected items & count them (excluding them if they are not selected)
+                //set the gateway.
+                //count them and exclude items that are not within the threshhold.
+                
+                foreach(NetworkDevice one in ItemsSelected)
                 {
+                    List<NetworkDevice> alreadydone = new List<NetworkDevice>();
+                    List<NetworkDevice> connections = one.ConnectedTo();
+
+                    for(int i=0; i< connections.Count; i++)
+                    {
+                        NetworkDevice connection = connections[i];
+                        //If we have not done it yet
+                        if (!ItemsSelected.Contains(connection))
+                            continue;
+                        if (myNetwork.VLANsEnabled && (ChosenVlanID != connection.PrimaryVLAN() && connection.myType != NetworkComponentType.net_switch))
+                            continue; //Skip the device if it is not on the primary vlan, and is not a switch (asuming switches are taggexd)
+                        if (!alreadydone.Contains(connection))
+                        {
+                            alreadydone.Add(connection);
+                            
+                            if(!connection.RoutesPackets() || connection.myType == NetworkComponentType.wrouter)
+                                connections.AddRange(connection.ConnectedTo(true));
+                            Renumberer.Count(connection.hostname);
+                        }
+                    }
                 }
+                Renumberer.SetAsGateway(gwDevice.hostname);
+
+                Renumberer.TryRenumbering(network, gw);
+
+                if (Renumberer.ListOfItems.Count >  network.NumberOfHosts)
+                {
+                    //Not enough room in the subnet for the various items.
+                    MessageBox.Show("There are not enough IP addresses in the subnet you specified for the number of items we need to renumber.");
+                    return;
+                }
+
+                //We should print off the things we are hoping to do.
+                string message = "What we are trying to do:\n";
+                foreach(NBRenumberData NBRD in Renumberer.ListOfItems)
+                {
+                    message += NBRD.Hostname + " " + NBRD.TypeString() + " -> " + NBRD.SuggestedIP + "\n";
+                }
+                DialogResult answer = MessageBox.Show(message,"Renumber?",MessageBoxButtons.OKCancel);
+                if (answer == DialogResult.OK)
+                    Renumberer.DoRenumbering(network, gw, ChosenVlanID);
             }
 
         }
diff --git a/EduNetworkBuilder/NetworkDevice.cs b/EduNetworkBuilder/NetworkDevice.cs
index ef620f2..9ee8e88 100644
--- a/EduNetworkBuilder/NetworkDevice.cs
+++ b/EduNetworkBuilder/NetworkDevice.cs
@@ -658,14 +658,19 @@ namespace EduNetworkBuilder
         /// <summary>
         /// Return the list of devices connected to this device
         /// </summary>
-        /// <returns>A list of devices</returns>
-        List<NetworkDevice> ConnectedTo()
+        /// <param name="skipwan">For routers, do not show things connected on the WAN</param>
+        /// <returns>A list of devices</returns>     
+        public List<NetworkDevice> ConnectedTo(bool skipwan= false)
         {
             List<NetworkDevice> connected = new List<NetworkDevice>();
             foreach (NetworkCard nic in NICs)
             {
-                NetworkDevice nd = ConnectedTo(nic);
-                if (nd != null) connected.Add(nd);
+                //We want to skip connections through the wan when searching a broadcast network
+                if (!(myType == NetworkComponentType.wrouter && skipwan && nic.GetNicType == NicType.wan))
+                {
+                    NetworkDevice nd = ConnectedTo(nic);
+                    if (nd != null) connected.Add(nd);
+                }
             }
             return connected;
         }
@@ -675,7 +680,7 @@ namespace EduNetworkBuilder
         /// </summary>
         /// <param name="NIC"></param>
         /// <returns></returns>
-        NetworkDevice ConnectedTo(NetworkCard NIC)
+        public NetworkDevice ConnectedTo(NetworkCard NIC)
         {
             return NIC.ConnectedTo();
         }
@@ -684,7 +689,7 @@ namespace EduNetworkBuilder
         /// </summary>
         /// <param name="NIC">the network card ID</param>
         /// <returns></returns>
-        NetworkDevice ConnectedTo(HostNicID NIC)
+        public NetworkDevice ConnectedTo(HostNicID NIC)
         {
             NetworkCard nic = NicFromID(NIC);
             if (nic == null) return null;
@@ -695,15 +700,16 @@ namespace EduNetworkBuilder
         /// </summary>
         /// <param name="nicname">The network card name</param>
         /// <returns></returns>
-        NetworkDevice ConnectedTo(string nicname)
+        public NetworkDevice ConnectedTo(string nicname)
         {
             NetworkCard nic = NicFromName(nicname);
             if (nic == null) return null;
             return nic.ConnectedTo();
         }
 
-        public int VLANFromNIF(NetworkInterface nif)
+        public int VLANFromNIF(NetworkInterface nif, int level = 0)
         {
+            level++;
             if (DoesVLANs())
             {
                 //This device itself does vlans
@@ -716,17 +722,21 @@ namespace EduNetworkBuilder
             if (nif == null) return NB.InvalidVLAN;
             NetworkCard nic = NicFromID(nif.AttachedToHostNic);
             if (nic == null) return NB.InvalidVLAN; //Nothing
-            return VLANFromNIC(nic);
+            if (level > 5) return NB.InvalidVLAN;
+            return VLANFromNIC(nic, level);
         }
 
-        public int VLANFromNIC(NetworkCard nic)
+        public int VLANFromNIC(NetworkCard nic, int level=0)
         {
+            level++;
             int vlan = NB.InvalidVLAN;
             if (DoesVLANs())
             {
                 foreach (NetworkInterface nif in nic.interfaces)
                 {
-                    int tvlan = VLANFromNIF(nif);
+                    int tvlan = NB.InvalidVLAN;
+                    if(DoesVLANs())
+                        tvlan = VLANFromNIF(nif, level);
                     if (tvlan != NB.InvalidVLAN)
                     {
                         if (vlan != NB.InvalidVLAN && vlan != tvlan)
@@ -1358,6 +1368,38 @@ namespace EduNetworkBuilder
             return null;
         }
 
+        public bool RenumberIP(string newip, NB_IPAddress gateway, int vlanID)
+        {
+            DefaultGW = new NB_IPAddress(gateway);
+            //figure out which interface to change.
+
+            if (!RenumberIP(newip, gateway, NicType.management_interface, vlanID))
+                if (!RenumberIP(newip, gateway, NicType.eth, vlanID))
+                    return RenumberIP(newip, gateway, NicType.wlan, vlanID);
+            return true;
+        }
+
+        private bool RenumberIP(string newip, NB_IPAddress gateway, NicType what, int vlan)
+        {
+            List<NetworkCard> smalllist = NICsFromTypeNic(what);
+            smalllist.Sort((p, q) => p._nic_name.CompareTo(q._nic_name));
+            foreach (NetworkCard nic in smalllist)
+            {
+                //see if we can renumber this one
+                //It cannot be dhcp, and must be connected.
+                if((!nic.UsesDHCP && nic.isConnected(false)) || nic.GetNicType == NicType.management_interface)
+                {
+                    //If we are doing vlans, the vlan should match the interface if eth
+                    //If we are doing vlans, the vlan should match if we are doing management interface
+                    //
+                    //right now, just do it.
+                    nic.PrimaryInterface().myIP = new NB_IPAddress(newip, gateway._mask.ToIpString(), IPAddressType.ip);
+                    return true;
+                }
+            }
+            return false;
+        }
+
         public NetworkCard NicFromIP(string IP)
         {
             foreach (NetworkCard nic in NICs)