关于LWIP的一点记录(二)

本文详细介绍了LWIP轻量级网络库中的数据包pbuf结构及其四种类型,强调了在数据传递中避免复制的策略。讲解了内存堆与内存池的使用场景,以及PBUF_RAM、PBUF_ROM、PBUF_REF和PBUF_POOL的区别。此外,还阐述了网络接口(netif)的作用,包括本地回环数据链和 ARP 协议处理。最后,探讨了IP分片、重组、ICMP错误报文处理以及子网与NAT路由器的工作原理。
摘要由CSDN通过智能技术生成

网络数据包(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回送请求耗尽目标主机的带宽和资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值