712
技術社區[雲棲]
網絡子係統55_ip協議分片重組_加入ipq
//ip分片加入到正確的ipq結構
//調用路徑:ip_defrag->ip_frag_queue
// 處理過程:
// 1.正在被釋放的ipq,不處理新加入的分片(ipq正在被釋放由last_in設置COMPLETE指出)
// 2.處理分片的合法性
// 2.1當該封包為最後一個分片時
// 2.1.1如果之前沒有接收到最後一個分片,則該分片在總有效載荷中的結尾位置需要大於等於以推測出的最大長度
// 2.1.2如果之前已經接收到最後一個分片,則該分片在總有效載荷中的結尾位置需要等於之前接收到的最後一個分片給出的結尾位置
// 2.2結尾位置對齊到8字節邊界,截去多餘的字節,希望後續到達的分片補齊
// 3.更新該分片的skb->data移動到ip有效載荷,skb->tail到8字節
// 4.處理重疊
// 5.將分片插入到ipq的分片列表中
// 6.更新ipq的時間戳,移動ipq到rcu鏈表尾部
// 重疊的處理:
// 1.一個分片最多隻會與一個前邊的分片發生重疊,此時,截去該分片發生重疊的部分
// 2.一個分片可能會與多個後邊的分片發生重疊,此時
// 2.1 如果後邊的一個分片完全被重疊,則釋放後邊的這個分片
// 2.2 如果後邊的這個分片隻有部分被重疊,則從後邊的這個分片中截去重疊的部分
// 3.使被截去緩存區的skb的校驗和失效
// 插入ipq->fragments中:
// sk_buff通過next域,插入到ipq->fragments中
1.1 static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
{
struct sk_buff *prev, *next;
int flags, offset;
int ihl, end;
//ipq正在被釋放
if (qp->last_in & COMPLETE)
goto err;
//此ip分片(ip有效載荷)在所有有效載荷中的偏移量
offset = ntohs(skb->nh.iph->frag_off);
flags = offset & ~IP_OFFSET;//DF MF標誌
offset &= IP_OFFSET;////偏移量為13bit的字段,去掉高3bit的flag
offset <<= 3; //偏移量以8字節為單位
ihl = skb->nh.iph->ihl * 4;
end = offset + skb->len - ihl;//該ip分片的有效載荷相對於總有效載荷的結束位置
//接收到最後一個分片
if ((flags & IP_MF) == 0) {
//1.該分片指示的總有效載荷大小不足從已接收到的報文推斷出的長度
if (end < qp->len ||
((qp->last_in & LAST_IN) && end != qp->len))//2.已經接收到最後一個分片,又接收到一個最後一個分片,但是長度不等
goto err;
qp->last_in |= LAST_IN;//LAST_IN表示已經接收到最後一個分片
qp->len = end;//總有效載荷的長度
} else {//非最後一個分片
if (end&7) {//結尾位置沒有對齊到8字節
end &= ~7;//去掉結尾的字節,希望後來的數據補齊
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
skb->ip_summed = CHECKSUM_NONE;//由於截去了末尾的字節,因此表示校驗和失效
}
if (end > qp->len) {//非最後一個分片,但是長度超過了總有效載荷長度
if (qp->last_in & LAST_IN)
goto err;//有錯誤
qp->len = end;//由於新接收到的分片其結尾字節位置大於最大的結尾位置,更新最大的結尾位置
}
}
if (end == offset)//長度為0分片的
goto err;
//pskb_pull 與 skb_pull的區別:
// 1.skb_pull隻簡單移動 skb->data的指針
// 2.pskb_pull考慮當skb->data到skb->tail之間的數據量如果不足夠移動時,從frags或者frag_list中向前拷貝
if (pskb_pull(skb, ihl) == NULL)//更新skb->data,使其指向ip有效載荷
goto err;
if (pskb_trim(skb, end-offset))//更新skb->tail指針,使skb->data與skb->tail之間的數據量為end-offset
goto err;
//在ipq->fragments中尋找該分片前邊的分片
//一個分片前邊分片的滿足條件:
// 1.它的偏移量小於該分片的偏移量
// 2.它後邊分片的偏移量大於等於該分片的偏移量
prev = NULL;
for(next = qp->fragments; next != NULL; next = next->next) {
if (FRAG_CB(next)->offset >= offset)
break;
prev = next;
}
//已經接收到該分片前邊的分片
//該分片最多隻會與前邊的一個分片重疊
if (prev) {
int i = (FRAG_CB(prev)->offset + prev->len) - offset;
//該分片與前邊的分片存在重疊的部分
if (i > 0) {
offset += i;//更新該分片的offset
if (end <= offset)
goto err;
if (!pskb_pull(skb, i))//從該分片中刪除重疊的部分
goto err;
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
skb->ip_summed = CHECKSUM_NONE;//由於該分片長度被截取,因此校驗和失效
}
}
//接著之前的搜索,尋找該分片的後邊的分片
//該分片會與多個後邊的分片重疊
while (next && FRAG_CB(next)->offset < end) {
int i = end - FRAG_CB(next)->offset;//與後邊第一個分片重疊的字節數
//重疊的字節數小於後邊分片的長度,說明隻與後邊一個分片發生了重疊
if (i < next->len) {
//更新後邊分片的data指針,截取重疊的部分
if (!pskb_pull(next, i))
goto err;
//更新後邊分片的偏移量
FRAG_CB(next)->offset += i;
//由於從後邊分片截取了重疊的部分,從已接收到的數據量減去這部分字節
qp->meat -= i;
if (next->ip_summed != CHECKSUM_UNNECESSARY)
next->ip_summed = CHECKSUM_NONE;//由於截取了後邊的這個分片,使其校驗和失效
break;
} else {//否則完全包含了後邊的這個分片, 則直接釋放掉被重疊的這個分片
struct sk_buff *free_it = next;
next = next->next;
if (prev)
prev->next = next;
else
qp->fragments = next;
qp->meat -= free_it->len;
frag_kfree_skb(free_it, NULL);
}
}
//強製轉換skb->cb為分片控製塊
//經過與前邊,後邊的分片比較,最終確定該分片的偏移量
//設置該分片的偏移量
FRAG_CB(skb)->offset = offset;
//將分片添加到ipq的fragments鏈表中
skb->next = next;
if (prev)
prev->next = skb;
else
qp->fragments = skb;//此分片為第一個分片
if (skb->dev)
qp->iif = skb->dev->ifindex;
skb->dev = NULL;
qp->stamp = skb->stamp;//更新ipq的時間戳為最新收到的這個skb的時間戳
qp->meat += skb->len;
//skb->truesize為skb中所有緩存區占用的總大小
atomic_add(skb->truesize, &ip_frag_mem);//增加分片子係統使用的內存量
if (offset == 0)
qp->last_in |= FIRST_IN;
write_lock(&ipfrag_lock);
list_move_tail(&qp->lru_list, &ipq_lru_list);
write_unlock(&ipfrag_lock);
return;
err:
kfree_skb(skb);
}
最後更新:2017-04-03 14:53:41