IP碎片重組過程分析

2021-06-04 02:46:59 字數 3363 閱讀 1134

1. 前言

對ip碎片的重組是防火牆提高安全性的乙個重要手段,通過提前進行碎片重組,可以有效防禦各種碎片攻擊,linux核心的防火牆netfilter就自動對ip碎片包進行了重組,本文介紹linux核心中的ip重組過程,核心**版本2.4.26。

2. 處理流程

實現ip重組的基本函式為ip_defrag(),在net/ipv4/ip_fragment.c中實現,基本過程是建立碎片處理佇列,佇列中每個節點是乙個鍊錶,這個鍊錶儲存同乙個連線的碎片,當碎片都到達之後進行資料報重組,或者在一定時間(預設30秒)內所有碎片包不能到達而釋放掉。

2.1 資料結構

在處理分片包時,將skb包的cb欄位儲存碎片控制資訊struct ipfrag_skb_cb。

#define frag_cb(skb) ((struct ipfrag_skb_cb*)((skb)->cb))

struct ipfrag_skb_cb

;ipq佇列節點結構:

/* describe an entry in the "incomplete datagrams" queue. */

struct ipq ;

2.2 ip_defrag()函式:

這是進行碎片重組的基本函式,返回重組後的skb包,或者返回null。

struct sk_buff *ip_defrag(struct sk_buff *skb)

// 找不到相關節點,丟棄該資料報

ip_inc_stats_bh(ipreasmfails);

kfree_skb(skb);

return null;

}2.3 ip_find()函式

ip_find()函式用於查詢符合資料報的源、目的位址、協議和id的佇列節點,找到後返回,如果找不到,則新建乙個節點:

static inline struct ipq *ip_find(struct iphdr *iph)

}read_unlock(&ipfrag_lock);

// 如果不存在,新建佇列節點

return ip_frag_create(hash, iph);

}ip_frag_create()函式,返回乙個碎片佇列節點

static struct ipq *ip_frag_create(unsigned hash, struct iphdr *iph)

2.4 ip_frag_queue()函式

ip_frag_queue()函式將新來的skb包插入佇列節點中,這個函式是防禦各種碎片攻擊的關鍵,要能處理各種異常的重組過程:

// ping of death, teardrop等就是靠異常的碎片偏移來進行攻擊,因此需要仔細檢查

// 是否碎片偏移是否異常

static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)

else

if (end > qp->len)

}if (end == offset)

goto err;

// 去掉ip頭部分,只保留資料部分

if (pskb_pull(skb, ihl) == null)

goto err;

// 調整碎片節點在最近使用佇列中的位置,在儲存區超過限值時先釋放的是最老的未用的

// 那些碎片

list_move_tail(&qp->lru_list, &ipq_lru_list);

write_unlock(&ipfrag_lock);

return;

err:

// 出錯時直接丟棄資料報,但佇列中已有的不釋放,如果重組失敗是等超時或

// 超過碎片記憶體限值上限時釋放

kfree_skb(skb);

}2.5 ip_frag_reasm()函式

ip_frag_reasm()函式實現最終的資料重組過程,是在所有資料都正確接收後進行

static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)

skb_shinfo(head)->frag_list = head->next;

skb_push(head, head->data - head->nh.raw);

atomic_sub(head->truesize, &ip_frag_mem);

// 依次將所有後續包資料長度累加,並將其長度從分配的記憶體計數中刪除

for (fp=head->next; fp; fp = fp->next)

head->next = null;

head->dev = dev;

head->stamp = qp->stamp;

// 對ip頭中的長度和偏移標誌進行重置

iph = head->nh.iph;

iph->frag_off = 0;

iph->tot_len = htons(len);

ip_inc_stats_bh(ipreasmoks);

// 各碎片skb已經得到處理,在釋放qp時將不再重新釋放了

qp->fragments = null;

return head;

out_nomem:

netdebug(if (net_ratelimit())

printk(kern_err

"ip: queue_glue: no memory for gluing queue %p\n",

qp));

goto out_fail;

out_oversize:

if (net_ratelimit())

printk(kern_info

"oversized ip packet from %d.%d.%d.%d.\n",

nipquad(qp->saddr));

out_fail:

ip_inc_stats_bh(ipreasmfails);

return null;

}2.6 ipq的釋放

重組完成後就要將碎片佇列釋放掉:

static __inline__ void ipq_put(struct ipq *ipq)

/* complete destruction of ipq. */

static void ip_frag_destroy(struct ipq *qp)

/* finally, release the queue descriptor itself. */

// 釋放碎片節點本身

frag_free_queue(qp);

}3. 結論

linux的ip碎片重組過程中考慮了多種可能的異常,具有較大的安全性,因此在資料報進入netfilter架構前進行資料報的重組就可以防禦各類碎片攻擊。

IP分片重組

libnids ip重組 收藏 1 ip分片 任何ip層接收到乙份要傳送的ip資料報時,它要判斷向本地哪個介面傳送資料,並查詢該介面的mtu。ip把mtu與資料報的長度進行比較,如果需要則進行分片。分片可以發生在原始傳送端主機上,也可以傳送在中間路由器上。ip資料報分片後,只有到達目的主機後才進行重...

IP分片與重組

物理網路層一般要限制每次傳送的資料幀的最大長度,當ip層接收到乙份要傳送的ip資料報時,它要判斷向本地哪個介面傳送資料,並查詢該介面獲得其mtu。ip把mtu與資料報長度進行比較,如果資料報大於mtu則進行分片。分片可以發生在原始傳送端主機上,也可以發生在中間路由器上。mtu指定了網路中可傳輸資料報...

分片報文的最後一片 IP分片(碎片)重組簡單概念

鏈路層具有最大傳輸單元mtu這個特性,它限制了資料幀的最大長度,不同的網路型別都有乙個上限值。乙太網的mtu是1500,可以用 netstat i 命令檢視這個值。如果ip層有資料報要傳,而且資料報的長度超過了mtu,那麼ip層就要對資料報進行分片 fragmentation 操作,使每一片的長度都...