ip層本機接受資料包處理
當資料包的目的地址是本機是,Ip_rcv_finish函式就會將skb->dst->input函式指標初始化為ip_local_deliver,ip層本地傳送資料包也分為兩個階段分配分別有兩個處理函式:ip_local_deliver和ip_local_deliver_finish。本地轉發資料包的首要任務是重組資料包,前送的資料包可以不要重組,前送可以轉發每個分片資料包。
一、本地重組資料包
ip_local_deliver函式主要的任務是重組ip資料包,重組資料包呼叫ip_defrag函式完成,ip_defrag重組資料包完畢後返回完整資料包指標,如果還沒有收到資料包的所有分片,資料包還不完整就返回NULL。重組資料包成功後呼叫網路過濾子系統的INPUT鏈上的鉤子函式對資料包做過濾處理,過濾處理完成就呼叫ip_local_deliver_finish函式繼續處理。
int ip_local_deliver(struct sk_buff *skb) { /* * Reassemble IP fragments. */ if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) { //重組資料包,如果資料包還不完整返回NULL if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER)) return 0; } //資料包重組完成,呼叫防火牆上INPUT鏈上的鉤子處理函式 //網路過濾子系統處理完成就呼叫ip_local_deliver_finish繼續處理 return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL, ip_local_deliver_finish); }
二、資料包從IP上傳到傳輸層
將資料包從IP層上傳給傳輸層的處理函式的ip_local_deliver_finish函式,它的主要任務有:
a、將資料包傳給正確的上層協議處理函式
b、將資料包傳給裸IP
c、對資料包進行安全策略檢查
(1)、傳輸層協議
在IP協議頭中protocol資料域佔了8位,所以傳輸層最多支援256種協議,linux支援的傳輸層協議定義成列舉變數在include/linux/in.h檔案中。
/* Standard well-defined IP protocols. */ enum { IPPROTO_IP = 0, /* Dummy protocol for TCP */ IPPROTO_ICMP = 1, /* Internet Control Message Protocol */ IPPROTO_IGMP = 2, /* Internet Group Management Protocol */ IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */ IPPROTO_TCP = 6, /* Transmission Control Protocol */ IPPROTO_EGP = 8, /* Exterior Gateway Protocol */ IPPROTO_PUP = 12, /* PUP protocol */ IPPROTO_UDP = 17, /* User Datagram Protocol */ IPPROTO_IDP = 22, /* XNS IDP protocol */ IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol */ IPPROTO_RSVP = 46, /* RSVP protocol */ IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */ IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling */ IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */ IPPROTO_AH = 51, /* Authentication Header protocol */ IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ IPPROTO_PIM = 103, /* Protocol Independent Multicast */ ... IPPROTO_RAW = 255, /* Raw IP packets */ IPPROTO_MAX };
(2)、傳輸層協議處理函式結構
傳輸層的每個協議都定義接受網路資料包的處理函式,完成對輸入資料包的處理,如分段、錯誤處理。傳輸層協議處理結構體struct net_protocol定義在include/net/protocol.h檔案中,結構體如下:
/* This is used to register protocols. */
struct net_protocol {
//協議註冊到核心的處理輸入資料包的函式
int (*handler)(struct sk_buff *skb);
//核心收到icmp 錯誤訊息處理函式
void (*err_handler)(struct sk_buff *skb, u32 info);
//傳送檢查gso
int (*gso_send_check)(struct sk_buff *skb);
struct sk_buff *(*gso_segment)(struct sk_buff *skb,
int features);
struct sk_buff **(*gro_receive)(struct sk_buff **head,
struct sk_buff *skb);
int (*gro_complete)(struct sk_buff *skb);
//是否需要ipsce策略檢查,1不需要
unsigned int no_policy:1,
netns_ok:1;
};
傳輸層的資料包處理結構體在IP層初始化函式inet_init中呼叫inet_add_protocol註冊到核心全域性陣列inet_protos中。
//核心全域性陣列,儲存了所有傳輸層的處理結構體
extern const struct net_protocol *inet_protos[MAX_INET_PROTOS];
inet_add_protocol函式:
int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol)
{
int hash, ret;
//根據協議編碼獲取雜湊值
hash = protocol & (MAX_INET_PROTOS - 1);
spin_lock_bh(&inet_proto_lock);
//將傳輸層協議處理結構體新增到全域性陣列inet_protos中
if (inet_protos[hash]) {
ret = -1;
} else {
inet_protos[hash] = prot;
ret = 0;
}
spin_unlock_bh(&inet_proto_lock);
return ret;
}
tcp協議net_protocol協議結構體例項:
static const struct net_protocol tcp_protocol = {
.handler = tcp_v4_rcv,
.err_handler = tcp_v4_err,
.gso_send_check = tcp_v4_gso_send_check,
.gso_segment = tcp_tso_segment,
.gro_receive = tcp4_gro_receive,
.gro_complete = tcp4_gro_complete,
.no_policy = 1,
.netns_ok = 1,
};
三、ip_local_deliver_finish函式
資料包通過網路過濾系統INPUT鏈上的鉤子處理函式後呼叫ip_local_deliver_finish函式繼續處理。ip_local_deliver函式主要任務:
a、初始化skb->data,設定傳輸層協議頭指標skb->transplant_header,使指標指向傳輸層協議頭的起始地址,這時IP層已經處理完成,但還是可以通過skb->network_header訪問Ip協議頭。
b、獲取傳輸層協議編碼iphdr-protocol,然後根據協議編碼在inet_protos全域性陣列中找到傳輸層結構體,呼叫傳輸層處理函式,資料包進如傳輸層。
c、檢查資料包是否需要ipsce檢查處理
c、錯誤處理,返回一個imcp包表示地址不可達。
ip_local_deliver_finish函式程式碼:
static int ip_local_deliver_finish(struct sk_buff *skb)
{
struct net *net = dev_net(skb->dev);
//資料包偏移ip頭部長度
__skb_pull(skb, ip_hdrlen(skb));
/* Point into the IP datagram, just past the header. */
//設定傳輸層協議頭指標skb->transplant_header
skb_reset_transport_header(skb);
//加讀鎖
rcu_read_lock();
{
int protocol = ip_hdr(skb)->protocol;
int hash, raw;
const struct net_protocol *ipprot;
resubmit:
raw = raw_local_deliver(skb, protocol);
//求得傳輸層的雜湊值
hash = protocol & (MAX_INET_PROTOS - 1);
//根據傳輸層協議號獲取傳輸層處理函式的結構體net_protocol
ipprot = rcu_dereference(inet_protos[hash]);
if (ipprot != NULL) {
int ret;
if (!net_eq(net, &init_net) && !ipprot->netns_ok) {
if (net_ratelimit())
printk("%s: proto %d isn't netns-ready\n",
__func__, protocol);
kfree_skb(skb);
goto out;
}
//配置了ipsec安全處理
if (!ipprot->no_policy) {
//ipsec策略安全檢查函式
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
kfree_skb(skb);
goto out;
}
nf_reset(skb);
}
//呼叫傳輸層的介面處理函式
//資料包進入傳輸層
ret = ipprot->handler(skb);
if (ret < 0) {
protocol = -ret;
goto resubmit;
}
IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);
} else {
//沒有找到傳輸層的處理結構體
if (!raw) {
if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
IP_INC_STATS_BH(net, IPSTATS_MIB_INUNKNOWNPROTOS);
//錯誤處理目的地址不可達,回覆一個icmp包
icmp_send(skb, ICMP_DEST_UNREACH,
ICMP_PROT_UNREACH, 0);
}
} else
IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);
kfree_skb(skb);
}
}
out:
rcu_read_unlock();
return 0;
}