1、ip數(shù)據(jù)包輸出
(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;
//查找合適的網(wǎng)卡接口,即ip地址在同一網(wǎng)段內(nèi)
if ((netif = ip_route(dest)) == NULL) {
return ERR_RTE;
}
return ip_output_if(p, src, dest, ttl, tos, proto, netif);
}
//填充IP報文頭部字節(jié),并判斷是否需要分片,最后發(fā)送至鏈路層
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);
}
分片函數(shù)ip_frag
//靜態(tài)分片數(shù)組定義
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,以便以太網(wǎng)幀頭部預(yù)留空間
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()
該函數(shù)的作用是首先判別ip地址是否給本地網(wǎng)口,其次判別該ip報文是否為分片報文,最后將該報文傳輸至上次協(xié)議。
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;
}
//刪除多余的填充字節(jié)
pbuf_realloc(p, iphdr_len);
ip_addr_copy(current_iphdr_dest, iphdr->dest);
ip_addr_copy(current_iphdr_src, iphdr->src);
{
//檢查網(wǎng)絡(luò)接口ip是否與ip地址ip相同
int first = 1;
netif = inp;
do {
if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) {
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地址是否為多播包和廣播包,若是則丟棄
{ if ((ip_addr_isbroadcast(¤t_iphdr_src, inp)) ||
(ip_addr_ismulticast(¤t_iphdr_src))) {
pbuf_free(p);
return ERR_OK;
}
}
//ip地址與本地網(wǎng)絡(luò)接口ip不相同,則轉(zhuǎn)發(fā)出去
if (netif == NULL) {
if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp)) {
ip_forward(p, iphdr, inp); //轉(zhuǎn)發(fā)數(shù)據(jù)包
} 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;
//根據(jù)上層協(xié)議不同,傳輸報文至上層協(xié)議
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, ¤t_iphdr_dest);
break;
#endif /* LWIP_IGMP */
default:
#if LWIP_ICMP
/* send ICMP destination protocol unreachable unless is was a broadcast */
if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp) &&
!ip_addr_ismulticast(¤t_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(¤t_iphdr_src);
ip_addr_set_any(¤t_iphdr_dest);
return ERR_OK;
}
(2)ip_reass函數(shù)
1、結(jié)構(gòu)體聲明
struct ip_reassdata {
struct ip_reassdata *next;
struct pbuf *p; //該數(shù)據(jù)報的數(shù)據(jù)鏈表
struct ip_hdr iphdr; //該數(shù)據(jù)報的ip首部
u16_t datagram_len; //已收到數(shù)據(jù)報的長度
u8_t flags; //是否收到最后一個分片
u8_t timer; //設(shè)置超時間隔
};
//ip報文pbuf連接幫助結(jié)構(gòu)體
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報文的偏移字節(jié),分片ip報文的數(shù)據(jù)長度
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重裝數(shù)據(jù)包鏈表,檢查該報文屬于哪個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鏈表中無此報文的重裝結(jié)構(gòu)體,則新建一個ip_reassdata結(jié)構(gòu)體
ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
if(ipr == NULL) {
goto nullreturn;
}
} else {
//此分片報文為ip_reassdata結(jié)構(gòu)的第一個pbuf,則復(fù)制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報文的的數(shù)據(jù)長度
}
//分片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;
}
//刪除已經(jīng)打包好ip報文的ip_reassdata
ip_reass_dequeue_datagram(ipr, ipr_prev);
//更改全局變量的pbuf個數(shù)
ip_reass_pbufcount -= pbuf_clen(p);
//返回打包好的pbuf
return p;
}
return NULL;
nullreturn:
pbuf_free(p);
return NULL;
}
3、分片插入
正對ip_reassdata鏈表上的某一個ip_reassdata 進行pbuf數(shù)據(jù)的插入排序和驗證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! */
}
|