lwip之IP(二)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

lwip之IP(二)
1、ip数据包输出
(1)输出ip报文
err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, u8_t ttl, u8_t tos, u8_t proto)
{
struct netif *netif;
//查找合适的网卡接口,即ip地址在同一网段内
if ((netif = ip_route(dest)) == NULL) {
return ERR_RTE;
}
return ip_output_if(p, src, dest, ttl, tos, proto, netif);
}
//填充IP报文头部字节,并判断是否需要分片,最后发送至链路层
err_t ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
u8_t ttl, u8_t tos,
u8_t proto, struct netif *netif)
{
struct ip_hdr *iphdr;
ip_addr_t dest_addr;
if (dest != IP_HDRINCL) {
u16_t ip_hlen = IP_HLEN;
if (pbuf_header(p, IP_HLEN)) {
return ERR_BUF;
}
iphdr = (struct ip_hdr *)p->payload;
IPH_TTL_SET(iphdr, ttl);
IPH_PROTO_SET(iphdr, proto);
ip_addr_copy(iphdr->dest, *dest);
IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
IPH_TOS_SET(iphdr, tos);
IPH_LEN_SET(iphdr, htons(p->tot_len));
IPH_OFFSET_SET(iphdr, 0);
IPH_ID_SET(iphdr, htons(ip_id));
++ip_id;
if (ip_addr_isany(src)) {
ip_addr_copy(iphdr->src, netif->ip_addr);
} else {
ip_addr_copy(iphdr->src, *src);
}
IPH_CHKSUM_SET(iphdr, 0);
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen)); } else {
/* IP header already included in p */
iphdr = (struct ip_hdr *)p->payload;
ip_addr_copy(dest_addr, iphdr->dest);
dest = &dest_addr;
}
#if IP_FRAG
//判断是否分片
if (netif->mtu && (p->tot_len > netif->mtu)) {
return ip_frag(p, netif, dest);
}
#endif /* IP_FRAG */
return netif->output(netif, p, dest);
}
(2)ip报文分片机制
分片条件:
if (netif->mtu && (p->tot_len > netif->mtu)) {
return ip_frag(p, netif, dest);
}
分片函数ip_frag
//静态分片数组定义
static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
err_t ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest) {
struct pbuf *rambuf;
struct pbuf *header;
struct ip_hdr *iphdr;
u16_t nfb;
u16_t left, cop;
u16_t mtu = netif->mtu;
u16_t ofo, omf;
u16_t last;
u16_t poff = IP_HLEN;
u16_t tmp;
//pbuf类型为PBUF_REF
rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
if (rambuf == NULL) {
return ERR_MEM;
}
rambuf->tot_len = rambuf->len = mtu;
rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
iphdr = (struct ip_hdr *)rambuf->payload; SMEMCPY(iphdr, p->payload, IP_HLEN);
tmp = ntohs(IPH_OFFSET(iphdr));
ofo = tmp & IP_OFFMASK;
omf = tmp & IP_MF;
left = p->tot_len - IP_HLEN;
nfb = (mtu - IP_HLEN) / 8;
while (left) {
last = (left <= mtu - IP_HLEN);
tmp = omf | (IP_OFFMASK & (ofo));
if (!last) {
tmp = tmp | IP_MF;
}
/* Fill this fragment */
cop = last ? left : nfb * 8;
poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
/* Correct header */
IPH_OFFSET_SET(iphdr, htons(tmp));
IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
IPH_CHKSUM_SET(iphdr, 0);
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
if (last) {
pbuf_realloc(rambuf, left + IP_HLEN);
}
//申请一个PBUF_RAM型的pbuf,以便以太网帧头部预留空间header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
if (header != NULL) {
pbuf_chain(header, rambuf); //连接两个pbuf
netif->output(netif, header, dest);
pbuf_free(header);
} else {
pbuf_free(rambuf);
return ERR_MEM;
}
left -= cop;
ofo += nfb;
}
pbuf_free(rambuf);
return ERR_OK;
}
2、ip报文输入
(1)ip_input()
该函数的作用是首先判别ip地址是否给本地网口,其次判别该ip 报文是否为分片报文,最后将该报文传输至上次协议。

err_t ip_input(struct pbuf *p, struct netif *inp)
{
struct ip_hdr *iphdr;
struct netif *netif;
u16_t iphdr_hlen;
u16_t iphdr_len;
//检查IP报文的合法性
iphdr = (struct ip_hdr *)p->payload;
if (IPH_V(iphdr) != 4) {
pbuf_free(p);
return ERR_OK;
}
iphdr_hlen = IPH_HL(iphdr);
iphdr_hlen *= 4; //ip报文的首部长度
iphdr_len = ntohs(IPH_LEN(iphdr)); //ip报文的总长度
//检查pbuf的长度是否小于IP报文的长度,pbuf链表第一个buf 长度是否大于ip首部长度
if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) {
pbuf_free(p);
return ERR_OK;
}
//检查ip报文校验和
if (inet_chksum(iphdr, iphdr_hlen) != 0) {
return ERR_OK;
}
//删除多余的填充字节
pbuf_realloc(p, iphdr_len);
ip_addr_copy(current_iphdr_dest, iphdr->dest);
ip_addr_copy(current_iphdr_src, iphdr->src);
{
//检查网络接口ip是否与ip地址ip相同
int first = 1;
netif = inp;
do {
if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) {
if (ip_addr_cmp(&current_iphdr_dest, &(netif->ip_addr)) ||
ip_addr_isbroadcast(&current_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地址是否为多播包和广播包,若是则丢弃
{ if ((ip_addr_isbroadcast(&current_iphdr_src, inp)) || (ip_addr_ismulticast(&current_iphdr_src))) {
pbuf_free(p);
return ERR_OK;
}
}
//ip地址与本地网络接口ip不相同,则转发出去
if (netif == NULL) {
if (!ip_addr_isbroadcast(&current_iphdr_dest, inp)) { ip_forward(p, iphdr, inp); //转发数据包
} else
{
snmp_inc_ipinaddrerrors();
snmp_inc_ipindiscards();
}
pbuf_free(p);
return ERR_OK;
}
//检查IP报文的偏移量是否为0,不为0则是分片报文
if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
p = ip_reass(p); //重装分片ip报文
if (p == NULL) {
return ERR_OK;
}
iphdr = (struct ip_hdr *)p->payload;
}
current_netif = inp;
current_header = iphdr;
//根据上层协议不同,传输报文至上层协议
switch (IPH_PROTO(iphdr)) {
#if LWIP_UDP
case IP_PROTO_UDP:
udp_input(p, inp);
break;
#endif /* LWIP_UDP */
#if LWIP_TCP
case IP_PROTO_TCP:
snmp_inc_ipindelivers();
tcp_input(p, inp);
break;
#endif /* LWIP_TCP */
#if LWIP_ICMP
case IP_PROTO_ICMP:
snmp_inc_ipindelivers();
icmp_input(p, inp);
break;
#endif /* LWIP_ICMP */
#if LWIP_IGMP
case IP_PROTO_IGMP:
igmp_input(p, inp, &current_iphdr_dest);
break;
#endif /* LWIP_IGMP */
default:
#if LWIP_ICMP
/* send ICMP destination protocol unreachable unless is was a broadcast */
if (!ip_addr_isbroadcast(&current_iphdr_dest, inp) &&
!ip_addr_ismulticast(&current_iphdr_dest)) {
p->payload = iphdr;
icmp_dest_unreach(p, ICMP_DUR_PROTO);
}
#endif /* LWIP_ICMP */
pbuf_free(p);
}
}
current_netif = NULL;
current_header = NULL;
ip_addr_set_any(&current_iphdr_src);
ip_addr_set_any(&current_iphdr_dest);
return ERR_OK;
}
(2)ip_reass函数
1、结构体声明
struct ip_reassdata {
struct ip_reassdata *next;
struct pbuf *p; //该数据报的数据链表
struct ip_hdr iphdr; //该数据报的ip首部
u16_t datagram_len; //已收到数据报的长度
u8_t flags; //是否收到最后一个分片
u8_t timer; //设置超时间隔
};
//ip报文pbuf连接帮助结构体
PACK_STRUCT_BEGIN
struct ip_reass_helper {
PACK_STRUCT_FIELD(struct pbuf *next_pbuf); PACK_STRUCT_FIELD(u16_t start);
PACK_STRUCT_FIELD(u16_t end);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
2、ip_reass
struct pbuf *ip_reass(struct pbuf *p)
{
struct pbuf *r;
struct ip_hdr *fraghdr;
struct ip_reassdata *ipr;
struct ip_reass_helper *iprh;
u16_t offset, len;
u8_t clen;
struct ip_reassdata *ipr_prev = NULL;
fraghdr = (struct ip_hdr*)p->payload;
if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
goto nullreturn;
}
//确定ip报文的偏移字节,分片ip报文的数据长度
offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
/* Check if we are allowed to enqueue more datagrams. */ clen = pbuf_clen(p);
if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { #if IP_REASS_FREE_OLDEST
if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
#endif /* IP_REASS_FREE_OLDEST */
{
goto nullreturn;
}
}
//查找ip_reassdata重装数据包链表,检查该报文属于哪个ip_reassdata
for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
break;
}
ipr_prev = ipr;
}
if (ipr == NULL) {
//若ip_reassdata链表中无此报文的重装结构体,则新建一个ip_reassdata结构体
ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
if(ipr == NULL) {
goto nullreturn;
}
} else {
//此分片报文为ip_reassdata结构的第一个pbuf,则复制ip报文头部到找到ipr
if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
}
}
//检查分片ip报文为ip_reassdata的最后一个pbuf,即IP_MF = 0
if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
ipr->flags |= IP_REASS_FLAG_LASTFRAG;
ipr->datagram_len = offset + len;//若为最后一个报文,确定整个ip报文的的数据长度
}
//分片ip报文插入与检查
if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { ipr->datagram_len += IP_HLEN;
处理除第一个pbuf以外的pbuf头部
r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
/* copy the original ip header back to the first pbuf */
fraghdr = (struct ip_hdr*)(ipr->p->payload);
SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
IPH_OFFSET_SET(fraghdr, 0);
IPH_CHKSUM_SET(fraghdr, 0);
IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
p = ipr->p;
/* chain together the pbufs contained within the reass_data list. */
while(r != NULL) {
iprh = (struct ip_reass_helper*)r->payload;
/* hide the ip header for every succeding fragment */
pbuf_header(r, -IP_HLEN);
pbuf_cat(p, r);
r = iprh->next_pbuf;
}
//删除已经打包好ip报文的ip_reassdata
ip_reass_dequeue_datagram(ipr, ipr_prev);
//更改全局变量的pbuf个数
ip_reass_pbufcount -= pbuf_clen(p);
//返回打包好的pbuf
return p;
}
return NULL;
nullreturn:
pbuf_free(p);
return NULL;
}
3、分片插入
正对ip_reassdata链表上的某一个ip_reassdata 进行pbuf数据的插入排序和验证ip_reassdata是否均接收到。

static int ip_reass_chain_frag_into_datagram_and_validate(struct
ip_reassdata *ipr, struct pbuf *new_p)
{
struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
struct pbuf *q;
u16_t offset,len;
struct ip_hdr *fraghdr;
int valid = 1;
fraghdr = (struct ip_hdr*)new_p->payload;
len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
iprh = (struct ip_reass_helper*)new_p->payload; iprh->next_pbuf = NULL;
iprh->start = offset;
iprh->end = offset + len;
//轮询整个ipr->p链表,并进行插入操作
for (q = ipr->p; q != NULL;) {
iprh_tmp = (struct ip_reass_helper*)q->payload; if (iprh->start < iprh_tmp->start) {
iprh->next_pbuf = q;
if (iprh_prev != NULL) {
iprh_prev->next_pbuf = new_p;
} else {
/* fragment with the lowest offset */
ipr->p = new_p;
}
break;
} else if(iprh->start == iprh_tmp->start) {
goto freepbuf;
} else {
if (iprh_prev != NULL) {
if (iprh_prev->end != iprh_tmp->start) {
valid = 0;
}
}
}
q = iprh_tmp->next_pbuf;
iprh_prev = iprh_tmp;
}
if (q == NULL) {
if (iprh_prev != NULL) {
iprh_prev->next_pbuf = new_p;
if (iprh_prev->end != iprh->start) {
valid = 0;
}
} else {
ipr->p = new_p;
}
}
if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
if (valid) {
if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) { valid = 0;
} else {
/* and check that there are no wholes after this datagram */ iprh_prev = iprh;
q = iprh->next_pbuf;
while (q != NULL) {
iprh = (struct ip_reass_helper*)q->payload;
if (iprh_prev->end != iprh->start) {
valid = 0;
break;
}
iprh_prev = iprh;
q = iprh->next_pbuf;
}
}
return valid;
}
return 0; /* not yet valid! */ }。

相关文档
最新文档