Linux下IP地址衝突檢測

檢測原理

發送ARP包判斷網絡內是否有相應的應答。有對應的應答則ip地址已經被其他設備使用,超時無應答則無衝突。
根據簡書陳兄抓的包的情況看,詢問ip是否被佔用時需要發送一個源IP爲0.0.0.0,目的IP爲查詢ip的arp包。具體代碼如下

檢測代碼

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <arpa/inet.h>

#define MAC_BCAST_ADDR      (unsigned char *) "/xff/xff/xff/xff/xff/xff"
#define ETH_INTERFACE       "eth0"

#define ARP_MSG_SIZE 0x2a

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

/*獲取eth0的ip地址和mac地址*/
int geteth0(u_int32_t *ip, unsigned char *mac)
{
    int fd;
    struct ifreq ifr;
    struct sockaddr_in *addr;

    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
    {
        strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);
        ifr.ifr_name[IFNAMSIZ - 1] = '\0';
        if (ioctl(fd, SIOCGIFADDR, &ifr) == 0)
        {
            addr = (struct sockaddr_in *) & (ifr.ifr_addr);
            *ip = addr->sin_addr.s_addr;
        }
        else
        {
            close(fd);
            return -1;
        }
        if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0)
        {
            memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
        }
        else
        {
            return -1;
        }
    }
    else
    {
        perror("getIpAddr error :");
        return -1;
    }
    close(fd);
    return 0;
}

/*參數說明 目標IP地址,本機IP地址,本機mac地址,網卡類型*/
int arpping(u_int32_t desIp, u_int32_t srcIp, unsigned char *mac, char *interface)
{
    int optval = 1;
    int fd;                      /* socket */
    int ret = 1;                 /* return value */
    struct sockaddr addr;       /* for interface name */
    struct arpMsg arp;
    fd_set fdset;
    struct timeval tm;
    int len = 0;
    /*socket發送一個arp包*/
    if ((fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1)
    {
        printf("Could not open raw socket\n");
        return -1;
    }

    /*設置套接口類型爲廣播,把這個arp包是廣播到這個局域網*/
    if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1)
    {
        printf("Could not setsocketopt on raw socket\n");
        close(fd);
        return -1;
    }

    memset(&arp, 0, sizeof(arp));
    memcpy(arp.ethhdr.h_dest, MAC_BCAST_ADDR, 6);   /* MAC DA */
    memcpy(arp.ethhdr.h_source, mac, 6);        /* MAC SA */
    arp.ethhdr.h_proto = htons(ETH_P_ARP);      /* protocol type (Ethernet) */
    arp.htype = htons(ARPHRD_ETHER);        /* hardware type */
    arp.ptype = htons(ETH_P_IP);            /* protocol type (ARP message) */
    arp.hlen = 6;                   /* hardware address length */
    arp.plen = 4;                   /* protocol address length */
    arp.operation = htons(ARPOP_REQUEST);       /* ARP op code */
    *((u_int *) arp.sInaddr) = srcIp;          /* source IP address */
    memcpy(arp.sHaddr, mac, 6);         /* source hardware address */
    *((u_int *) arp.tInaddr) = desIp;      /* target IP address */

    memset(&addr, 0, sizeof(addr));
    strcpy(addr.sa_data, interface);
    /*發送arp請求*/
    if (sendto(fd, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0)
        ret = -2;

    tm.tv_sec = 2;
    tm.tv_usec = 0;
    FD_ZERO(&fdset);
    FD_SET(fd, &fdset);
    while (1)
    {
        if (select(fd + 1, &fdset, NULL, NULL, &tm) <= 0)
        {
            //printf("timeout\n");		//超時算沒有衝突
            break;
        }
        else if (FD_ISSET(fd, &fdset))
        {
            if ((len = recv(fd, &arp, sizeof(arp), 0)) < 0)
            {
                ret = 0;
            }

            // if(len>=ARP_MSG_SIZE)		//這裏會接收到其他arp包
            // {
            //	 u_int32_t tmp = *((u_int *) arp.sInaddr);
            //	 printf("ip=%d.%d.%d.%d\n",tmp&0xff,(tmp>>8)&0xff,(tmp>>16)&0xff,tmp>>24);
            // }

            /*如果條件 htons(ARPOP_REPLY) bcmp(arp.tHaddr, mac, 6) == 0 *((u_int *) arp.sInaddr) == desIp
              三者都爲真,則ARP應答有效,說明這個地址是已近存在的*/
            if (arp.operation == htons(ARPOP_REPLY) &&
                    bcmp(arp.tHaddr, mac, 6) == 0 &&
                    *((u_int *) arp.sInaddr) == desIp)
            {
                printf("Valid arp reply receved for this mac address=%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]);
                ret = 0;
                break;
            }
        }
    }
    close(fd);
    return ret;
}

int test_check_myself_ip()
{
    int ret = -1;
    u_int32_t ip = 0;
    unsigned char mac[6] = {0};
    if (geteth0(&ip, mac))
    {
        printf("get eth0 error\n");
        return -1;
    }
    ret = arpping(ip, 0, mac, "eth0");  //將ip替換爲需要檢測的ip,這裏檢測本機。
    if (ret == 0)
    {
        printf("conflict-->\n");
    }
    else if (ret == 1)
    {
        printf("no conflict-->\n");
    }
    else
    {
        printf("arping error\n");
    }
    return 0;
}

int main(int argc, char *argv[])
{
    test_check_myself_ip();
    return 0;
}

參考網站

1- 圖解ARP協議(五)免費ARP:地址衝突了腫麼辦?
2- linux下檢測ip衝突
3- Linux下IP衝突檢測程序源碼及分析(利用免費arp)—感謝原作者

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章