Bootstrap

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)—感谢原作者

;