关于LWIP的一点记录(二)

编程入门 行业动态 更新时间:2024-10-18 03:35:32

关于<a href=https://www.elefans.com/category/jswz/34/1731451.html style=LWIP的一点记录(二)"/>

关于LWIP的一点记录(二)

网络数据包(pbuf)
当数据在各层之间传递时,LWIP极力避免数据的copy
struct pbuf{

struct pbuf* next;//形成pbuf链
void *payload;	  //数据指针,可能指向pbuf的RAM,也可能ROM,但并未指向数据区起址,而是留了一个offset,用来填充TCP,IP首部等
u16_t len,tot_len,ref;//数据总长,剩余长度(包括本pbuf),被引用次数
u8_t type,flags;	//4种类型之一,

}
四类pbuf:
typedef enum{

PBUF_RAM,//内存堆
PBUF_ROM,
PBUF_REF,
PBUF_POOL//内存池

}pbuf_type;

PBUF_RAM类型:从代码看,并没有用链表,而是直接一块装下(貌似符合内存堆分配的意思),maybe一般不装太大的数据?
内存池由于初始化已经搞定了结构与大小,所以分配速度比内存堆快(内存堆要在链上找、比较、截取、标记之类的)
系统初始化内存池时,会搞两类POOL:
MEMP_PBUF_POOL(包含pbuf结构和590B的TCP包空间):申请PBUF_POOL类型时,协议栈根据所需大小分配一个或多个MEMP_PBUF_POOL;
MEMP_PBUF:主要用于分配给PBUF_ROM和PBUF_REF类型,仅有一个pbuf结构,没有数据区,当payload指向ROM时为PBUF_ROM类型,指向RAM时为PBUF_REF类型
so,PBUF_RAM类型对应内存堆分配,其他三种对应内存池分配?
大数据包形成pbuf链时,只需第一个节点的offset留出首部空间就行了,后面节点的payload可以直接指向自己的数据区
以上四种类型可以通过链表自由组合(maybe)
另:PBUF_REF和PBUF_ROM的payload需要用户自己指定

数据包pbuf申请的俩参数:类型和层次(TRANSPORT、IP、LINK、RAW)
分成传输、网络、链路、原始四个层次是因为要填充本层的东西(调整offset),
但从程序上来讲,可以参考 netif->ARP->IP->TCP/UDP->应用层 这样的数据流向来分层分析

pbuf_free:从给定的节点开始ref-1,判断是0则删除,看下一个,不是0则停止
所以给pbuf_free的节点一定应当是要删除数据包的首节点,或者是被其他地方引用过的节点(更新ref),否则会出问题

网络接口(netif):代表一个实际的物理网卡,包括IP、MAC、输入输出函数、状态等,所有的netif形成一条单链,用于后面查找匹配
其中还包含loop_first和loop_last指针,作为本地回环数据链的头尾指针,本地回环的pbuf被挂到这个链上,
通过周期性调用netif_poll在链上取数据递交给IP层,不会实际经过网卡
etharp_raw从内存堆申请pbuf并组装arp包(请求或者响应)
底层接到数据包后调用ethernet_input(注册在netif上的回调),根据以太网首部的帧类型判断,
if是IP数据包则交给etharp_ip_input(同时可以选择加入ARP缓存表);
if是ARP包(请求或响应),交给etharp_arp_input,比较目的IP看下是不是给我的,是的话加入缓存表,不是的话尽量加(没空位就算了),
再看下ARP包的操作码,如果是给我的且需要应答,则把拿到的这个包改动一下(填上mac等)再发回去
单播包发送流程:首先需要知道mac,所以调etharp_query去缓存表里找mac,返回一个表项索引,if状态是stable可以直接发;若为empty(表示是新建的表项无mac),则把状态置为pending并发ARP询问mac(如果之前已经是pending,说明还没拿到响应,也发一次ARP),如果单播包中有数据包的话,就先挂在pending后面(缓冲),等收到ARP响应状态变为stable后发送
etharp_find_entry根据IP寻找ARP表中对应的表项,没有时返回最小的empty表项,if表满了且要求替换时,顺序为:
最老的stable表项——>最老的无缓冲数据的pending表项——>最老的有数据缓冲的pending表项(这里看程序貌似是把后面的数据直接删了,等不到ARP也不考虑抢救一下了)
etharp_update_arp_entry负责维护arp缓存表,调etharp_find_entry返回表项后,设置stable、netif、ethaddr、年龄,如果后面挂着数据就发出去
据说PBUF_REF,POOL,RAM类型的pbuf需要先拷再挂,防止发送之前被别的地方改了 ———— so,ROM是可以直接挂,不会被改?
子网优点:合理分配不同子网主机数;分隔各网段通信量;方便管理
子网跟平时用的192.168不是一回事,子网中的主机是有唯一IP的
192.168这种局域网内部主机通过NAT路由器维护一个NAT路由转换表来实现与公网IP的通信
其中一种实现方式为端口多路复用:NAT路由器为每个内网与外网的连接对应一个唯一的端口,记录在NAT转换表中
缺点:需要使用端口号才行,所以只适用于TCP、UDP;转换过程需要改端口、校验等,效率下降一点

ip_output:首先使用ip_route寻找跟目的IP处于同一子网的netif,然后填充首部、TTL、地址等参数,
比较如果是本地回环地址则送给netif_loop_output,如果比mtu大,则送给ip_frag分片,然后调用注册在netif上的output发送

分片结构:一个RAM类型链接一个REF类型
ip_input:首先判断协议、各部分长度、校验和等,不对则丢弃;
然后根据目的地址遍历比较自己的netif链,看看有没有匹配的(从进来那个netif开始,因为可能性最大)———— 目的是校验ip数据报是不是给自己的
如果是给自己的或者是广播就可以准备重组分片ip_reass,然后向上递交 ———— 过滤数据不是应该在硬件层面由mac地址搞定吗?还是说mac过滤完软件还是要用IP再过滤一遍?
如果不是给自己的且非广播,则在ip_forward中根据目的IP重新找接口(在同一子网或者默认路由,类似ip_route),if找到的接口不是进来的那个,则转发
转发前TTL-1,if TTL = 0则返回超时ICMP ———— 像是个路由器哦
如果没有给出正确的协议类型,则返回不可达ICMP

关于分片的结构:
struct ip_reassdata{

struct ip_reassdata * next;	//形成单链
struct pbuf * p;
struct ip_hdr iphdr;
u16_t datagram_len;			//已收到数据长度
u8_t flags;					//是否收到最后一片
u8_t timer;					//相当于TTL,减到0时干掉本条重装链

}
所有链上的pbuf总数有个IP_REASS_MAX_PBUFS(默认10)的上限,古老版本没有分片和重组功能
ip_reass_chain_frag_into_datagram_and_validate:每拿到一个分片调用一次
start = offset,end = offset + len,之间就是本分片在整个数据报中的范围,比较范围并插入,重叠时删除

如前所述,LWIP可发送超时和目的不可达的ICMP,对于差错报文则直接丢弃
重定向:路由器发现由别人转发更好,用ICMP告诉发报人
5种查询报文中,LWIP只实现了一种:回送请求或回答(ping使用,为诊断通信)
ICMP攻击:大量ICMP回送请求耗尽目标主机的带宽和资源

更多推荐

关于LWIP的一点记录(二)

本文发布于:2024-03-12 11:48:28,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1731443.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:LWIP

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!