1:什麼是arp協議緩存
參考文章:http://blog.csdn.net/tigerjibo/article/details/7351992網絡
全稱是:Address Resolution Protocol 地址解析協議,也就是用IP地址獲取MAC地址的工具。socket
2:何時使用arp函數
arp是處於網絡層的協議,但倒是爲鏈路層服務的,好比建立一個TCP的套接字,鏈路層、網絡層、傳輸層的數據報文都是內核替你封裝的(固然你要傳遞必要的參數),在內核封裝鏈路層的報文時,必須得知道目的地的MAC地址(由於網卡收到的數據先通過鏈路層,若是發現報文的目的MAC不是本身通常會直接丟棄此報文,更沒有可能進入協議棧了.有一個特殊狀況是當網卡設置爲混雜模式時,全部流經它的報文它都拿進協議棧處理),首先內核會在操做系統維護的arp緩存表中找目的IP是否對應了一個MAC,若是有就把這個MAC拿來使用,不然這時候arp就登場了.工具
arp首先廣播此IP地址(沒有目的MAC),局域網中的主機就開始檢查本身的IP,若是是這個IP就會單播回覆,我是這個IP的主人,而且個人MAC地址是多少,收到此回覆後操做系統就更新或者添加IP-MAC地址對到arp緩存表.這是在同一個局域網的狀況下,若是目的地址不在同一局域網呢?狀況就是根據路由表的下一跳地址,去解析這個地址的MAC,到達以後繼續解析下一跳,直到到達目的地爲止.ui
3:簡單應用spa
這是一個簡單的在局域網中利用一個IP地址獲取MAC地址的程序,根據arp協議,首先要使用arp廣播這個IP地址,根據回覆的單播報文獲取到此IP的MAC,思路很簡單,下面是源代碼:操作系統
1:read_interface 得到源IP,源MAC,這兩個參數在發送arp報文時須要用到.net
/* 用socket得到當前interface的信息 1:ip地址 2:mac地址 3:interface index 注:建立的是原始套接字,編譯及執行都須要root權限 */ int read_interface(const char* interface, uint32_t *sour_ip, uint8_t *sour_mac) { int fd; int ret = 0; struct ifreq ifr; struct sockaddr_in *ip_addr = NULL; memset(&ifr, 0, sizeof(struct ifreq)); fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (fd > 0){ ifr.ifr_addr.sa_family = AF_INET; strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name)); //get interface ip if (sour_ip) { if (ioctl(fd, SIOCGIFADDR, &ifr) == 0){ ip_addr = (struct sockaddr_in *) &ifr.ifr_addr; printf("The sour_ip addr is:%s\n", inet_ntoa(ip_addr->sin_addr)); *sour_ip = ip_addr->sin_addr.s_addr; } else { perror("SIOCGIFADDR failed"); ret = -1; goto out; } } //get interface mac if (sour_mac) { if(ioctl(fd, SIOCGIFHWADDR, &ifr) == 0){ memcpy(sour_mac, ifr.ifr_hwaddr.sa_data, 6); printf("interface sour_mac: %02X:%02X:%02X:%02X:%02X:%02X\n", sour_mac[0],sour_mac[1],sour_mac[2],sour_mac[3],sour_mac[4],sour_mac[5]); } else { perror("SIOCGIFHWADDR failed"); ret = -1; goto out; } } } else { perror("read_interface socket failed"); ret = -1; } out: close(fd); return ret; }
2:arpping 封裝arp報文發送並接收結果code
int arpping(const uint8_t* sour_mac, const uint32_t sour_ip, const uint32_t dest_ip, const uint8_t* interface) { int s; /* raw socket */ int send_times = 3; int optval = 1; struct arpMsg arp; struct sockaddr addr; /* create a raw socket */ if ((s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1) { perror("arpping open raw socket failed"); return -1; } /* 容許發送廣播包,若是沒有設置此option,即便目的MAC是FF:FF:FF:FF:FF:FF此報文也不會被操做系統發送出去! */ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) { perror("setsockoptetopt on raw socket failed"); close(s); return -1; } /* 封裝 Ethernet 報文 */ memset(&arp, 0, sizeof(arp)); memcpy(arp.ethhdr.h_dest, MAC_BROADCAST_ADDR, 6); memcpy(arp.ethhdr.h_source, sour_mac, 6); /* 封裝 arp 報文 */ arp.htype = htons(ARPHRD_ETHER); arp.ptype = htons(ETH_P_IP); arp.hlen = 6; arp.plen = 4; arp.operation = htons(ARP_REQUEST); memcpy(arp.sHaddr, sour_mac, 6); memcpy(arp.sInaddr, &sour_ip, sizeof(sour_ip)); memcpy(arp.tInaddr, &dest_ip, sizeof(dest_ip)); /* 發送arp request 廣播包*/ memset(&addr, 0, sizeof(addr)); strncpy(addr.sa_data, interface, sizeof(addr.sa_data)); if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0){ perror("arpping sendto error"); return -1; } /* 獲取返回數據(阻塞的) */ if (recv(s, &arp, sizeof(arp), 0) < 0) { printf("no response\n"); return -1; } printf("dest_mac: %02X:%02X:%02X:%02X:%02X:%02X\n",\ arp.sHaddr[0],arp.sHaddr[1],arp.sHaddr[2],\ arp.sHaddr[3],arp.sHaddr[4],arp.sHaddr[5]); return 0; }
3:main 函數
int main(int argc,char** argv) { int ret; if(argc < 2){ printf("need a dest ip\n"); return -1; } uint32_t dest_ip = inet_addr(argv[1]); char *interface = "eth0"; uint32_t sour_ip; uint8_t sour_mac[6] = {0}; /* 獲取本地IP,MAC信息 */ read_interface(interface, &sour_ip, sour_mac); /* 調用arpping廣播arp報文獲取MAC地址 */ arpping(sour_mac, sour_ip, dest_ip, interface); return 0; }
4:頭文件及結構體等
#include <unistd.h> #include <sys/types.h> #include <sys/time.h> #include <netinet/ip.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <netinet/if_ether.h> #include <net/if_arp.h> #include <net/if.h> #include <netdb.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <arpa/inet.h> #define MAC_BROADCAST_ADDR (uint8_t *)"/xff/xff/xff/xff/xff/xff" #define ARP_REQUEST 1 #define ARP_REPLY 2 struct arpMsg { struct ethhdr ethhdr; /* 鏈路層頭部 */ u_short htype; /* 硬件類型 (ARPHRD_ETHER=1 表示以太網) */ u_short ptype; /* 協議類型 (ETH_P_IP=0x0800 表示IP協議類型) */ u_char hlen; /* 硬件地址長度 (6) */ u_char plen; /* 協議地址長度 (4) */ u_short operation; /* ARP 操做類型 (1:arp請求 2:arp應答)*/ u_char sHaddr[6]; /* 源MAC地址 */ u_char sInaddr[4]; /* 源IP地址 */ u_char tHaddr[6]; /* 目的MAC地址 */ u_char tInaddr[4]; /* 目的IP地址 */ u_char pad[18]; /* 填充字節,由於愛以太網中傳輸的幀最小是64字節 */ };