Obtendo o MAC Address da interface de rede…

MAC é a sigla de Media Access Control. O MAC Address é o endereço da sua placa de rede. Este endereço é diferente do IP Address. Este último é o endereço da Inetnet. Se você pretende lidar com a camada de link (Link Layer ou Layer 2), você precisa saber qual é o endereço MAC da sua placa de rede e, se quiser saber o endereço MAC de algum dispositivo remoto, dado um endereço IP, deverá usar um protocolo conhecido como ARP (Address Resolution Protocol).

Não estou interessado, aqui, por enquanto, em obter o endereço MAC de outros dispositivos a não ser o da sua placa de rede.

O método é bem simples: Crie um socket com base na família desejada (AF_INET ou AF_INET6) e peça ao driver de rede para te dar o MAC Address para o dispositivo que você quer, usando o socket previamente criado:

/* Teste: Pega MAC address de eth0. */
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>

/* Um endereço MAC tem 6 bytes de tamanho! */
typedef union {
  unsigned char u8[6];
  unsigned long long u64; 
} mac_addr_t;

/* Definição do macro macaddr_ntohll(), se precisarmos dele! */
#if __BYTE_ORDER == __LITTLE_ENDIAN
  #ifdef __x86_64
    #define macaddr_ntohll(x) (__builtin_bswap64((x)) >> 16)
  #else
    // IA-32 não tem __builtin_bswap64()!
    #define macaddr_htonll(x) { \
      unsigned long long _a = (unsigned long long)(x); \
      _a >>= 16; \
      unsigned int *_b = (unsigned int *)&_a, tmp; \
      tmp = *_b; *_b = *(_b + 1); *(_b + 1) = tmp; \
      *((unsigned long long *)&(x)) = \
        __builtin_bswap32(*_b) + ((unsigned long long)__builtin_bswap32(*(_b + 1)) << 32); \
    }
  #endif
#else
  #define macaddr_ntohll(x) ((x) & 0x0000ffffffffffffULL)
#endif

int main(void)
{
  int fd;
  struct ifreq ifr = {};
  mac_addr_t *macptr;

  /* Cria o socket.  */
  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
  {
    perror("socket");
    return 1;
  }

  /* Preenche a estrtutura que o driver precisa. */
  ifr.ifr_addr.sa_family = AF_INET;
  strncpy(ifr.ifr_name, "eth0", IFNAMSIZ - 1);

  /* Chama o driver pedindo o MAC Address */
  if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1)
  {
    perror("ioctl");
    close(fd);
    return 1;
  }

  macptr = (mac_addr_t *)ifr.ifr_hwaddr.sa_data;

  printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
    macptr->u8[0], macptr->u8[1], macptr->u8[2], 
    macptr->u8[3], macptr->u8[4], macptr->u8[5]);

  /* Imprime em hexadecimal, note que o endereço está 
     orientado como BIG ENDIAN! */
  printf("0x%llx\n", macaddr_ntohll(mac->u64));

  close(fd);

  return 0;
}

Isso, provavelmente, só funcionará para LINUX. Ainda é preciso verificar se funciona no FreeBSD e OS/X.
No Windows, nem pensar!

Anúncios

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s