arping IP衝突問題

#ifndef __RANDOM_IP_H__
#define __RANDOM_IP_H__
 
#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif
 
#include <sys/time.h>
 
typedef struct
{
    char ip[16];
    char gw[16];
    char mask[16];
    char dns[2][16]; // 保留,暫時沒用到
}NetCfg;
 
int abstime_get(struct timeval* timeval);
int get_local_ip(const char* if_name, char* local_ip);
int set_local_ip(const char* if_name, char* new_ip);
int get_random_net(const char *ref_ip, NetCfg* Net);
 
 
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
 
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/if_ether.h>
#include <net/if_arp.h>
#include <time.h>
#include <unistd.h>
 
#include "random_ip.h"
 
#define IF_NAME "eth0"
 
//ARP消息包結構
typedef struct tagArpMsg
{
    struct ethhdr ethhdr;              /* Ethernet header */
    unsigned short htype;              /* hardware type (must be ARPHRD_ETHER) */
    unsigned short ptype;              /* protocol type (must be ETH_P_IP) */
    unsigned char  hlen;               /* hardware address length (must be 6) */
    unsigned char  plen;               /* protocol address length (must be 4) */
    unsigned short operation;          /* ARP opcode */
    unsigned char  sHaddr[6];          /* sender's hardware address */
    unsigned char  sInaddr[4];         /* sender's IP address */
    unsigned char  tHaddr[6];          /* target's hardware address */
    unsigned char  tInaddr[4];         /* target's IP address */
    unsigned char  pad[18];            /* pad for min. Ethernet payload (60 bytes) */
}ArpMsg;
 
typedef struct
{
    char local_ip[16];
    char local_mac[6];
    int timeout; // ms
    NetCfg result;
}RunArg;
 
 
int abstime_get(struct timeval* timeval)
{
    struct timespec tp;
    if(clock_gettime(CLOCK_MONOTONIC, &tp) < 0)
    {
        printf("clock get time error: %s\n", strerror(errno));
        return -1;
    }
 
    timeval->tv_sec = tp.tv_sec;
    timeval->tv_usec = tp.tv_nsec / 1000;
 
    return 0;
}
 
static char *pr_ether(unsigned char *ptr)
{
    static char buff[64];
 
    snprintf(buff, sizeof(buff), "%02x:%02x:%02x:%02x:%02x:%02x",
        (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
        (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
        );
 
    return (buff);
}
 
static int get_mac(const char* if_name, char* mac)
{
    int ret;
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        printf("socket failed by %s\n", strerror(errno));
        return -1;
    }
 
    struct ifreq req;
    memset(&req, 0, sizeof(struct ifreq));
    strcpy(req.ifr_name, if_name);
 
    ret = ioctl(fd, SIOCGIFHWADDR, &req);
    if (ret < 0)
    {
        printf("ioctl SIOCGIFHWADDR %s failed by %s\n", if_name, strerror(errno));
        return -1;
    }
 
    close(fd);
 
    struct sockaddr sa;
    memcpy(&sa, &req.ifr_hwaddr, sizeof(struct sockaddr));
    strcpy(mac, pr_ether((unsigned char*)sa.sa_data));
 
    return 0;
}
 
static int arpping(unsigned int destIp, unsigned int sourceIp, const char *mac, int timeOutMs)
{
    int ret = 0;               /* return value */
    int optval = 1;
    int sockFd = -1;            /* socket */
    struct sockaddr addr;       /* for interface name */
    unsigned int iSenderIp;
    ArpMsg arp;
    fd_set fdset;
 
    struct timeval prevTime;
 
    /*socket發送一個arp包*/
    if ((sockFd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1)
    {
        printf("Could not open raw socket.\n");
        return -1;
    }
 
    /*設置套接口類型爲廣播,把這個arp包是廣播到這個局域網*/
    if (setsockopt(sockFd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1)
    {
        printf("Could not setsocketopt on raw socket.\n");
        close(sockFd);
        return -1;
    }
 
    /* 對arp設置,這裏按照arp包的封裝格式賦值即可,詳見http://blog.csdn.net/wanxiao009/archive/2010/05/21/5613581.aspx */
    memset(&arp, 0, sizeof(arp));
    memset(arp.ethhdr.h_dest, 0xff, 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 */
    if (destIp == sourceIp)
    {
        if (destIp != htonl(0xa9fe010a))
        {
            iSenderIp = htonl(0xa9fe010a);         /* Use Reserve IP for Sender */
        }
        else
        {
            iSenderIp = htonl(0xa9fe010b);         /* Use Reserve IP for Sender */
        }
    }
    else
    {
        iSenderIp = sourceIp;
    }
    *((u_int *) arp.sInaddr) = iSenderIp;    /* Sender IP address */
    memcpy(arp.sHaddr, mac, 6);              /* Sender hardware address */
    memset(arp.tHaddr, 0xff, 6);             /* target hardware address */
    *((u_int *) arp.tInaddr) = destIp;       /* target IP address */
 
    memset(&addr, 0, sizeof(addr));
    strcpy(addr.sa_data, IF_NAME);
    /*發送arp請求*/
    if (sendto(sockFd, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0)
    {
        printf("arpping sendto failed.\n");
        close(sockFd);
        return -1;
    }
 
    /* 利用select函數進行多路等待*/
    abstime_get(&prevTime);
    while (1)
    {
        FD_ZERO(&fdset);
        FD_SET(sockFd, &fdset);
        struct timeval timeout = {0, 1000};
        ret = select(sockFd + 1, &fdset, NULL, NULL, &timeout);
        if (ret < 0)
        {
            if (errno != EINTR)
            {
                ret = -1;
                break;
            }
        }
        else if (ret == 0)
        {
            struct timeval curTime;
            abstime_get(&curTime);
            int nSpendTime = (curTime.tv_sec - prevTime.tv_sec) * 1000 + (curTime.tv_usec - prevTime.tv_usec) / 1000;
            if (nSpendTime >= timeOutMs)
            {
                printf("timeout: %d ms\n", nSpendTime);
                break;
            }
        }
        else if (FD_ISSET(sockFd, &fdset))
        {
            if (recv(sockFd, &arp, sizeof(arp), 0) < 0)
            {
                ret = 0;
            }
 
            if (arp.operation == htons(ARPOP_REPLY))
            {
                /*ARP應答有效,說明這個地址是已經存在的*/
                if ((memcmp(arp.sHaddr, mac, 6) != 0)
                    && (memcmp(arp.tHaddr, mac, 6) == 0)
                    && (*((u_int *) arp.sInaddr) == destIp))
                {
                    printf("Valid arp reply receved for this address.\n");
                    ret = 1;
                    break;
                }
            }
        }
    }
 
    close(sockFd);
 
    return ret;
}
 
static int is_ip_conflict(RunArg* run_arg, const char *pCheckIp)
{
    unsigned int destIp = inet_addr(pCheckIp);
    unsigned int sourceIp = inet_addr(run_arg->local_ip);
 
    int ret = arpping(destIp, sourceIp, run_arg->local_mac, run_arg->timeout);
 
    return ret;
}
 
static int init(RunArg* run_arg)
{
    int ret = -1;
    run_arg->timeout = 600;
    int mac[6] = {0};
    char szMac[20];
    memset(szMac, 0, sizeof(szMac));
 
    ret = get_mac(IF_NAME, szMac);
    if (ret< 0)
    {
        printf("error with: %#x\n", ret);
        return -1;
    }
    printf("mac: %s\n", szMac);
    sscanf(szMac, "%02x:%02x:%02x:%02x:%02x:%02x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
    unsigned int i = 0;
    for (i = 0; i < sizeof(mac)/sizeof(*mac); i++)
    {
        run_arg->local_mac[i] = mac[i];
    }
 
    ret = get_local_ip(IF_NAME, run_arg->local_ip);
    if (ret< 0)
    {
        printf("error with: %#x\n", ret);
        return -1;
    }
    printf("ipaddr: %s\n", run_arg->local_ip);
 
    return 0;
}
 
static int random_proc(RunArg* run_arg, const char *ref_ip)
{
    int ret = -1;
    unsigned int uiIp = 0;
    unsigned int uiRandomIp = 0;
    struct in_addr addr;
 
    /*檢查IP是否合法*/
    uiIp = inet_addr(ref_ip);
    if ((unsigned int) (-1) == uiIp || 0 == uiIp)
    {
        printf("invalid ip[%s]\n", ref_ip);
        return -1;
    }
 
    ret = init(run_arg);
    if (ret< 0)
    {
        printf("error with: %#x\n", ret);
        return -1;
    }
 
    struct timeval curTime;
    abstime_get(&curTime);
    srand(curTime.tv_usec);
    unsigned int random = rand() % 255;
    printf("%d\n", random);
 
    // 優先修改第四位ip地址,若第四位不夠用,則修改第三位(CPU爲小端模式)
    unsigned char old = (uiIp >> 16) & 0xFF;
    unsigned int i = old;
    for (i = old; i < 255+old; i++)
    {
        unsigned char char_3rd = i % 255;
        unsigned int ref_ip = ((uiIp & 0xFF00FFFF) | (char_3rd << 16));
        unsigned int j = random;
        for (j = random; j < 255+random; j++)
        {
            unsigned char char_4th = j % 255;
            char_4th = (0 == char_4th) ? 1 : char_4th;
            printf("char_3rd: %d, j: %d, char_4th: %d\n", char_3rd, j, char_4th);
            uiRandomIp = ((ref_ip & 0xFFFFFF) | (char_4th << 24));
            if (uiIp == uiRandomIp)
            {
                // 與參照的IP地址相同,重新獲取隨機IP
                continue;
            }
            addr.s_addr = uiRandomIp;
            memset(run_arg->result.ip, 0, sizeof(run_arg->result.ip));
            strncpy(run_arg->result.ip, inet_ntoa(addr), sizeof(run_arg->result.ip));
            printf("new ip: %s\n", run_arg->result.ip);
            ret = is_ip_conflict(run_arg, run_arg->result.ip);
            if (ret == 0)
            {
                // 沒有IP衝突
                char* gw = rindex(run_arg->result.ip, '.');
                if (gw)
                {
                    memset(run_arg->result.gw, 0, sizeof(run_arg->result.gw));
                    memcpy(run_arg->result.gw, run_arg->result.ip, gw - run_arg->result.ip);
                    strcat(run_arg->result.gw, ".1");
                }
 
                strcpy(run_arg->result.mask, "255.255.255.0");
                if (i > old)
                {
                    strcpy(run_arg->result.mask, "255.255.0.0");
                }
 
                return 0;
            }
 
        }
    }
 
    return -1;
}
 
int get_local_ip(const char* if_name, char* local_ip)
{
    int ret;
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        printf("socket failed by %s\n", strerror(errno));
        return -1;
    }
 
    struct ifreq req;
    memset(&req, 0, sizeof(struct ifreq));
    strcpy(req.ifr_name, if_name);
 
    struct sockaddr_in *sin;
    sin = (struct sockaddr_in *)&req.ifr_addr;
 
    ret = ioctl(fd, SIOCGIFADDR, &req);
    if (ret < 0 && errno != EADDRNOTAVAIL)
    {
        printf("ioctl SIOCSIFADDR %s failed by %s\n", if_name, strerror(errno));
        return -1;
    }
 
    close(fd);
    strcpy(local_ip, inet_ntoa(sin->sin_addr));
 
    return 0;
}
 
int set_local_ip(const char* if_name, char* new_ip)
{
    int ret;
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        printf("socket failed by %s\n", strerror(errno));
        return -1;
    }
 
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(struct ifreq));
    strcpy(ifr.ifr_name, if_name);
 
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    inet_aton(new_ip, &addr.sin_addr);
 
    memcpy(&ifr.ifr_ifru.ifru_addr, &addr, sizeof(struct sockaddr_in));
    ret = ioctl(fd, SIOCSIFADDR, &ifr);
    if (ret < 0)
    {
        printf("ioctl SIOCSIFADDR %s failed by %s\n", if_name, strerror(errno));
        return -1;
    }
 
    close(fd);
 
    return 0;
}
 
int get_random_net(const char *ref_ip, NetCfg* Net)
{
    RunArg run_arg;
    memset(&run_arg, 0, sizeof(run_arg));
    memset(Net, 0, sizeof(NetCfg));
 
    int ret = random_proc(&run_arg, ref_ip);
    if (ret < 0)
    {
        printf("random_proc failed, ret=%d\n", ret);
        return -1;
    }
 
    memcpy(Net, &run_arg.result, sizeof(*Net));
 
    return 0;
}
 
static int auto_adjust_net(char* peer_ip)
{
    static struct timeval last_adjust = {0, 0};
 
    int ret = -1;
    NetCfg result;
    memset(&result, 0, sizeof(result));
 
    struct timeval cur = {0, 0};
    abstime_get(&cur);
    int pass_time = (cur.tv_sec - last_adjust.tv_sec)*1000 + (cur.tv_usec - last_adjust.tv_usec)/1000;
    if (pass_time < 5000)
    {
        printf("too many discovery package,pass_time=%d ms\n", pass_time);
        return -1;
    }
    memcpy(&last_adjust, &cur, sizeof(cur));
 
    char local_ip[16];
    memset(local_ip, 0, sizeof(local_ip));
 
    ret = get_local_ip("eth0", local_ip);
    if (ret < 0)
    {
        printf("failed to get_local_ip with %#x\n", ret);
        return -1;
    }
 
    char* find = rindex(local_ip, '.');
    if (find)
    {
        char temp[2][16];
        memset(temp, 0, sizeof(temp));
 
        strncpy(temp[0], local_ip, find-local_ip);
 
        find = rindex(peer_ip, '.');
        strncpy(temp[1], peer_ip, find-peer_ip);
 
        if (!strcmp(temp[0], temp[1]))
        {
            //同網段,不用修改
            printf("reference ip[%s] is same network with local ip[%s]\n", peer_ip, local_ip);
            return -1;
        }
    }
 
    ret = get_random_net(peer_ip, &result);
    if (ret < 0)
    {
        printf("failed to adjust net with %#x\n", ret);
        return -1;
    }
 
    printf("random ip addr: %s\n", result.ip);
    printf("random gw addr: %s\n", result.gw);
    printf("random mask addr: %s\n", result.mask);
 
    return 0;
}
 
int main()
{
 
    auto_adjust_net("192.168.11.5");
 
    return 0;
}

 

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