arp協議及簡單應用

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字節 */
};
相關文章
相關標籤/搜索