博客地址: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]#