lwip之数据收发流程_1
lwip从逻辑上看也是分为4层:链路层、网络层(IP、ARP、(ICMP、IGMP这两个协议是网络层的补充协议,并不严格属于网络层))、传输层(TCP、UDP)、应用层,基本等同TCP/IP,只是各层之间可以进行交叉存取,没有严格划分。
协议汇总:1. ARP协议:根据IP地址获取物理地址MAC的一个TCP/IP协议
一个典型的lwip系统包含3个进程:首先是上层应用程序进程,然后是lwip协议栈进程,最后是底层硬件数据包接收进程动态内存管理:采用ucos-ii内存管理系统,即申请一块内存,分割成整数个大小相同的内存块
一. 链路层当主机A要与主机B通信时,ARP协议可以将主机B的IP地址解析成主机B的MAC地址,工作流程如下: 第一步:主机A先检查自己的ARP缓冲,看是否存在主机B匹配的MAC地址,如果没有,就会向外广播一个ARP请求包第二步:其他主机收到后,发现请求的IP地址与自己的IP地址不匹配,则丢弃ARP请求第三步:主机B确定ARP请求中的IP地址与自己的IP地址匹配,则将主机A的IP地址和MAC地址映射到本地ARP缓存中第四步:主机B将包含其MAC地址的ARP回复发回给主机A第五步:主机A收到从主机B发来的ARP回复时,会将主机B的IP地址和MAC地址映射更新到本地ARP缓存中。主机B的MAC地址一旦确定,主机A就可以向主机B发送IP通信了连接链路层和网络层的纽带:以太网数据包接收进程tcpip_threadstatic void tcpip_thread(void *arg){struct tcpip_msg *msg; // 消息来自于网卡中断while(1){// 该任务阻塞在这里接收要处理的消息,当有数据包到来时,网卡芯片中断函数接收数据,并post消息,中断退出后,该任务获取消息sys_timeouts_mbox_fetch(&mbox, (void **)&msg); // 判断本条消息的类型,只关注数据包消息TCPIP_MSG_INPKTswitch (msg->type){case TCPIP_MSG_INPKT: // 数据包消息if(msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET))ethernet_input(msg->msg.inp.p,msg->msg.inp.netif); // 如果支持ARP,先进行ARP处理,再判断是否递交IP层,对于IP数据包,这里2个选择最终都要调用ip_input进入IP层elseip_input(msg->msg.inp.p, msg->msg.inp.netif); // 否则直接递交IP层,ip_input为IP层主要函数,解析见下文,这里直接调用ip_input存在问题,有误,需要先以太网数据包指针,使掠过包头,指向IP协议包头memp_free(MEMP_TCPIP_MSG_INPKT, msg); // 释放消息内存break;case TCPIP_MSG_TIMEOUT: // 超时消息sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);memp_free(MEMP_TCPIP_MSG_API, msg);break;default:break;}}}err_t ethernet_input(struct pbuf *p,struct netif *netif){struct eth_hdr *ethhdr; // 以太网数据包头结构体u16_t type;s16_t ip_hdr_offset = SIZEOF_ETH_HDR; // 包头固定值14字节ethhdr = (eth_hdr *)p->payload;type = htons(ethhdr->type);switch(type){case ETHTYPE_IP: // IP数据包etharp_ip_input(netif,p); // 使用收到的IP包更新ARP缓存表,详见《lwip之ARP协议》pbuf_header(p, -ip_hdr_offset); // 调整以太网数据包指针,使掠过包头,指向IP协议包头ip_input(p,netif); // 提交IP层,ip_input为IP层主要函数,解析见下文case ETHTYPE_ARP: // ARP数据包etharp_arp_input(netif,(struct eth_addr *)netif->hwaddr,p); // ARP数据包处理,第二个形参是本机MAC,详见《lwip之ARP协议》break;default:break;}}注:消息结构体struct tcpip_msg {enum tcpip_msg_type type; // 本条消息的类型:TCPIP_MSG_INPKT - 数据包消息,TCPIP_MSG_TIMEOUT - 超时消息sys_sem_t *sem; // 事件控制块ECBunion{struct api_msg *apimsg;struct netifapi_msg *netifapimsg;struct {struct pbuf *p;struct netif *netif;} inp; // inp结构体最重要,内含数据包内容结构、网络接口结构struct {tcpip_callback_fn function;void *ctx;} cb;struct {u32_t msecs;sys_timeout_handler h;void *arg;} tmo;}msg;}----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二. 网络层lwip使用一个ip_hdr的结构体来描述IP协议包头:struct ip_hdr{u16_t _v_hl_tos; // 包含4位版本号(IPv4 - 4,IPv6 - 6)、4位IP包头长(通常为5*4,即本结构体大小)、8位服务类型u16_t _len; // 整个IP数据包长度u16_t _id; // 16位标识用于标识IP层发出的每一份IP报文,自增u16_t _offset; // 包含3位标志和13位片偏移,IP数据包分片时使用u8_t _ttl; // TTL描述该IP数据包最多能被转发的次数,自减u8_t _proto; // 协议字段用于描述该IP数据包的上层协议,0x01 - ICMP,0x02 - IGMP,0x06 - TCP,0x17 - UDPu16_t _chksum; // 16位的IP首部校验和ip_addr_p_t src; // 源IPip_addr_p_t dest; // 目的IP}ip_input为IP层主干函数,完成了IP层数据包处理(核心工作就是IP地址匹配;得到完整数据包),然后将合适的数据包提交给上层,这里的p->payload已经越过了14字节包头,指向了IP头err_t ip_input(struct pbuf *p,struct netif *inp){struct ip_hdr *iphdr; // 指向IP包头的指针struct netif *netif; // 指向netif硬件网络接口设备描述符的指针u16_t iphdr_hlen; // IP包头的长度,通常是固定20字节u16_t iphdr_len; // 整个IP包长,包含IP包头、上层协议头、数据// 取出 IP数据包头iphdr = (struct ip_hdr *)p->payload;// 检查IP包头中的版本号字段,IPv4 - 4,IPv6 - 6if(IPH_V(iphdr) != 4){pbuf_free(p);return ERR_OK; }// 提取IP包头中的头长度字段,通常固定值20字节iphdr_hlen = IPH_HL(iphdr);iphdr_hlen *= 4;// 提取IP包头中的IP包总长度字段,确保小于递交上来的pbuf包中的总长度iphdr_len = ntohs(IPH_LEN(iphdr));if(iphdr_len > p->len || iphdr_len > p->tot_len){pbuf_free(p);return ERR_OK; }// 校验IP数据包头if (inet_chksum(iphdr, iphdr_hlen) != 0){pbuf_free(p);return ERR_OK; }// 对IP数据报进行截断,得到完整无冗余IP数据包pbuf_realloc(p, iphdr_len);// 遍历netif_list链表(系统存在2个网卡设备,意味着有2个netif分别用于描述它们,也意味着本机有2个IP地址,所以此时就需要遍历),检测IP数据包中的目的IP是否与本机相符,不符则丢弃或转发ip_addr_copy(current_iphdr_dest, iphdr->dest);ip_addr_copy(current_iphdr_src, iphdr->src);int first = 1;netif = inp;do{// 通过netif->flag标志位判断该网卡设备是否配置且使能,同时判断本机IP是否有效if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))){// 如果目的IP地址与本机IP地址匹配或者目的IP地址是广播类型,意味着成功匹配,退出遍历if(ip_addr_cmp(¤t_iphdr_dest, &(netif->ip_addr)) || ip_addr_isbroadcast(¤t_iphdr_dest, netif)) {break; }}if (first){first = 0;netif = netif_list;}else{netif = netif->next;}if (netif == inp){netif = netif->next;}}while(netif != NULL);// 如果该数据包中的源IP地址是广播IP,则直接丢弃if ((ip_addr_isbroadcast(¤t_iphdr_src, inp)) || (ip_addr_ismulticast(¤t_iphdr_src))){pbuf_free(p);return ERR_OK;}// 遍历完成以后,如果依旧没有找到匹配的netif结构体,说明该数据包不是给本机的,转发或丢弃(这里直接丢弃)if (netif == NULL){pbuf_free(p);return ERR_OK;}// 判断该IP包是否是分片数据包// 如果是分片数据包,则需要将该分片包暂存,等接收完所有分片包后,统一将整个数据包提交给上层if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0){// 在这里重组接收到的分片包,如果还没接收完整,p=NULLp = ip_reass(p);// 如果分片包还没接收完整,本函数结束if (p == NULL){return ERR_OK;}// 如果分片包接收完整,这时的p已经是一个完整的数据包结构体了// 再从p中获取完整的IP包iphdr = (struct ip_hdr *)p->payload; }// 能到达这一步的数据包必然是未分片的或经过分片重组完整后的数据包current_netif = inp;current_header = iphdr;if (raw_input(p, inp) == 0){// 根据IP数据包头中的协议字段判断该数据包应该被递交给上层哪个协议switch (IPH_PROTO(iphdr)){case IP_PROTO_UDP: // UDP协议udp_input(p, inp); // 从这里进入传输层,解析见下文break; case IP_PROTO_TCP: // TCP协议tcp_input(p, inp); // 从这里进入传输层,解析见下文break;case IP_PROTO_ICMP: // ICMP协议icmp_input(p, inp);break;case IP_PROTO_IGMP: // IGMP协议igmp_input(p, inp, ¤t_iphdr_dest);break;default: // 如果都不是// 如果不是广播数据包,返回一个协议不可达ICMP数据包给源主机if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp) && !ip_addr_ismulticast(¤t_iphdr_dest)){p->payload = iphdr;icmp_dest_unreach(p, ICMP_DUR_PROTO);} pbuf_free(p); } }current_netif = NULL;current_header = NULL;ip_addr_set_any(¤t_iphdr_src);ip_addr_set_any(¤t_iphdr_dest);}IP层的补充协议:ICMP、IGMP这时候主机A学到了主机B的MAC地址,就把这个MAC封装到ICMP协议中向主机B发送,报文格式如下:包头14字节 :因为ICMP协议包属于网络层协议,所以帧类型是0x0800+ ICMP协议头(主要是二级协议类型、源IP、目的IP) :二级协议类型ICMP对应值0x01+ ICMP协议主体(主要是一个类别) :类别取值0x00 - 这是一条回应信息 0x03 - 目的不可达 0x08 - 请求回应信息---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
lwip之数据收发流程_1相关推荐
- lwip之数据收发流程_3
// 只会被tcp_process函数调用,用于进一步完成对输入报文的处理,具体来说,该函数主要是完成输入报文的冗余截断,管理unacked.unsent.ooseq三张链表void tcp_rece ...
- lwip之数据收发流程_2
三.传输层(这里主要讲TCP)TCP连接的建立过程(三次握手):1. 客户端发送一个SYN标志置1的TCP数据报,握手包中指明源端口和目的端口,同时告知客户端初始序号seqno_client2. 当服 ...
- 【SemiDrive源码分析】【MailBox核间通信】46 - Android侧 RPMSG_IPCC_RPC驱动分析(下) 之 RPMSG_IPCC_RPC驱动初始化、数据收发流程分析
[SemiDrive源码分析][MailBox核间通信]46 - Android侧 RPMSG_IPCC_RPC驱动分析(下) 之 RPMSG_IPCC_RPC驱动初始化.数据收发流程分析 三. rp ...
- Linux: 网络数据收发流程简析
文章目录 1. 前言 2. 背景 3. 网卡数据收发流程 3.1 网络数据接收流程 3.1.1 网卡数据接收流程 3.1.2 网卡数据向上传递给L3,L4的流程 3.2 网卡数据发送流程 1. 前言 ...
- linux内核源码分析之网络数据收发流程
目录 一.TCP/IP 模型与 ISO模型 二.内核中分层模型的结构 三.数据帧的封装 四.协议栈收发包流程 1.网络包接收流程 2.网络包发送流程 一.TCP/IP 模型与 ISO模型 内核中使用的 ...
- dpdk报文收发流程--理解dma控制器、UIO驱动、描述符空间、mbuf空间、KNI
1. dpdk报文收发流程 1.1 报文接收流程 传统方式接收报文时,当网卡接收到报文后会产生硬件中断,进而报文会通过协议栈,最后到达应用层,这个过程需要内核协议栈的处理. 和传统报文接收不同,当应用 ...
- ZYNQ裸机跑LWIP,频繁收发,时间一长就会死机问题解决
ZYNQ裸机跑LWIP,频繁收发,时间一长就会死机问题解决 问题描述: 前阵子已经调试好数据收发,在去老化跑硬件的时候,发现数据频繁收发久了会死机,当时第一反应就是lwip的内存泄漏,或者指针跑飞,但 ...
- STM32CubeMX+STM32F407+FreeRTos+LAN8720 以太网通信实现数据收发功能
目录 前言 一.STM32CubeMX配置 二.修改代码 三.硬件测试 总结 前言 该工程应用的以太网芯片是LAN8720,代码是基于STM32CUbeMx6.2.1配置生成的,在CubeMx中配置了 ...
- 2021年大数据HBase(十三):HBase读取和存储数据的流程
全网最详细的大数据HBase文章系列,强烈建议收藏加关注! 新文章都已经列出历史文章目录,帮助大家回顾前面的知识重点. 目录 系列历史文章 HBase读取和存储数据的流程 一.HBase读取数据的流程 ...
最新文章
- 破除SCI至上!评价 AI 科研成果,需要新思路【附报告下载】
- Spring Boot中使用自定义Annotation来实现接口自动幂等
- 五百字简文告诉你美国为何要倾一国之力对中国的一家民营企业华为痛下杀手?...
- IBM中国开发中心吉燕勇: 通过Cloud Data Services打造新型认知计算数据分析云平台...
- Qt在VS2010的安装与配置
- 调试Angular指令实现时一个有用的内部属性:__ngContext__
- Exchange 2010 DAG转载
- 今天,全网曝光这几个公众号
- 128.最长连续序列
- opencv-api minAreaRect
- Oracle中各个命中率的总结及调优笔记整理
- aspiration定义_志愿者的定义、理念与精神核心
- .set伪指令(mips)
- 信号与系统分析中的复变函数
- .NET 针对465加密端口 加密协议SSL(Implicit SSL)进行的邮件发送
- 华为MA5626 ONU配置成交换机及开启POE指令教程
- 不等号属于不等式吗_什么是不等式
- Nginx安装及常用优化和配置
- jquery保留两位小数方法,字符串保留两位小数
- Java选择题(八)