What port is that system on anyways? C#, SNMP and Dell Switch Ports

If any of you have ever tried hunting down a port when the network goes out on a punch down only to realize your documentation hasn’t been kept up on will appreciate this one.  After digging through many unanswered threads, the very few well written blogs on the subject I managed to find a decent method for correlating a switch port to a host name.

It’s not something you would use all the time, but when you need it, you need it.

I have to give thanks to the creators of SnmpSharpNet and to the article that helped me find the correct OID for Dell Switch Ports by Geoff Garside as they were essential in creating this project.

While this article pertains primarily to Dell Switches, since that’s what I had to work with, it could easily be setup to work with HP by changing the OID and possibly others by tweaking the SNMP portion. The OID for Dell switches, at least 3500 and 5500 Series based on experience and other articles is 1.3.6.1.2.1.17.7.1.2.2.1.2.1 though there is a good chance it will work with any of them.

I decided on a WPF application this time around since you can create a list of custom objects and have it displayed on a DataGrid in a single line of code, very cool stuff.

The CODE:

using SnmpSharpNet;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace PortMapper
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        List<KeyValuePair<string, string>> portList = new List<KeyValuePair<string, string>>();
        List<Portmap> portMaps = new List<Portmap>();

        public class Portmap
        {
            public string Hostname { get; set; }
            public string Port { get; set; }
            public string IP { get; set; }
            public string MAC { get; set; }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            IPAddress ip = IPAddress.Parse("192.168.0.2");
            IPAddress ip2 = IPAddress.Parse("192.168.0.3");
            SnmpWalk(ip, "community", "1.3.6.1.2.1.17.7.1.2.2.1.2.1", "1");
            SnmpWalk(ip2, "community","1.3.6.1.2.1.17.7.1.2.2.1.2.1", "2");
            DhcpQuery("netsh", "dhcp server \\\\servername scope 192.168.0.0 show clients", "192");
            List<Portmap> gridResults = new List<Portmap>();
            //Example of filtering uplink ports from other switches
            foreach(Portmap portMap in portMaps)
            {
                if (portMap.Port != "1/2/48" && portMap.Port != "2/1/48")
                {
                    gridResults.Add(portMap);
                }
            }
            PortMapGrid.ItemsSource = gridResults;
        }
        
        //Use NETSH to retrieve a list of DHCP MAC and IP addresses
        private void DhcpQuery(string cmd, string args, string subnet)
        {
            ProcessStartInfo procStartInfo = new ProcessStartInfo();
            procStartInfo.RedirectStandardOutput = true;
            procStartInfo.UseShellExecute = false;
            procStartInfo.FileName = cmd;
            procStartInfo.Arguments = args;
            procStartInfo.CreateNoWindow = true;
            string output;
            using (Process proc = Process.Start(procStartInfo))
            {
                output = proc.StandardOutput.ReadToEnd();
                proc.WaitForExit();
            }

            //Find valid leases in command output
            string[] lines = output.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
            List<string> leases = new List<string>();
            foreach (string line in lines)
            {
                if (line.StartsWith(subnet))
                {
                   leases.Add(line);
                }
            }

            //Create threads
            Thread[] threadArray = new Thread[leases.Count];
            int threadcount = 0;

            //Loop each Dhcp Lease
            foreach (string line in leases)
            {
                string[] pieces = line.Split('-');
                string ipAddress = pieces[0].Trim();
                string mac = "";
                string hostname = "";
                foreach (string piece in pieces)
                {
                    if (piece.Trim().Length == 2)
                    {
                        mac += piece;
                    }
                }

                ThreadStart start = delegate
                {
                    hostname = GetHost(ipAddress);
                    foreach (KeyValuePair<string, string> port in portList)
                    {
                        if (port.Key.ToUpper().Trim() == mac.ToUpper().Trim())
                        {
                            Portmap portMap = new Portmap();
                            portMap.IP = ipAddress;
                            portMap.MAC = mac.ToUpper();
                            portMap.Port = port.Value;
                            portMap.Hostname = hostname;
                            portMaps.Add(portMap);
                        }
                    }
                };
                threadArray[threadcount] = new Thread(start);
                threadArray[threadcount].Start();
                threadcount = threadcount + 1;
            }

            //Join all threads in the array to wait for results
            for (int i = 0; i < threadcount; i++)
            {
                threadArray[i].Join();
            }
        }

        //SNMPWALK the ports on a switch or stack of switches. Ports will be labeled SwitchNum/Stack Number/Port Numbers.
        private void SnmpWalk(IPAddress ip, string snmpCommunity, string oid, string switchNum)
        {
            UdpTarget target = new UdpTarget(ip);

            // SNMP community name
            OctetString community = new OctetString(snmpCommunity);
            // Define agent parameters class
            AgentParameters param = new AgentParameters(community);
            // Set SNMP version to 1
            param.Version = SnmpVersion.Ver1;


            // Define Oid that is the root of the MIB tree you wish to retrieve
            Oid rootOid = new Oid(oid);

            // This Oid represents last Oid returned by the SNMP agent
            Oid lastOid = (Oid)rootOid.Clone();

            // Pdu class used for all requests
            Pdu pdu = new Pdu(PduType.GetNext);

            // Loop through results
            while (lastOid != null)
            {
                // When Pdu class is first constructed, RequestId is set to a random value
                // that needs to be incremented on subsequent requests made using the
                // same instance of the Pdu class.
                if (pdu.RequestId != 0)
                {
                    pdu.RequestId += 1;
                }
                // Clear Oids from the Pdu class.
                pdu.VbList.Clear();
                // Initialize request PDU with the last retrieved Oid
                pdu.VbList.Add(lastOid);
                // Make SNMP request
                SnmpV1Packet result = (SnmpV1Packet)target.Request(pdu, param);
                // You should catch exceptions in the Request if using in real application.

                // If result is null then agent didn't reply or we couldn't parse the reply.
                if (result != null)
                {
                    // ErrorStatus other then 0 is an error returned by 
                    // the Agent - see SnmpConstants for error definitions
                    if (result.Pdu.ErrorStatus != 0)
                    {
                        // agent reported an error with the request
                        Console.WriteLine("Error in SNMP reply. Error {0} index {1}",
                            result.Pdu.ErrorStatus,
                            result.Pdu.ErrorIndex);
                        lastOid = null;
                        break;
                    }
                    else
                    {
                        // Walk through returned variable bindings
                        foreach (Vb v in result.Pdu.VbList)
                        {
                            // Check that retrieved Oid is "child" of the root OID
                            if (rootOid.IsRootOf(v.Oid))
                            {
                                //Convert OID to MAC
                                string[] macs = v.Oid.ToString().Split('.');
                                string mac = "";
                                int counter = 0;
                                foreach (string chunk in macs)
                                {
                                    if (counter >= macs.Length - 6)
                                    {
                                        mac += string.Format("{0:X2}", int.Parse(chunk));
                                    }
                                    counter += 1;
                                }

                                //Assumes a 48 port switch (52 actual). You need to know these values to correctly iterate through a stack of switches.
                                int dellSwitch = 1 + int.Parse(v.Value.ToString()) / 52;
                                int port = int.Parse(v.Value.ToString()) - (52 * (dellSwitch - 1));
                                KeyValuePair<string, string> Port = new KeyValuePair<string, string>(mac, switchNum + "/" + dellSwitch.ToString() + "/" + port.ToString());
                                portList.Add(Port);

                                //Exit Loop
                                lastOid = v.Oid;
                            }
                            else
                            {
                                //End of the requested MIB tree. Set lastOid to null and exit loop
                                lastOid = null;
                            }
                        }
                    }
                }
                else
                {
                    Console.WriteLine("No response received from SNMP agent.");
                }
            }
            target.Close();
        }

        private string GetHost(string ipAddress)
        {
            try
            {
                IPHostEntry entry = Dns.GetHostEntry(ipAddress);
                return entry.HostName;
            }
            catch
            {
                return "";
            }
        }
    }
}