This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TM4C129XNCZAD:如何检测网络上的 IP 冲突

Guru**** 2390735 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1239964/tm4c129xnczad-how-can-i-detect-ip-conflict-on-the-network

器件型号:TM4C129XNCZAD

我有 NDK 版本 NDK_2_25_00_09、并希望检测网络上的 IP 冲突、以决定停止堆栈以使网络行为标准化并提高每个节点的可访问性

通过检测冲突、我的意思是检测另一台计算机或 Mac 地址在网络上具有相同的 IP、  

1.第一种方法

在启动期间、我尝试对本地 IP 执行 Ping 操作、如果 Ping 成功、则继续正常操作、如果失败、则停止 NDK 堆栈、但

-当我的以太网电缆未连接到设备 ping 请求成功,即使我实际上不是在网络所以,在这种情况下,我正在 ping 自己,在这种情况下, 实际上、我注意到该设备没有在网络上转发 ping 请求-即使在我的 PC 上 ping 自己时、我在 Wireshark 上看不到 ICMP 数据包、但当我 ping 另一个 IP 时、我看到了以太网连接上发出的 ICMP 数据包
-也当电缆连接到我的(交换机或路由器)和我的 IP 存在于网络上之前, (冲突情况,我想检测)  该设备未能 ping 通其 IP、因为它没有接收到自己的 ICMP 数据包、所以我收到了 ICMP 请求超时的 ping 失败、有时这种情况得到了验证、如果我启动了该设备、然后启动了冲突的设备、所以根据 连接到以太网总线的第一个节点将被验证、但实际上会根据 ping 失败来决定禁用网络、或者不够、因为许多情况下、其中一个是冲突  

2.第二种方法  

我使用 arp 表来检查我的本地 IP 是否存在于缓存的 arp 条目中,如果存在,则存在冲突,如果不存在,则不存在冲突,但

-此表被缓存,所以如果节点存在于总线上,但从未在我的本地 ARP 缓存中注册,因为 ARP 表只被填充 ARP 数据包接收,这只在通信期间触发所以  
如果器件在堆栈上进行了配置和初始化、但从未向另一个节点发送请求、因此该节点没有此节点的 IP 条目并尝试将自己的 IP 更改为总线上的另一个 IP 节点、该怎么办?


那么、NDK 内部是否有办法检测冲突、或者使用某种方法我可以在没有亲情或违反任何协议规则的情况下检测冲突?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

     我认为 NDK 中没有内置 API 或方法来检测 IP 冲突。 您将需要在应用程序中检测冲突。 在一些谷歌搜索后,我认为你在做什么是正确的路径-你的第二种方法 似乎是建议在各种文章。 也许、您可以将方法1和方法2结合在一起。 下面是一些提供有关如何检测 IP 冲突的建议的文章。 您可以自行进行更多搜索。  

    https://www.dnsstuff.com/ip-address-conflict#:~:text=The%20best%20way%20to%20avoid,addresses%20from%20your%20core%20devices

    https://documentation.its.umich.edu/node/523

    https://www.auvik.com/franklyit/blog/how-to-fix-ip-address-conflicts/

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    谢谢、Charles

    这些文章非常有用、我依赖 TI 堆栈中的这些函数

    -  在 NDK 引导期间使用 arp 挂钩的读取 arp 包(仅限)
    - conmping 我修改了这个函数 alittle 位,以重构它,并适合我的应用程序

    第一个场景是使用网络中已有的 IP 对节点进行静态配置

    - NDK 启动任务在本质上会发送一条 ARP 广播消息,在网络上声明自己,因此如果此节点已经存在(冲突)
    远程节点使用其 MAC 地址、该地址与当前节点 Mac 不同。因此在引导任务期间、我们会设置 ARP 挂钩并读取接收到的消息、如果我们收到配置为源发送器的 IP、并使用不同的 MAC、被视为冲突、 我们采取了措施、我禁用了堆栈、但是您可以禁用接口并定期对其进行检查

    对于这种方法、我使用了代码片段、

    LLI_setARPHook(fxnARPCallback); //to set the arp hook before starting (booting) the stack
    
    do
    {
        rc= NC_NetStart(hCfgIpAddr,\
        SoAd_vNetworkOpenHook,\
        SoAd_vNetworkCloseHook,\
        SoAd_vNetworkIPAddressChangeHook);
    }while(rc> 0);


    和钩子本身  

    void fxnARPCallback(void* data)
    {
        ARPHDR* ARPPacketHDR;
        ARPPacketHDR = (ARPHDR*)( ((INT8U*)data) + 14U);
    
        if( memcmp((ARPPacketHDR->IPSrc),&(SoAd_objstrDCFGType.objstrIPSettings.u32LocalIP), (INT32U) 4U ) == 0U)
        {
            if( memcmp((ARPPacketHDR->SrcAddr), SoAd_objstrDCFGType.objstrIPSettings.u8MACAdressArray, (INT32U) 6U) != 0U)
            {
                /*conflict is detected */
            }
        }
    }



    然后通过删除和添加 IP 条目在更改 IP 时进行 DETech 合并,在执行此操作之前,我们需要对这个新的 IP 执行 ping 操作,如果它没有 ping 操作,则 IP 不被使用,如果 ping 操作成功,则我们可以更改我们的 IP 配置。  在运行时使用并回复我并记录检测到 conflcit 的消息   

    我的 ping 函数版本如下所示
    INT8 SoAd_Ping( IPN *IPAddr, INT8S s8Attempts )
    {
        fd_set              ibits;
        INT32S              cc,fromlen;
        UINT16              cnt_send=0, cnt_recv=0;     /* Packets send and received */
        SOCKET              s;                          /* ICMP socket file descriptor */
        struct  sockaddr_in to;                         /* who to ping */
        struct  sockaddr_in from;                       /* who replied */
        static INT8S        pBuf[30] = {0};
        struct  timeval     timeout;                    /* Timeout struct for select */
        INT32U              timestart;
    
        INT8S               retCode;
        INT8U               SucessAttempts;
    
        (void)fdOpenSession(TaskSelf());
        retCode = Ping_Success;
        SucessAttempts = 0U;
        /* Create the ICMP Socket */
        s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
        if( s == INVALID_SOCKET )
        {
            SoAd_vmLogError1(&objstrSoAdModuleInfoType, "failed socket create (%d)",fdError());
            retCode = Ping_INVALID_SOCKET;
        }
        /* Create to "to" address */
        bzero( &to, sizeof(struct sockaddr_in));
        to.sin_family       = AF_INET;
        to.sin_addr.s_addr  = *IPAddr;
        /* Create to "from" address */
        bzero( &from, sizeof(struct sockaddr_in));
        from.sin_family     = AF_INET;
        /* Set socket option to allow us to broadcast */
        cc = 1;
        if( setsockopt( s, SOL_SOCKET, SO_BROADCAST, &cc, sizeof(INT32S) ) < 0 )
        {
            SoAd_vmLogError1(&objstrSoAdModuleInfoType, "failed set IP broadcast (%d)",fdError());
            retCode = Ping_Failed_SetIPBroadcast;
        }
        /* Configure our timeout to be 0.5 seconds */
        timeout.tv_sec  = 0;
        timeout.tv_usec = 500000;
    
        SoAd_vmLogActivity5(&objstrSoAdModuleInfoType, "Pinging %d.%d.%d.%d with %d data bytes",\
                            (INT32U)(*IPAddr & 0xFFU),\
                            (INT32U)((*IPAddr >> 8)&0xFFU),\
                            (INT32U)((*IPAddr >> 16)&0xFFU),\
                            (INT32U)((*IPAddr >> 24)&0xFFU),\
                            PING_DATALEN );
        /* Ping loop */
        while( cnt_send < s8Attempts )
        {
            /* Use send count for sequence - starts at "1" */
            cnt_send++;
            memset(pBuf, 0x00, sizeof(char)*40);
            /* Build the ping packet */
            SoAd_BuildPing( pBuf, cnt_send );
            /* Get the time now */
            timestart = llTimerGetTime(0);
            cnt_recv = 0;
            /* Send the ping */
            cc = sendto( s, pBuf, PING_DATALEN, 0,(struct sockaddr *)&to, sizeof(to) );
            if( cc < 0 )
            {
                SoAd_vmLogError1(&objstrSoAdModuleInfoType, "failed sento (%d)",fdError());
            }
            else if( cc != PING_DATALEN )
            {
                SoAd_vmLogError0(&objstrSoAdModuleInfoType, "sento - partial write!");
            }
            else{
                while( (timestart+2) > llTimerGetTime(0) )
                {
                    /* Select for timeout period second */
                    FD_ZERO(&ibits);
                    FD_SET(s, &ibits);
                    if( fdSelect( (INT32S)s, &ibits, 0, 0, &timeout ) < 0 )
                    {
                        SoAd_vmLogError1(&objstrSoAdModuleInfoType, "failed select (%d)",fdError());
                        retCode = Ping_Failed_SelectSocket;
                        break;
                    }
                    /* Check for a reply */
                    if( FD_ISSET(s, &ibits) )
                    {
                        fromlen = sizeof(from);
                        cc = (INT32S)recvfrom( s, pBuf, PING_DATALEN, 0,(struct sockaddr *)&from, &fromlen );
                        if( cc < 0 )
                        {
                            SoAd_vmLogError1(&objstrSoAdModuleInfoType, "failed recvfrom (%d)",fdError());
                            retCode = Ping_Failed_recvfrom;
                            break;
                        }
                        /* Check the reply. Bump the recv count if good reply */
                        if( SoAd_CheckPing( pBuf, cnt_send, cc, IPAddr) )
                        {
                            SucessAttempts++;
                            cnt_recv++;
                            break;
                        }
                    }
                }
                /* If send is more than reply, then we missed a reply */
                if( !cnt_recv )
                {
                    SoAd_vmLogError0(&objstrSoAdModuleInfoType, "Reply timeout");
                    retCode = Ping_Reply_Timeout;
                }
            }
        }
    
        if(retCode == Ping_Success && SucessAttempts > s8Attempts/2)
        {
            retCode = Ping_Success;
        }
        else
        {
            /*do nothing*/
            retCode = Ping_Fail;
        }
    
        if( s != INVALID_SOCKET )
            fdClose( s );
    
        return retCode;
    }
    
    static INT8 SoAd_CheckPing( INT8S *ps8Buf,INT16U u16Seq,INT32S s32NumberOfBytes, IPN* ps8IpStr)
    {
        INT32S     ICMPLen;
        INT16U     Id,Seq;
        INT8U      retCode;
    
        retCode = Ping_SUCCESS_ECHOREPLY;
        /* Get header pointers */
    
        /* Get the total length of the ICMP message */
        ICMPLen = (uint)(HNC16( ((IPHDR *)ps8Buf)->TotalLen )) - ( (((IPHDR *)ps8Buf)->VerLen & 0xF) * 4 );
    
        /* Verify the ICMP type */
        if( ((ICMPHDR *)(ps8Buf+( (((IPHDR *)ps8Buf)->VerLen & 0xF) * 4 )))->Type != ICMP_ECHOREPLY )
            retCode = Ping_NOTICMP_ECHOREPLY ;
    
        /* Get the seq and the id */
        Seq = (INT32S)HNC16(((ICMPREQHDR *)((ICMPHDR *)(ps8Buf+( (((IPHDR *)ps8Buf)->VerLen & 0xF) * 4 )))->Data)->Seq);
        Id  = (INT32S)HNC16(((ICMPREQHDR *)((ICMPHDR *)(ps8Buf+( (((IPHDR *)ps8Buf)->VerLen & 0xF) * 4 )))->Data)->Id);
    
        /* If the reply is incorrect, don't continue */
        if( Id != PING_IDENTIFICATION || Seq != u16Seq )
        {
            SoAd_vmLogError4(&objstrSoAdModuleInfoType, "Non-matching echo reply from %d.%d.%d.%d", \
                             (INT32U)(*ps8IpStr & 0xFFU),\
                             (INT32U)((*ps8IpStr >> 8)&0xFFU),\
                             (INT32U)((*ps8IpStr >> 16)&0xFFU),\
                             (INT32U)((*ps8IpStr >> 24)&0xFFU));
            retCode = Ping_NonMatching_ECHOREPLY;
        }
    
        /* Print out data on correct reply */
        SoAd_vmLogActivity7(&objstrSoAdModuleInfoType, "Reply from %d.%d.%d.%d, with %d bytes, Seq=%u, TTL = %d", \
                            (INT32U)(*ps8IpStr & 0xFFU),\
                            (INT32U)((*ps8IpStr >> 8)&0xFFU),\
                            (INT32U)((*ps8IpStr >> 16)&0xFFU),\
                            (INT32U)((*ps8IpStr >> 24)&0xFFU),\
                            ICMPLen-8,\
                            Seq,\
                            ((IPHDR *)ps8Buf)->Ttl);
    
        /* Validate the data payload */
        if( ICMPLen != PING_DATALEN )
        {
            SoAd_vmLogError2(&objstrSoAdModuleInfoType, "Data length error in reply (%d of %d)", s32NumberOfBytes, PING_DATALEN);
            retCode = Ping_DataLenError_ECHOREPLY;
        }
    
    
        if( *(UINT8 *)((ps8Buf+( (((IPHDR *)ps8Buf)->VerLen & 0xF) * 4 )+8)) != (UINT8)('0'+8) )
        {
            SoAd_vmLogError3(&objstrSoAdModuleInfoType, "Data verification error in reply (%d 0x%x 0x%x)", \
                             s32NumberOfBytes, \
                             *(UINT8 *)((ps8Buf+( (((IPHDR *)ps8Buf)->VerLen & 0xF) * 4 )+s32NumberOfBytes)), \
                             '0'+s32NumberOfBytes );
            retCode = Ping_DataVerFailed_ECHOREPLY;
        }
        return retCode;
    }
    
    static void SoAd_BuildPing( INT8S *ps8Buf, INT16U u16seq )
    {
        *(ps8Buf+8) = '0'+8;
        /* Get pointers to the ICMP and ICMPREQ headers */
    
        /* Fill out echo request */
        ((ICMPHDR *)ps8Buf)->Type   = ICMP_ECHO;
        ((ICMPHDR *)ps8Buf)->Code   = 0;
        ((ICMPREQHDR *)(((ICMPHDR *)ps8Buf)->Data))->Id    = HNC16(PING_IDENTIFICATION);
        ((ICMPREQHDR *)(((ICMPHDR *)ps8Buf)->Data))->Seq   = HNC16(u16seq);
    
        /* Checksum the ICMP header */
        /* Although the checksum function is reentrant, we follow the rules */
        /* and call llEnter()/llExit() since this is a stack function. */
        llEnter();
        ICMPChecksum( (ICMPHDR *)ps8Buf, PING_DATALEN );
        llExit();
    }