arp协议及简单应用

1:什么是arp协议

  参考文章:http://blog.csdn.net/tigerjibo/article/details/7351992

  全称是:Address Resolution Protocol 地址解析协议,也就是用IP地址获取MAC地址的工具。

2:什么时候使用arp

  arp是处于网络层的协议,但却是为链路层服务的,比如创建一个TCP的套接字,链路层、网络层、传输层的数据报文都是内核替你封装的(当然你要传递必要的参数),在内核封装链路层的报文时,必须得知道目的地的MAC地址(因为网卡收到的数据先经过链路层,如果发现报文的目的MAC不是自己一般会直接丢弃此报文,更没有可能进入协议栈了.有一个特殊情况是当网卡设置为混杂模式时,所有流经它的报文它都拿进协议栈处理),首先内核会在操作系统维护的arp缓存表中找目的IP是否对应了一个MAC,如果有就把这个MAC拿来使用,否则这时候arp就登场了.

  arp首先广播此IP地址(没有目的MAC),局域网中的主机就开始检查自己的IP,如果是这个IP就会单播回复,我是这个IP的主人,并且我的MAC地址是多少,收到此回复后操作系统就更新或者添加IP-MAC地址对到arp缓存表.这是在同一个局域网的情况下,如果目的地址不在同一局域网呢?情况就是根据路由表的下一跳地址,去解析这个地址的MAC,到达之后继续解析下一跳,直到到达目的地为止.

3:简单应用

  这是一个简单的在局域网中利用一个IP地址获取MAC地址的程序,根据arp协议,首先要使用arp广播这个IP地址,根据回复的单播报文获取到此IP的MAC,思路很简单,下面是源代码:

1:read_interface 获得源IP,源MAC,这两个参数在发送arp报文时需要用到

/*
    用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报文发送并接收结果

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字节 */
};

 

posted @ 2017-04-19 08:59  会飞的小丑  阅读(1212)  评论(0编辑  收藏  举报