通过IP获取MAC地址例子(应用层)

  博客地址:http://home.cnblogs.com/u/zengjianrong/

  由于某种需求,需要获取某个ip的mac地址,在应用层实现例子如下代码。

  流程:1. 先遍历arp表,若存在对应mac地址,则取出并结束。否则继续。

     2. 构造arp包,发arp request,若收不到arp reply,则返回失败并结束。否则继续。

     3. 解析arp reply包,获取MAC,并将ip、mac信息存到内核的arp表。

#include <errno.h>
#include <net/if_arp.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define MAC_BCAST_ADDR        (unsigned char *)"xffxffxffxffxffxff"
#define LOCAL_MAC_ADDR        (unsigned char *)"x00x50x56xCCx99x0E"
#define LOCAL_DEV           (unsigned char *)"eth0"

#ifndef  ATF_FORCE_ADD
#define ATF_FORCE_ADD    0x80        /* force to add an entry    --add by zengjianrong */
#endif


typedef unsigned int        u_int32_t;
typedef unsigned int        u_int;

#ifdef ZJR_READ  /* just for reading */
typedef unsigned short    sa_family_t; 
struct sockaddr {
    sa_family_t    sa_family;    /* address family, AF_xxx    */
    char        sa_data[14];    /* 14 bytes of protocol address    */
};
struct sockaddr_in 
{
    sa_family_t        sin_family;    /* Address family        */
    __be16            sin_port;    /* Port number            */
    struct in_addr    sin_addr;    /* Internet address        */

    /* Pad to size of `struct sockaddr'. */
    unsigned char    __pad[__SOCK_SIZE__ - sizeof(short int) -
            sizeof(unsigned short int) - sizeof(struct in_addr)];
};
struct arpreq 
{
    struct sockaddr    arp_pa;        /* protocol address        */
    struct sockaddr    arp_ha;        /* hardware address        */
    int            arp_flags;    /* flags            */
    struct sockaddr       arp_netmask;    /* netmask (only for proxy arps) */
    char            arp_dev[16];
};
#endif

#define SYSTEM_ERROR    1
#define PROCESS_PRINT   0
#define DEBUGGING_LVL   0
#define DEBUGGING

#ifdef DEBUGGING
#define DEBUG(lvl, ...)
    do
    {
        if (lvl >= DEBUGGING_LVL)
        {
            printf("[DEBUG] in %s:%d %s(), ",
                __FILE__, __LINE__, __FUNCTION__);
            printf(__VA_ARGS__);
            printf("
");
        }
    } while(0)
#else
#define DEBUG(lvl, ...) do {} while(0)
#endif 

typedef enum
{
    ARP_TBL_BEGIN = 1,
    ARP_TBL_GET,
    ARP_TBL_SET,
    ARP_TBL_DEL,
    ARP_TBL_END,
}ARP_TBL_TRL_ENUM;

struct arpMsg 
{
    struct ethhdr ethhdr;           /* Ethernet header */
    u_short htype;              /* hardware type (must be ARPHRD_ETHER) */
    u_short ptype;              /* protocol type (must be ETH_P_IP) */
    u_char  hlen;               /* hardware address length (must be 6) */
    u_char  plen;               /* protocol address length (must be 4) */
    u_short operation;          /* ARP opcode */
    u_char  sHaddr[6];          /* sender's hardware address */
    u_char  sInaddr[4];         /* sender's IP address */
    u_char  tHaddr[6];          /* target's hardware address */
    u_char  tInaddr[4];         /* target's IP address */
    u_char  pad[18];            /* pad for min. Ethernet payload (60 bytes) */
};

static int fd_arp_tbl = -1;

/******************************************************************************
 * nSendArp - send arp, get mac from arp reply if have
 * DESCRIPTION: -  
 * Input:       unRemoteIp  - remote ip
 *              unLocalIp   - our ip
 *              pucLocalMac - our mac address
 *              pchEth      - interface to use
 * Output:      pstArp      - arp info
 * Returns:     1           - addr free
 *              0           - addr used, get mac ok
 *              -1          - error 
 * 
 * modification history
 * --------------------
 * 2.00, 2014-12-25 , zengjianrong written
 * --------------------
 ******************************************************************************/
int nSendArp(u_int32_t unRemoteIp, u_int32_t unLocalIp, unsigned char *pucLocalMac, char *pchEth, struct arpMsg *pstArp)
{
    int     nTimeOut = 2;
    int     nOptVal = 1;
    int     nSk = -1;         /* socket */
    int     nRtVal = 1;         /* return value */
    fd_set  fdset;
    time_t  prevTime;
    struct sockaddr addr;   /* for interface name */
    struct timeval  tm;

    if (-1 == (nSk=socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)))) 
    {
        DEBUG(SYSTEM_ERROR, "func->socket error(%d:%s).", errno, strerror(errno));
        return -1;
    }

    if (-1 == setsockopt(nSk, SOL_SOCKET, SO_BROADCAST, &nOptVal, sizeof(nOptVal))) 
    {        
        DEBUG(SYSTEM_ERROR, "func->setsocketopt error(%d:%s).", errno, strerror(errno));
        close(nSk);
        return -1;
    }

    /* make a arp request */
    memset(pstArp, 0, sizeof(struct arpMsg));
    memcpy(pstArp->ethhdr.h_dest, MAC_BCAST_ADDR, 6);  /* MAC DA */
    memcpy(pstArp->ethhdr.h_source, pucLocalMac, 6);               /* MAC SA */
    pstArp->ethhdr.h_proto = htons(ETH_P_ARP);             /* protocol type (Ethernet) */
    pstArp->htype = htons(ARPHRD_ETHER);                   /* hardware type */
    pstArp->ptype = htons(ETH_P_IP);                       /* protocol type (ARP message) */
    pstArp->hlen = 6;                                      /* hardware address length */
    pstArp->plen = 4;                                      /* protocol address length */
    pstArp->operation = htons(ARPOP_REQUEST);              /* ARP op code */
    *((u_int *) pstArp->sInaddr) = unLocalIp;                     /* source IP address */
    memcpy(pstArp->sHaddr, pucLocalMac, 6);                        /* source hardware address */
    //*((u_int *) pstArp->tInaddr) = unRemoteIp;         
    memcpy(pstArp->tInaddr, (unsigned char *)&unRemoteIp, sizeof(pstArp->tInaddr));/* target IP address */    
    memset(&addr, 0, sizeof(addr));
    strcpy(addr.sa_data, pchEth);
    
    /* send arp request */
    if (0 > sendto(nSk, pstArp, sizeof(struct arpMsg), 0, &addr, sizeof(addr)))
    {
        DEBUG(SYSTEM_ERROR, "func->sendto error(%d:%s).", errno, strerror(errno));
        close(nSk);
        return -1;
    }

    /* wait arp reply, and check it */
    tm.tv_usec = 0;
    time(&prevTime);
    while (nTimeOut > 0)
    {
        FD_ZERO(&fdset);
        FD_SET(nSk, &fdset);
        tm.tv_sec = nTimeOut;
        if (select(nSk + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0)
        {
            DEBUG(SYSTEM_ERROR, "func->select error(%d:%s).", errno, strerror(errno));
            if (errno != EINTR)
            {
                nRtVal = 0;
            }
        }
        else if (FD_ISSET(nSk, &fdset))
        {
            if (recv(nSk, pstArp, sizeof(struct arpMsg), 0) < 0 )
            {
                nRtVal = 0;
            }
            if (pstArp->operation == htons(ARPOP_REPLY) && 
                bcmp(pstArp->tHaddr, pucLocalMac, 6) == 0 && 
                *((u_int *) pstArp->sInaddr) == unRemoteIp)
            {
                //DEBUG(PROCESS_PRINT, "valid arp reply receved for this address.");
                nRtVal = 0;
                break;
            }
        }
        nTimeOut -= time(NULL) - prevTime;
        time(&prevTime);
    }
    close(nSk);
    DEBUG(PROCESS_PRINT, "%salid arp replies for this address", nRtVal ? "no v" : "v");  
    return nRtVal;
}           

/******************************************************************************
 * nArpTblCtl - get/set/del ip<->mac info 
 * DESCRIPTION: -  
 * Input: 
 * Output: 
 * Returns: 
 * 
 * modification history
 * --------------------
 * 2.00, 2014-12-25 , zengjianrong written
 * --------------------
 ******************************************************************************/
int nArpTblCtl(ARP_TBL_TRL_ENUM cmd, struct in_addr stRemoteIp, char *pcDevName, unsigned char *pucMac)
{
    unsigned int        unCmd = 0;
    unsigned char       *pucHwAddr = NULL;
    struct sockaddr_in  *pSin = NULL;
    struct arpreq       stArpreq;
    
    if ((0 == stRemoteIp.s_addr)
        || (cmd <= ARP_TBL_BEGIN)
        || (cmd >= ARP_TBL_END)
        || (ARP_TBL_DEL != cmd && NULL == pucMac))
    {
        DEBUG(SYSTEM_ERROR, "param error.");
        return -1;
    }
    
    memset(&stArpreq, 0, sizeof(struct arpreq));
    switch (cmd)
    {
        case ARP_TBL_GET:
            unCmd = SIOCGARP;
            break;
        case ARP_TBL_DEL:
            stArpreq.arp_ha.sa_family = AF_UNSPEC;
            unCmd = SIOCDARP;
            break;
        case ARP_TBL_SET:
            stArpreq.arp_ha.sa_family = AF_UNSPEC;
            //stArpreq.arp_flags = ATF_PERM | ATF_PUBL | ATF_FORCE_ADD;
            stArpreq.arp_flags = ATF_COM;
            memcpy((char *)stArpreq.arp_ha.sa_data, pucMac, 6);
            unCmd = SIOCSARP;
            break;
        default:
            return -1;
    }

    if (-1 == fd_arp_tbl)
    {
        fd_arp_tbl = socket(AF_INET, SOCK_DGRAM, 0);
        if (fd_arp_tbl < 0)
        {
            DEBUG(SYSTEM_ERROR, "func->socket error(%d:%s).", errno, strerror(errno));
            return -1;
        }
    }
    
    pSin = (struct sockaddr_in *) &stArpreq.arp_pa;
    pSin->sin_family = AF_INET;
    memcpy(&(pSin->sin_addr), &stRemoteIp, sizeof(struct in_addr));
    strcpy(stArpreq.arp_dev, pcDevName);

    if (0 > ioctl(fd_arp_tbl, unCmd, &stArpreq))
    {
        DEBUG(SYSTEM_ERROR, "func->ioctl error(%d:%s).", errno, strerror(errno));
        close(fd_arp_tbl);
        fd_arp_tbl = -1;
        return -1;
    }
    else if (ARP_TBL_GET == cmd)
    {        
        pucHwAddr = (unsigned char *) stArpreq.arp_ha.sa_data;
        memcpy(pucMac, pucHwAddr, 6);
        if (0 == pucMac[0] && 0 == pucMac[1] && 0 == pucMac[2] 
            && 0== pucMac[3] && 0 == pucMac[4] && 0 == pucMac[5])
        {
            return -1;
        }
    }
    
    return 0;
}


int main(int argc, char *argv[])
{
    unsigned char       aucMac[6];
    struct in_addr      sin_remote_addr;
    struct in_addr      sin_local_addr;
    struct arpMsg       stArpMsg;

    memset(&sin_local_addr, 0, sizeof(struct in_addr));
    memset(&stArpMsg, 0, sizeof(struct arpMsg));
    memset(aucMac, 0, 6);

    if (argc!=2) 
    {
        DEBUG(SYSTEM_ERROR, "usage: %s <IP address>
",argv[0]);
        return -1;
    }

    /* initial global values */
    fd_arp_tbl = -1;

    if (0 == (inet_aton("138.0.225.226", &sin_local_addr))) 
    {
        DEBUG(SYSTEM_ERROR, "func->inet_aton error(%d:%s).", errno, strerror(errno));
        return -1;
    }
    
    if (0 == (inet_aton(argv[1], &sin_remote_addr))) 
    {
        DEBUG(SYSTEM_ERROR, "func->inet_aton error(%d:%s), '%s' not valid.", 
            errno, strerror(errno), argv[1]);
        return -1;
    }

    if (0 > (nArpTblCtl(ARP_TBL_GET, sin_remote_addr, LOCAL_DEV, aucMac)))
    {
        DEBUG(PROCESS_PRINT, "no entry in arp_cache for '%s', send arp request.", argv[1]);
        if (0 != (nSendArp(sin_remote_addr.s_addr, sin_local_addr.s_addr, 
                          LOCAL_MAC_ADDR, LOCAL_DEV, &stArpMsg)))
        {   
            DEBUG(SYSTEM_ERROR, "there is no device using '%s', or nSendArp error.", argv[1]);
            return -1;
        }
        else
        {
            if (0 > (nArpTblCtl(ARP_TBL_SET, sin_remote_addr, LOCAL_DEV, stArpMsg.sHaddr)))
            {
                DEBUG(SYSTEM_ERROR, "func->nArpTblCtl set error.");
                return -1;
            }
            if (0 > (nArpTblCtl(ARP_TBL_GET, sin_remote_addr, LOCAL_DEV, aucMac)))
            {
                DEBUG(PROCESS_PRINT, "there is no device using '%s'.", argv[1]);
                return 0;
            }
        }
    }
    DEBUG(PROCESS_PRINT, "%s-->%02x:%02x:%02x:%02x:%02x:%02x.", argv[1], 
        aucMac[0], aucMac[1], aucMac[2], aucMac[3], aucMac[4], aucMac[5]);  
    
    return 0;
}

  测试结果如下:

[root@zeng test]# ifconfig 
eth0      Link encap:Ethernet  HWaddr 00:50:56:CC:99:0E  
          inet addr:138.0.225.226  Bcast:138.0.255.255  Mask:255.255.0.0
          inet6 addr: fe80::250:56ff:fecc:990e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2314211 errors:0 dropped:0 overruns:0 frame:0
          TX packets:13648 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:161772378 (154.2 MiB)  TX bytes:18844506 (17.9 MiB)
          Interrupt:67 Base address:0x2024 

eth1      Link encap:Ethernet  HWaddr 00:50:56:DE:99:0E  
          inet addr:192.168.123.225  Bcast:192.168.123.255  Mask:255.255.255.0
          inet6 addr: fe80::250:56ff:fede:990e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:32187 errors:0 dropped:0 overruns:0 frame:0
          TX packets:21581 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:2529373 (2.4 MiB)  TX bytes:1040688 (1016.2 KiB)
          Interrupt:75 Base address:0x20a4 

eth2      Link encap:Ethernet  HWaddr 00:50:56:DC:99:0E  
          inet addr:192.168.213.225  Bcast:192.168.213.255  Mask:255.255.255.0
          inet6 addr: fe80::250:56ff:fedc:990e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1658819 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1976333 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:340738638 (324.9 MiB)  TX bytes:1445346752 (1.3 GiB)
          Interrupt:75 Base address:0x2424 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:840 errors:0 dropped:0 overruns:0 frame:0
          TX packets:840 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:56072 (54.7 KiB)  TX bytes:56072 (54.7 KiB)

[root@zeng test]# ./getMacFromIp.o 138.0.0.90
[DEBUG] in /mnt/hgfs/source_code_20140312/test/2getMacFromIp.c:278 nArpTblCtl(), func->ioctl error(6:No such device or address).
[DEBUG] in /mnt/hgfs/source_code_20140312/test/2getMacFromIp.c:333 main(), no entry in arp_cache for '138.0.0.90', send arp request.
[DEBUG] in /mnt/hgfs/source_code_20140312/test/2getMacFromIp.c:220 nSendArp(), valid arp replies for this address
[DEBUG] in /mnt/hgfs/source_code_20140312/test/2getMacFromIp.c:355 main(), 138.0.0.90-->74:d4:35:49:5b:7c.
[root@zeng test]# ./getMacFromIp.o 138.0.156.125
[DEBUG] in /mnt/hgfs/source_code_20140312/test/2getMacFromIp.c:278 nArpTblCtl(), func->ioctl error(6:No such device or address).
[DEBUG] in /mnt/hgfs/source_code_20140312/test/2getMacFromIp.c:333 main(), no entry in arp_cache for '138.0.156.125', send arp request.
[DEBUG] in /mnt/hgfs/source_code_20140312/test/2getMacFromIp.c:220 nSendArp(), no valid arp replies for this address
[DEBUG] in /mnt/hgfs/source_code_20140312/test/2getMacFromIp.c:337 main(), there is no device using '138.0.156.125', or nSendArp error.
[root@zeng test]# 
原文地址:https://www.cnblogs.com/zengjianrong/p/4185424.html