well, as said in the title: We need to get a mac-address of a NIC that is providing the connection to a particular host. We use the mac-address during the login process using MSSQL and Entity Framework.
technology: C#.NET 4.0, EF 4.1
private string _max_address;
public string ConnectionString { get; };
public string MacAddress {
get {
if (this._mac_address == null) {
SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder(this.ConnectionString);
IPAddress[] addres = Dns.GetHostAddresses(csb.DataSource.Split('\\')[0]);
//find nic interface address from routing table
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.CreateNoWindow = false;
p.StartInfo.FileName = "route";
p.StartInfo.Arguments = "PRINT";
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
p.Close();
p.Dispose();
string[] lines = output.Split(new string[]{"\r\n"}, StringSplitOptions.RemoveEmptyEntries);
List<Route> routes = new List<Route>();
bool found = false;
foreach (string line in lines) {
if (line == "Active Routes:") {
if (found) //if routes have already been found exit loop, meaning this is IPv6 routes, should never reach this point
break;
found = true;
continue; //next line
}
if (found) {
if (line[0] == 'N') //if the line is headers skip it
continue;
else if (line[0] == '=') //if the routes have ended exit loop
break;
routes.Add(new Route(line)); //read route, custom class
}
}
routes.Sort(); //sort routes by Mask, then by Address
string nic_addr = "";
foreach (IPAddress addr in addres) {
if (addr.AddressFamily == AddressFamily.InterNetwork) { //if IPv4
for (int i = routes.Count - 1; i >= 0; i--) { //start from the largest Mask
if(routes[i].IncludesHost(addr)) { //whether the ip fits in the subnet
nic_addr = routes[i].ReadableInterface;
break; //if so, save interface name, exit loop
}
}
break;
}
}
//Get NIC of found Interface by IP
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) {
if (nic.OperationalStatus == OperationalStatus.Up) {
IPInterfaceProperties properties = nic.GetIPProperties();
foreach (UnicastIPAddressInformation addr_info in properties.UnicastAddresses) {
if (addr_info.Address.AddressFamily == AddressFamily.InterNetwork && addr_info.Address.ToString()==nic_addr) {
this._mac_address = nic.GetPhysicalAddress().ToString();
break; //if address matches that of the routing table interface address then save MAC and exit loop
}
}
if (this._mac_address != null)
break; //if mac address is found exit loop;
}
}
}
return this._mac_address;
}
}
public class Route : IComparable
{
private uint _address;
private uint _mask;
private uint _interface;
private int _metric;
public uint Address { get { return this._address; } }
public uint Mask { get { return this._mask; } }
public uint Interface { get { return this._interface; } }
public string ReadableAddress { get { return Route.IPLongToString(this._address); } }
public string ReadableMask { get { return Route.IPLongToString(this._mask); } }
public string ReadableInterface { get { return Route.IPLongToString(this._interface); } }
public Route(string[] route)
{
this._address=IPStringToLong(route[0]);
this._mask=IPStringToLong(route[1]);
this._interface=IPStringToLong(route[3]);
this._metric=UInt16.Parse(route[4]);
}
public Route(string line)
: this(line.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries))
{
}
public static uint IPStringToLong(string str)
{
string[] values = str.Split(new char[]{'.'});
uint multiplier = 1;
uint value = 0;
for(int i=3;i>=0;i--) {
value+=UInt32.Parse(values[i])*multiplier;
multiplier*=256;
}
return value;
}
public static string IPLongToString(uint addr)
{
byte[] bytes = BitConverter.GetBytes(addr);
string[] bs = new string[]{"","","",""};
for(int i=0;i<4;i++) {
bs[i] = bytes[3-i].ToString();
}
return String.Join(".",bs);
}
public bool IncludesHost(IPAddress addr)
{
if (addr.AddressFamily == AddressFamily.InterNetwork)
return this.IncludesHost(Route.IPStringToLong(addr.ToString()));
else
return false;
}
public bool IncludesHost(string addr)
{
return this.IncludesHost(Route.IPStringToLong(addr));
}
public bool IncludesHost(long addr)
{
return (this._mask&this._address) == (this._mask&addr);
}
public int CompareTo(object _y)
{
Route y = (Route)_y;
if (y == null)
return 1;
else
{
int retval = this.Mask.CompareTo(y.Mask);
if (retval != 0)
return retval;
else
return this.CompareToByAddress(y);
}
}
}
EDIT:
so far, it's a mess I don't like the solution i've come up with.
- get host name from connectionstring
- get host ip from hostname
- execute "route PRINT"
- put output in string arrray
- loop through output lines
- find "Active Routes" line
- skip header
- load route into custom Route class
- exit line loop
- loop through found routes
- find matching host ip&mask network&mask
- save its interface ip
- loop through NIC
- check whether ipv4 if so check interface ip
- get mac of found NIC
isn't here a more elegant way accomplishing steps 1 through 14, it's just so much code just to get a MAC-address of a particular NIC\
OR at least a better way to accomlish steps 3 through 10
EDIT: I've missed declaration of CompareToByAddress()
public int CompareToByAddress( Route y)
{
if (y == null)
{
return 1;
}
else
{
int retval = this.Address.CompareTo(y.Address);
if (retval != 0)
{
return retval;
}
else
{
//should never reach this point meaning there's duplicate entry in the routing table
return 0;
}
}
}
though, the solutions below do not use the CompareTo methods;