Há um tempo abri um tema em forma de pergunta no Stackoverflow, tanto em português quanto em inglês.
Em uma rede local, queria ter controle de dispositivos que se conectassem a um servidor para realizar tarefas automáticas. O servidor local poderia enviar e receber comandos desses dispositivos. Além do essencial, queria gravar um histórico, e saber quem era quem, pelo IP. Como os dispositivos tinham muitas limitações (não tinham processadores para instalar ferramentas, apenas placas de WiFi), o único jeito era capturar a comunicação TCP/IP destes dispositivos com o servidor. A exclusividade teórica do MAC-Address de uma placa WiFi poderia ser o identificador exclusivo de cada dispositivo. Mas como conseguir isso em uma plataforma mais universal?
Graças ao Alessandro Schneider, pude desenvolver uma solução bem simples. Ele ainda recomenda o uso para Linux do arping, solução mais robusta, competente.
A seguir disponho uma solução para fazer um sistema que rode em W10 e Linux. Esta solução foi testada em um Raspberry 3.0.
using System.Diagnostics; namespace Ferramentas { public class Rede { ///FOR LINUX public static string Bash(string cmd) { var escapedArgs = cmd.Replace("\"", "\\\""); var process = new Process() { StartInfo = new ProcessStartInfo { FileName = "/bin/bash", Arguments = $"-c \"{escapedArgs}\"", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true, } }; process.Start(); string result = process.StandardOutput.ReadToEnd(); process.WaitForExit(); return result; } ///FOR WINDOWS public static string CMD(string cmd) { var process = new Process() { StartInfo = new ProcessStartInfo { FileName = "cmd.exe", Arguments = $@"/c {cmd}", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true, } }; process.Start(); string result = process.StandardOutput.ReadToEnd(); process.WaitForExit(); return result; } //Mais métodos } } |
Poderia transformar esses dois métodos em um só? Sim!!! Mas estão aqui, separados, apenas para mostrar que muitas coisas em Windows e Linux são comuns com o .NET Core.
Continuando…
using System.Diagnostics; using System.Net.NetworkInformation; namespace Ferramentas { public class Rede { //Método Bash //Método CMD //Tratativa de erro simplificada, sem Log, sem envio por e-mail e sem _erro.Clear() private static StringBuilder _erro = new StringBuilder(); public static string ErrorMessage { get { return _erro.ToString(); } } ///Primeira tentativa de comunicação public static bool Ping(string ipOrName, int timeout = 0, bool throwExceptionOnError = false) { bool p = false; try { using (Ping ping = new Ping()) { PingReply reply = null; if (timeout > 0) reply = ping.Send(ipOrName, timeout); else reply = ping.Send(ipOrName); if (reply != null) p = (reply.Status == IPStatus.Success); //p = Convert.ToInt32(reply.RoundtripTime); } } catch (PingException e) { _erro.Append(e.Message); _erro.Append(Environment.NewLine); if (throwExceptionOnError) throw e; } catch (Exception ex) { _erro.Append(ex.Message); _erro.Append(Environment.NewLine); } return p; } //Mais métodos } } |
Agora usaremos duas classes (poderia ser uma) para chamar as ferramentas. Essas classes é que poderiam ser as públicas, para uso externo.
using System.Diagnostics; using System.Net.NetworkInformation; using System.Text; using System.Text.RegularExpressions; namespace Ferramentas { public class Rede { //Método Bash //Método CMD //Método Ping public static string TryGetMacAddressOnLinux(string ip) { _erro.Clear(); //Descobrir o IP para usar o comando arp (comum tanto em Windows quanto em Linux) if (!Ping(ip)) _erro.Append("Não foi possível testar a conectividade (ping) com o ip informado.\n"); //O comando arp devolve o MAC-Address do IP informado string arp = $"arp -a {ip}"; string retorno = Bash(arp); StringBuilder sb = new StringBuilder(); //A diferença para o Windows é o formato do retorno. Linux usa ":" como separador string pattern = @"(([a-f0-9]{2}:?){6})"; int i = 0; foreach (Match m in Regex.Matches(retorno, pattern, RegexOptions.IgnoreCase)) { if (i > 0) //Contrariando Windows e Linux, devolvo o resultado com o separador ";". sb.Append(";"); sb.Append(m.ToString()); i++; } return sb.ToString(); } public static string TryGetMacAddressOnWindows(string ip) { _erro.Clear(); //Descobrir o IP para usar o comando arp (comum tanto em Windows quanto em Linux) if (!Ping(ip)) _erro.Append("Não foi possível testar a conectividade (ping) com o ip informado.\n"); //O comando arp devolve o MAC-Address do IP informado string arp = $"arp -a {ip}"; string retorno = CMD(arp); StringBuilder sb = new StringBuilder(); //A diferença para o Linux é o formato do retorno. Windows usa "-" como separador string pattern = @"(([a-f0-9]{2}-?){6})"; int i = 0; foreach (Match m in Regex.Matches(retorno, pattern, RegexOptions.IgnoreCase)) { if (i > 0) //Contrariando Windows e Linux, devolvo o resultado com o separador ";". sb.Append(";"); sb.Append(m.ToString()); i++; } return sb.ToString(); } //Mais métodos } } |
Agora, para usar, pode-se usar alguma lógica que ache mais conveniente.
Em essência seria algo como:
public Socket MeuSocket { get; set; } IPEndPoint remoteIpEndPoint = MeuSocket.RemoteEndPoint as IPEndPoint; if (remoteIpEndPoint == null) { //Trata erro e sai } if (_tipo == MeuEnum.LINUX_ARM || _tipo == MeuEnum.LINUX) { macAddress = Rede.TryGetMacAddressOnLinux(remoteIpEndPoint.Address.ToString()); } else { macAddress = Rede.TryGetMacAddressOnWindows(remoteIpEndPoint.Address.ToString()); } |
Espero que ajude alguém!!