Linux ARP探测报文

项目场景:

最近需要解决一个在局域网中获取对方IP的问题,所以我想到一个ARP探测的方法。在网上找了一个代码,但是需要改进。这边分享一下改进后的代码


问题描述

需要在局域网中获取对方的IP,MAC地址


解决方案:

代码展示

#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#include <netinet/if_ether.h>
#include <netinet/in.h>

#include <arpa/inet.h>
#include <net/if.h>

/* 6个FF代表广播 */

#define MACLEN 6
#define IPLEN 4
#define DESIPLEN 20

unsigned char broad_mac[MACLEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
unsigned char local_mac[MACLEN] = {0}; // 存储本机硬件地址
unsigned char local_ip[IPLEN] = {0};   // 存储本机IP地址
unsigned char des_ip[DESIPLEN] = {0};  // 存储目的IP地址

/* 根据 RFC 0826 修改*/
typedef struct _Ether_pkg Ether_pkg;
struct _Ether_pkg
{
    /* 前面是ethernet头 */
    unsigned char ether_dhost[MACLEN];  /* 目地硬件地址 */
    unsigned char ether_shost[MACLEN];  /* 源硬件地址 */
    unsigned short int ether_type; /* 网络类型 */

    /* 下面是arp协议 */
    unsigned short int ar_hrd; /* 硬件地址格式 */
    unsigned short int ar_pro; /* 协议地址格式 */
    unsigned char ar_hln;      /* 硬件地址长度(字节) */
    unsigned char ar_pln;      /* 协议地址长度(字节) */
    unsigned short int ar_op;  /* 操作代码 */
    unsigned char arp_sha[MACLEN];  /* 源硬件地址 */
    unsigned char arp_spa[IPLEN];  /* 源协议地址 */
    unsigned char arp_tha[MACLEN];  /* 目地硬件地址 */
    unsigned char arp_tpa[IPLEN];  /* 目地协议地址 */
};

/* 得到本机的mac地址和ip地址 */
int GetLocalMac(const char *device, unsigned char *mac, unsigned char *ip)
{
    int sockfd;
    struct ifreq req;

    /* 创建套接字 SOCK_DGRAM:UDP报文常用 */
    if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
    {
        fprintf(stderr, "Sock Error:%s\n\a", strerror(errno));
        return -1;
    }

    memset(&req, 0, sizeof(req));
    strcpy(req.ifr_name, device);

    /* 获取硬件地址类型 */
    if (ioctl(sockfd, SIOCGIFHWADDR, (char *)&req) == -1)
    {
        fprintf(stderr, "Get hardware mac error:%s\n\a", strerror(errno));
        close(sockfd);
        return -1;
    }

    memcpy(mac, req.ifr_hwaddr.sa_data, MACLEN);

    req.ifr_addr.sa_family = PF_INET;

    if (ioctl(sockfd, SIOCGIFADDR, (char *)&req) == -1)
    {
        fprintf(stderr, "ioctl SIOCGIFADDR:%s\n\a", strerror(errno));
        close(sockfd);
        return -1;
    }
    // 获取ip 并存储
    memcpy(ip, &((struct sockaddr_in *)&req.ifr_addr)->sin_addr, IPLEN);

    printf("ip= %s  mac %s \n", inet_ntoa(*(struct in_addr *)ip), ether_ntoa(mac));
    return 0;
}


int fill_pkg(Ether_pkg *pkg)
{
    unsigned char desip[IPLEN] = {0};

    memset(pkg, 0, sizeof(pkg));
    /*
        目的MAC地址 | 源MAC地址 | 帧类型 | 硬件类型 | 协议类型 |硬件地址长度 |协议长度 | 操作类型 |发送方硬件地址|发送方IP地址|目标硬件地址|目标IP地址|补充报文

    */
    /* 填充以太网头部报文 */
    memcpy(pkg->ether_dhost, broad_mac, MACLEN); // 目的MAC地址
    memcpy(pkg->ether_shost, local_mac, MACLEN); // 填充本机mac地址

    pkg->ether_type = htons(ETHERTYPE_ARP); // 帧类型 arp

    /* 填充arp报文 */
    pkg->ar_hrd = htons(ARPHRD_ETHER); // 硬件类型
    pkg->ar_pro = htons(ETHERTYPE_IP); // 协议类型
    pkg->ar_hln = 6;                   // 硬件协议长度
    pkg->ar_pln = 4;                   // 协议长度
    pkg->ar_op = htons(ARPOP_REQUEST);   // 操作类型 请求

    memcpy(pkg->arp_sha, local_mac, MACLEN); // 填充源mac地址
    memcpy(pkg->arp_spa, local_ip, IPLEN);       // 填充源IP地址
    memcpy(pkg->arp_tha, broad_mac, MACLEN); // 填充目的硬件地址

    /* 填入目的IP地址 */
    if (inet_aton(des_ip, (struct in_addr *)desip) == 0)
    {
        return -1;
    }

    memcpy(pkg->arp_tpa, desip, IPLEN);
    return 0;
}

void get_desip(unsigned char *dstip, unsigned char *srcip, unsigned char num)
{
    /* 假设取出IP 192.168.144 */
    char *des = inet_ntoa(*(struct in_addr *)srcip);
    /* 取出字符串 .144 */
    char *tmp = strrchr(des, '.');

    /* 这边指针相减 取出 192.168.144 */
    char newdes[20] = {0};
    memcpy(newdes, des, tmp - des);

    sprintf(dstip, "%s.%d", newdes, num);
}

int sendpkg(Ether_pkg *pkg, char *net)
{

    struct hostent *host = NULL;
    struct sockaddr sa;
    int sockfd, len;

    printf("Resolve [%s],Please Waiting...\n", des_ip);

    fflush(stdout);

    /* 实际应该使用 PF_PACKET */
    if ((sockfd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ARP))) == -1)
    {
        fprintf(stderr, "Socket Error:%s\n\a", strerror(errno));
        return (0);
    }

    memset(&sa, '\0', sizeof(sa));
    strcpy(sa.sa_data, net); // 指定网络的出口

    len = sendto(sockfd, pkg, sizeof(*pkg), 0, &sa, sizeof(sa));
    if (len != sizeof(*pkg))
    {
        fprintf(stderr, "Sendto Error:%s\n\a", strerror(errno));
        return (0);
    }
    return sockfd;
}

int get_arpreply(int sockfd)
{
    int len = 0;
    char buffer[255];
    Ether_pkg *parse = (Ether_pkg *)buffer;
    fd_set readfds;
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 500000; /*500毫秒 超时时间*/
    FD_ZERO(&readfds);
    FD_SET(sockfd, &readfds);

    /* 开始轮询 */
    len = select(sockfd + 1, &readfds, 0, 0, &tv);
    if (len > -1)
    {
        /* 查看套接字是否可读 */
        if (FD_ISSET(sockfd, &readfds))
        {
            memset(buffer, 0, sizeof(buffer));
            len = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, &len);
            if ((ntohs(parse->ether_type) == ETHERTYPE_ARP) &&
                (ntohs(parse->ar_op) == ARPOP_REPLY))
            {
                printf("ARP REPLY IP=[%s] MAC=[%s]\n", inet_ntoa(*(struct in_addr *)parse->arp_spa), ether_ntoa(parse->arp_sha));
            }
        }
    }

    return 1;
}

/*  修改增加参数 网口设置 */
int main(int argc, char **argv)
{
    Ether_pkg pkg;
    int sockfd;
    unsigned char start, end = 0;
    if (argc < 2)
    {
        printf("please input right param\n");
        printf("Example: ./xxx eth0 1 50\n"); //查看 1-50 地址的IP
        return -1;
    }

    start = atoi(argv[2]);
    end = atoi(argv[3]);

    /* 获取要使用arp探测的网口名称 */
    if (GetLocalMac(argv[1], local_mac, local_ip) == -1)
        return (-1);

    /* 填充我们所需的报文 */
    for (start; start <= end; start++)
    {
        /* 设置对方的ARP地址 */
        get_desip(des_ip, local_ip, start);
        /* 填充ARP报文 */
        fill_pkg(&pkg);
        /* 发送ARP报文 */
        sockfd = sendpkg(&pkg, argv[1]);
        if(sockfd <=0)
        {
            return -1;
        }
        /* 获取报文 */
        get_arpreply(sockfd);
    }

    return 0;
}

使用方法

./send_arp eth0 1 50

参数含义
eth0发送arp的网口
1(xxx.xxx.xxx.1)
50(xxx.xxx.xxx.50)

在这里插入图片描述

在这里插入图片描述

总结

这边是抄的网上的一个代码改编的,但是我忘记链接地址了。在这边感谢那个老哥。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值