分析TCP/IP协议栈代码之ARP(STM32平台)

发布者:真实幻想最新更新时间:2018-04-28 来源: eefocus关键字:TCP  IP协议栈  ARP  STM32平台 手机看文章 扫描二维码
随时随地手机看文章

1. ARP的简介

Address Resolution Protocol-地址解析协议

ARP为IP地址到对应的硬件地址之间提供动态映射。从逻辑Internet地址到对应的物理硬件地址需要进行翻译。这就是ARP的功能。ARP的功能是在32 bit的IP地址和采用不同网络技术的硬件地址之间提供动态映射。


2. ARP的应答流程


任何时候我们敲入下面这个形式的命令:

[html] view plain copy

  1. % ftp bsdi  //示例而已  

都会进行以下这些步骤。这些步骤的序号如图 4 - 2所示。

1) 应用程序FTP客户端调用函数gethostbyname(3)把主机名(bsdi)转换成32 bit的IP地址。这个函数在DNS(域名系统)中称作解析器,我们将在第1 4章对它进行介绍。这个转换过程或者使用DNS,或者在较小网络中使用一个静态的主机文件(/etc/hosts) 。

2) FTP客户端请求TCP用得到的IP地址建立连接。

3) TCP发送一个连接请求分段到远端的主机,即用上述 IP地址发送一份IP数据报(在第1 8章我们将讨论完成这个过程的细节) 。

4) 如果目的主机在本地网络上(如以太网、令牌环网或点对点链接的另一端) ,那么IP数据报可以直接送到目的主机上。如果目的主机在一个远程网络上,那么就通过 IP选路函数来确定位于本地网络上的下一站路由器地址,并让它转发 IP数据报。在这两种情况下,IP数据报都是被送到位于本地网络上的一台主机或路由器。

5) 假定是一个以太网,那么发送端主机必须把 32 bit的IP地址变换成48 bit的以太网地址。从逻辑Internet地址到对应的物理硬件地址需要进行翻译。这就是 ARP的功能。ARP本来是用于广播网络的,有许多主机或路由器连在同一个网络上。

6) ARP发送一份称作ARP请求的以太网数据帧给以太网上的每个主机。这个过程称作广播,如图 4 - 2中的虚线所示。 ARP请求数据帧中包含目的主机的IP地址(主机名为bsdi) ,其意思是“如果你是这个IP地址的拥有者,请回答你的硬件地址。 ”

7) 目的主机的ARP层收到这份广播报文后,识别出这是发送端在寻问它的 IP地址,于是发送一个ARP应答。这个ARP应答包含IP地址及对应的硬件地址。

8) 收到ARP应答后,使ARP进行请求—应答交换的IP数据报现在就可以传送了。

9) 发送IP数据报到目的主机。

3. ARP的分组格式


• 以太网报头中的前两个字段是以太网的源地址和目的地址。目的地址为全 1的特殊地址是广播地址。电缆上的所有以太网接口都要接收广播的数据帧。

• 两个字节长的以太网帧类型表示后面数据的类型。对于 A R P请求或应答来说,该字段的值为0 x 0 8 0 6。

• 硬件类型字段表示硬件地址的类型。它的值为 1即表示以太网地址。

• 协议类型字段表示要映射的协议地址类型。它的值为 0 x 0 8 0 0即表示I P地址。它的值与包含I P数据报的以太网数据帧中的类型字段的值相同,这是有意设计的(参见图 2 - 1) -忘了截过来了。

• 接下来的两个1字节的字段,硬件地址长度和协议地址长度分别指出硬件地址和协议地址的长度,以字节为单位。对于以太网上I P地址的ARP请求或应答来说,它们的值分别为6和4。

• 操作字段(op)指出四种操作类型,它们是 ARP请求(值为1) 、ARP应答(值为2) 、RARP请求(值为3)和R ARP应答(值为4) (我们在第5章讨论RARP) 。这个字段必需的,因为ARP请求和ARP应答的帧类型字段值是相同的。

• 接下来的四个字段是发送端的硬件地址(在本例中是以太网地址) 、发送端的协议地址(IP地址) 、目的端的硬件地址和目的端的协议地址。注意,这里有一些重复信息:在以太网的数据帧报头中和ARP请求数据帧中都有发送端的硬件地址。

对于一个ARP请求来说,除目的端硬件地址外的所有其他的字段都有填充值。当系统收到一份目的端为本机的 ARP请求报文后,它就把硬件地址填进去,然后用两个目的端地址分别替换两个发送端地址,并把操作字段置为 2,最后把它发送回去。

--------------------------------以上内容整理于《TCP/IP协议详解:卷1》----------------------------

理是那个那个理,但是过于抽象了,不过是基础,看完上面再看实现,那感觉很爽的~~~

------------------------------------------以下内容产生于代码及分析--------------------------------------

4. ARP的宏定义实现

 以太网协议而非802.3协议,看ETH命名的头名字就晓得了,地址位置可以结合两个header算算就出来了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


// ******* ARP *******
//ARP包长度
#define ETH_ARP_PACKET_LEN          28

//硬件地址长度值
#define ETHTYPE_ARP_L_V             0x06
//协议地址长度值
#define ETHTYPE_ARP_PROTOCOL_SIZE_V 0x04
//操作码位置 2字节
#define ETH_ARP_OPCODE_H_P 0x14
#define ETH_ARP_OPCODE_L_P 0x15
//ARP请求操作码值
#define ETH_ARP_OPCODE_REQUEST_V    0x0001
#define ETH_ARP_OPCODE_REQUEST_H_V  0x00
#define ETH_ARP_OPCODE_REQUEST_L_V  0x01
//ARP响应操作码值
#define ETH_ARP_OPCODE_REPLY_V      0x0002
#define ETH_ARP_OPCODE_REPLY_H_V    0x00
#define ETH_ARP_OPCODE_REPLY_L_V    0x02
// 发送者源硬件地址位置 6字节
#define ETH_ARP_SRC_MAC_P           0x16
//发送者源IP地址位置 4字节
#define ETH_ARP_SRC_IP_P            0x1c
//目标硬件地址位置 6字节
#define ETH_ARP_DST_MAC_P           0x20
//目标IP地址位置 4字节
#define ETH_ARP_DST_IP_P            0x26

5. ARP的实现函数

以太网的header在ARP的header之前,很简单的,介绍先。

 配置以太网的头,为14字节:6字节目的mac地址+6字节源mac地址+2字节协议类型,如图4-3

1
2
3
4
5
6
7
8
9
10
11
12
13


// make a return eth header from a received eth packet
void make_eth(unsigned char *buf)
{
    unsigned char  i = 0;

    //copy the destination mac from the source and fill my mac into src
    while(i < sizeof(mac_addr))
    {
        buf[ETH_DST_MAC + i] = buf[ETH_SRC_MAC + i];
        buf[ETH_SRC_MAC + i] = macaddr[i];
        i++;
    }
}


展开就是这样的,看看宏定义是否与此一一对应呢。


 在判断为arp请求之后,填充以太网的头之后响应arp请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31


void make_arp_answer_from_request(unsigned char *buf)
{
    unsigned char  i = 0;
    //配置以太网的头,为14字节:6字节目的mac地址+6字节源mac地址+2字节协议类型
    make_eth(buf);
    buf[ETH_ARP_OPCODE_H_P] = ETH_ARP_OPCODE_REPLY_H_V; //arp 响应
    buf[ETH_ARP_OPCODE_L_P] = ETH_ARP_OPCODE_REPLY_L_V;

     // 后面的ARP_DEBUG插入此处即可。

    // fill the mac addresses:
    while(i < sizeof(mac_addr))
    {
        buf[ETH_ARP_DST_MAC_P + i] = buf[ETH_ARP_SRC_MAC_P + i];
        buf[ETH_ARP_SRC_MAC_P + i] = macaddr[i];
        i++;
    }

    i = 0;
    //fill the ipv4 addresses
    while(i < sizeof(ipv4_addr))
    {
        buf[ETH_ARP_DST_IP_P + i] = buf[ETH_ARP_SRC_IP_P + i];
        buf[ETH_ARP_SRC_IP_P + i] = ipaddr[i];
        i++;
    }

    // eth+arp is 42 bytes:
    enc28j60PacketSend(ETH_HEADER_LEN + ETH_ARP_PACKET_LEN, buf);
}

当然,响应ARP请求的前提是你得确定有人向你发出ARP请求(下面那个函数就是了),并且这个人是谁,你是要知道的(通过发送者的IP和MAC地址),这个很容易,本协议是将地址放在几个全局变量里面的,大家就都知道了,虽然全局变量用起来很爽,但是对模块化以及后期维护带来的不便也是很大的。

 检查是否为合法的eth,并且只接受发给本机的arp数据,此函数在上面那个函数之前被调用,再下面的代码就是演示的例程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30


//检查是否为合法的eth,并且只接受发给本机的arp数据
unsigned char  eth_type_is_arp_and_my_ip(unsigned char *buf, unsigned  int len)
{
    unsigned char  i = 0;

    // 帧长度不得小于以太网的最小帧长度值,即46-除以太网头和CRC检测
    if(len < MIN_FRAMELEN)
    {
        return(0);
    }

    if(buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V)
    {
        return(0);
    }

    //不是发给本机IP地址的不接收,那么如此说来,我在这里可以设定监听其他IP的信息!
    while(i < sizeof(ipv4_addr))
    {
        if(buf[ETH_ARP_DST_IP_P + i] != ipaddr[i])
        {
            return(0);
        }

        i++;
    }

    return(1);
}

以上函数在别人向你发送任何请求之前都将被调用一次(原因是本协议只是实现了对IP和ARP的响应),所以需要在一个while死循环或者RTOS的一个thread/task/process里面。如下所示:

 上层调用示例代码 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36


/*
    此部分为一部分代码
*/

/*do something initial */

while(1)
{
    // get the next new packet:         
    plen = enc28j60PacketReceive(BUFFER_SIZE, buf);      
      
    // plen will be unequal to zero if there is a valid packet (without crc error)          
    if(plen==0)
    {
        continue;
    }
    // check if ip packets are for us:          
    if(eth_type_is_ip_and_my_ip(buf,plen)==0) 
    {
        //丢弃本次获取的数据,再接下一个
        continue;
    }   
    // arp is broadcast if unknown but a host may also
    // verify the mac address by sending it to 
    // a unicast address.
    //这里就是ARP的响应了,如果我们在这里加入串口调试,
    //就可以将谁在向我发送arp请求的数据打印到串口
    //当然加一个选择宏,放在函数里面更方便一点           
    if(eth_type_is_arp_and_my_ip(buf,plen))
    {
        make_arp_answer_from_request(buf);          
        continue;
    }
    /*do other things */
  
}

6. ARP实验调式

嗯,接者来看看在make_arp_answer_from_request函数里面加入串口调试信息来输出arp请求者的ip和mac地址。

 加入到 make_arp_answer_from_request中的调试代码,用于输出ARP请求者的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42


#ifdef ARP_DEBUG
printf("ARP请求者IP地址 : \r\n");

while(i < sizeof(ipv4_addr))
{
    printf("%d", buf[ETH_ARP_SRC_IP_P + i]);

    if(i != sizeof(ipv4_addr) - 1) // 加入判断只是为了输出的形式好看点 
    {
        printf(".");
    }

    else
    {
        printf("\r\n");
    }

    i++;
}

i = 0;
printf("ARP请求者MAC地址 :\r\n");

while(i < sizeof(mac_addr))
{
    printf("%x", buf[ETH_ARP_SRC_MAC_P + i]);

    if(i != sizeof(mac_addr) - 1)
    {
        printf(":");
    }

    else
    {
        printf("\r\n");
    }

    i++;
}

i = 0;
#endif

PC端:测试arp请求需要先执行“arp -d”清楚本地的arp-ip对应列表,这样PC机才会发送ARP请求

ps:enj28j60的MAC地址是软件设定的,所以就不打码了。


串口端:显示调试信息而已


下面是我放着没动它,局域网内就有其他主机来找我的嵌入式Web Server了,O(∩_∩)O~


--------------------------------------------以下是我看代码看的不仔细的纠结,可跳过-----------------------------------------------

但是在实验过程中发现如下情况是不会调用make_arp_answer_from_request函数的:PC机本地有arp-ip列表,就是ping过一次以后ping第二次的就不会去响应了,这个本来就是要如此的,希望结果就是这样的,你要问我是谁(MAC地址是多少),然后我告诉你一次,你记住就ok了么。但是问题出在我们的协议栈实现代码里面,本协议栈在通过eth_type_is_arp_and_my_ip判断了是发给本机的数据包之后,就会调用arp响应的,没有一个对以太网header的类型的判断啊。怎么会自己变的智能了呢?

 代码如下 

1
2
3
4
5
6



if(eth_type_is_arp_and_my_ip(buf, plen))
{
    make_arp_answer_from_request(buf);
    continue;
}

那么问题出在哪里呢?

1. 难道是进入make_arp_answer_from_request 函数,但是没有发送出去么?当然也不会在enc28j60PacketSend(ETH_HEADER_LEN + ETH_ARP_PACKET_LEN, buf); 里面,因为没有判别信息传递进去。(当然发不发,只要如函数之后就会有打印信息输出到串口)

2. 那么就是eth_type_is_arp_and_my_ip 函数返回为0了;

但是ping命令还是可以得到响应的,哦,对了,还有ICMP(我本来就用的ping,都忘了,放在IP层),那么就好解释了,第一次是由ARP+ICMP响应,第二次及之后的只有ICMP响应。所以现象还是符合原理解释的。

但是,我用网页去刷新和点亮LED等,走的肯定是IP包,ARP还是不响应。

回去看代码,当时对自己就无语了

if(buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V)

是ARP啊,我以为是对IP和ARP都会响应,而且人家的函数名是eth_type_is_arp_and_my_ip ,我自己把它想成eth_type_is_arp_and_ip ,╮(╯▽╰)╭

代码就在那里,自己SB了~~~

--------------------------------------------以上是我边想边实验边写的过程-----------------------------------------------

硬件环境:STM32+ENC28J60

软件环境:MDK4.70a

TCP/IP协议栈:开发者的网站已经关闭了,也没有命名~~~

给出作者信息:

/*********************************************
 * modified: 2007-08-08
 * Author  : awake
 * Copyright: GPL V2
 * http://www.icdev.com.cn/?2213/
 * Host chip: ADUC7026
**********************************************/


关键字:TCP  IP协议栈  ARP  STM32平台 引用地址:分析TCP/IP协议栈代码之ARP(STM32平台)

上一篇:分析TCP/IP协议栈代码之UDP(STM32平台)
下一篇:分析TCP/IP协议栈代码之IP & ICMP(STM32平台)

推荐阅读最新更新时间:2024-03-16 16:00

SIM900A与网络调试助手进行TCP收发通信
硬件平台:正点原子ATK-SIM900A GSM/GPRS模块开发板 软件平台:PC端串口调试工具&网络调试工具 测试目的:测试开发板GPRS功能、熟悉AT指令 1.设置网络 因为我这里是通过路由器上网,所以要先进行一些设置。 先查看自己本机IP,这里是192.168.1.103,如图1. 图1 通过自己的路由器,查看本机对外的IP,如图2. 图2 路由器的设置里面有一项“转发规则”,如图3,选择“DMZ主机”。 图3 在“DMZ主机”中,启动DMZ主机状态,将DMZ主机IP设置为前面查到的192.168.1.103,然后保存,如图4. 图4 这样就把本机的IP映射到外网上,就可以从外网直接访问自己的电脑。 2
[单片机]
SIM900A与网络调试助手进行<font color='red'>TCP</font>收发通信
基于STM32的双路信号源及配置平台设计
随着在雷达探测、仪表测量、化学分析等领域研究的不断深入,不仅要求定性的完成目标检测,更加需要往高精度、高分辨率成像的方向发展。一方面,产生频率、幅度灵活可控,尤其是低相位噪声、低杂散的频率源对许多仪器设备起着关键作用。另一方面,电子元器件实际性能参数并非理想以及来存在自外部内部的干扰,大量的误差因素会严重影响系统的准确性。双路参数可调的信号源可有效地对系统误差、信号通道间不平衡进行较调,并且可以产生严格正交或相关的信号,这在弱信号检测中发挥重要作用。为此本文采用双通道DDS方法,以STM32为控制器,完成了一种高分辨率灵活可调的双路信号源电路设计。 1 DDS原理及系统方案 1.1 DDS工作原理 直接数字频率合成(DDS)是一种以
[单片机]
基于<font color='red'>STM32</font>的双路信号源及配置<font color='red'>平台</font>设计
意法半导体推出低价位的硬件开发平台 STM32 Discovery Kit
微控制器厂商意法半导体(纽约证券交易所代码:STM)发布一款低价位的硬件开发平台STM32 Discovery Kit,让开发人员以最简单的方式着手基于32位微控制器的应用开发,用户可从主要的第三方软件工具厂商Atollic、IAR和Keil处下载免费或低价的软件开发工具。 作为一个超低价位且简便的开发入门平台,STM32 Discovery Kit特别适用于STM32超值系列微控制器。这个配备USB接口的开发板可通过USB线直接连接个人电脑,开发板的安装使用浅显易懂。以24MHz、64引脚的STM32F100RBT6B超值系列微控制器为核心,开发板整合128KB的闪存和多路定时器、模拟外设和工业标准的串行接口
[单片机]
意法半导体推出低价位的硬件开发<font color='red'>平台</font> <font color='red'>STM32</font> Discovery Kit
单片机联网,UIP实现tcp/udp协议
UIP是单片机界联网的一个很好地选择,移植这个库有点复杂,首先是第一步,网卡驱动要写好,使用的网卡芯片为ENC28J60,驱动可以再工程包里面找到 //配置网卡硬件,并设置MAC地址 //返回值:0,正常;1,失败; u8 tapdev_init(u8* macaddr) { u8 i,res=0; res=ENC28J60_Init((u8*)macaddr); //初始化ENC28J60 //把IP地址和MAC地址写入缓存区 for (i = 0; i 6; i++)uip_ethaddr.addr =macaddr ; //指示灯状态:0x4
[单片机]
嵌入式TCP/IP协议单片机技术在网络通信中的应用
摘要:介绍了嵌入式TCP/IP协议单片机在网络通信中的数据传输技术。将TCP/IP协议嵌入式单片机中,借助网卡芯片CS8900实现了单片机在局域网内和通过局域网在因特网上的数据传输。用户终端以单片机系统板为媒介,通过网络与远程数据终端实现数据通信。 关键词:TCP/IP协议 单片机 因特网 局域网 网卡芯片 在因特网上,TCP/IP协议每时每刻保证了数据的准确传输。在数据采集领域,如何利用TCP/IP协议在网络中进行数据传输成为一个炙手可热的话题。在本系统中,笔者利用TCP/IP协议中的UDP(用户数据报协议)、IP(网络报文协议)、ARP(地址解析协议)及简单的应用层协议成功地实现了单片机的网络互连,既提高了数据传输的速度,
[应用]
LwIP之TCP Server发送和接收Demo
在sal的基础上,tcp server的操作和pc端的流程基本一致,这里做一个小demo,server监听5000端口,向发起连接的client对象发送一个字符串,然后阻塞接收client发回的数据,再断开连接,等待下一个连接请求。 /* * Copyright (c) 2006-2019, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-02-20 ShineRoyal the first version */
[单片机]
LwIP之<font color='red'>TCP</font> Server发送和接收Demo
51单片机ARP协议实现原理
ARP是Address Resolution Protocol的缩写。中文译做“地址解析协议”,本质是完成网络地址到物理地址的映射。从概念上讲就是找到一个映射方法f,使得“物理地址 = f(网络地址)”。物理地址有两种基本类型:以太网类型和proNET令牌环网类型,网络地址特指IP地址,对映射方法的要求就是高效。具体到以太网,它使用的是动态绑定转换的方法。为什么不直接使用同一种地址,而要这么麻烦呢?因为TCP/IP网络就是为将不同种类计算机互联而发明的,它的体系结构是分层的,层和层之间相互独立,改变物理层的实现不会影响到网络层。 32位IP地址到以太网48位物理地址的映射,采用动态绑定转换的方法会遇到许多细节问题,例如:减
[单片机]
嵌入式TCP/IP协议单片机技术在网络通信中的应用
摘要:介绍了嵌入式TCP/IP协议单片机在网络通信中的数据传输技术。将TCP/IP协议嵌入式单片机中,借助网卡芯片CS8900实现了单片机在局域网内和通过局域网在因特网上的数据传输。用户终端以单片机系统板为媒介,通过网络与远程数据终端实现数据通信。 关键词:TCP/IP协议 单片机 因特网 局域网 网卡芯片 在因特网上,TCP/IP协议每时每刻保证了数据的准确传输。在数据采集领域,如何利用TCP/IP协议在网络中进行数据传输成为一个炙手可热的话题。在本系统中,笔者利用TCP/IP协议中的UDP(用户数据报协议)、IP(网络报文协议)、ARP(地址解析协议)及简单的应用层协议成功地实现了单片机的网络互连,既提高了数据传输的速度,
[嵌入式]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved