(OK) 第五章 傳輸層(tcp)到網路層(ip)--基於Linux3.10
根據資料的流向跟蹤程式碼,由於資料傳送是從tcp層到網路層再到網路到主機層,所以先來看tcp層向ip層傳送資料的函式。
tcp的傳送函式和接收函式一樣位於net/ipv4/資料夾,檔名是tcp_output.c檔案,傳輸層和網路層聯絡的函式是tcp_transmit_skb(...):
在進入該函式時,先看該函式用到的一個重要的資料結構,其定義於net/dccp/ipv4.c檔案,919行是傳送時用到的函式。
- 918 staticconststruct inet_connection_sock_af_ops dccp_ipv4_af_ops = {
- 919 .queue_xmit = ip_queue_xmit,
- 920 .send_check = dccp_v4_send_check,
- 921 .rebuild_header = inet_sk_rebuild_header,
- 922 .conn_request = dccp_v4_conn_request,
- 923 .syn_recv_sock = dccp_v4_request_recv_sock,
- 924 .net_header_len = sizeof(struct iphdr),
- 925 .setsockopt = ip_setsockopt,
- 926 .getsockopt = ip_getsockopt,
- 927 .addr2sockaddr = inet_csk_addr2sockaddr,
- 928 .sockaddr_len = sizeof(struct sockaddr_in),
- 929 .bind_conflict = inet_csk_bind_conflict,
- 934 };
919行的函式服務於ip層,也就是四層模型中的網路層,我們先看傳輸層的函式tcp_transmit_skb,該函式會建立IP層的頭,並將該頭傳遞給網路層。
- net/ipv4/tcp_output.c
- 840 staticint tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
- 841 gfp_t gfp_mask)
- 842 {
- 843 conststruct inet_connection_sock *icsk = inet_csk(sk);
- 844 struct inet_sock *inet;
- 845 struct tcp_sock *tp;
- 961 err = icsk->icsk_af_ops->queue_xmit(skb, &inet->cork.fl);
- }
icsk_af_ops->queue_xmit的兩個引數,structsk_buff *skb已經在第二章敘述過了,inet的原型是inet_sock是inet的套接字的代表。
- struct inet_sock {
- /* sk and pinet6 has to be the first two members of inet_sock */
- struct sock sk; //祖先的類
- /* Socket demultiplex comparisons on incoming packets. */
- #define inet_daddr sk.__sk_common.skc_daddr //外部ipv4地址
- #define inet_rcv_saddr sk.__sk_common.skc_rcv_saddr//繫結的本地ipv4地址
- #define inet_addrpair sk.__sk_common.skc_addrpair
- #define inet_dport sk.__sk_common.skc_dport//目的埠號
- #define inet_num sk.__sk_common.skc_num//本地埠號
- #define inet_portpair sk.__sk_common.skc_portpair
- __be32 inet_saddr;
- __s16 uc_ttl; //單播的TTL,time to live
- __u16 cmsg_flags;
- __be16 inet_sport;
- __u16 inet_id;
- struct ip_options_rcu __rcu
- *inet_opt;
- int rx_dst_ifindex;
- __u8 tos;
- __u8 min_ttl;
- __u8 mc_ttl;
- __u8 pmtudisc;
- __u8 recverr:1,
- is_icsk:1,
- freebind:1,
- hdrincl:1,
- mc_loop:1,
- transparent:1,
- mc_all:1,
- nodefrag:1;
- __u8 rcv_tos;
- int uc_index;
- int mc_index;//多播裝置索引
- __be32 mc_addr;
- struct ip_mc_socklist __rcu
- *mc_list;
- struct inet_cork_full
- cork; //每個分片的ip packet構建ip頭時用到的資料結構
- };
ip_queue_xmit這個函式就是網路層的傳輸函數了;
- 326 int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl)
- 327 {
- 328 struct sock *sk = skb->sk;
- 329 struct inet_sock *inet = inet_sk(sk);
- 360 rt = ip_route_output_ports(sock_net(sk), fl4, sk, //獲取輸出資訊的路由表,路由資訊會填充到skb的dst欄位去。
- 361 daddr, inet->inet_saddr,
- 362 inet->inet_dport,
- 363 inet->inet_sport,
- 364 sk->sk_protocol,
- 365 RT_CONN_FLAGS(sk),
- 366 sk->sk_bound_dev_if);
- 403 res = ip_local_out(skb); //傳送出去
- 412 }
第403行是傳送packet的函式, res = ip_local_out(skb); //傳送出去
- staticinlineint dst_output(struct sk_buff *skb)
- {
- return skb_dst(skb)->output(skb);
- }
- int ip_local_out(struct sk_buff *skb)
- {
- err = __ip_local_out(skb);
- if (likely(err == 1))
- err = dst_output(skb);
- }
由上面兩個函式,真正呼叫的其實是rth->dst.output= ip_output指向的函式,該函式將主要工作放到ip_finish_output去完成,這和接收時的方法很類似,並且這裡也有一個鉤子函式。
- 298 int ip_output(struct sk_buff *skb)
- 299 {
- 300 struct net_device *dev = skb_dst(skb)->dev;
- 301
- 302 IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
- 303
- 304 skb->dev = dev;
- 305 skb->protocol = htons(ETH_P_IP);
- 306
- 307 return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev,
- 308 ip_finish_output,
- 309 !(IPCB(skb)->flags & IPSKB_REROUTED));
- 310 }
ip_finish_output函式的定義如下,CONFIG_NETFILTER和CONFIG_XFRM都是和安全相關的機制。231行判斷ip是否被分片了,通常乙太網上的資料沒有經過分片操作,如果分片了使用ip_fragment來處理,處理完了呼叫ip_finish_output2完成實質的傳送工作。
- 222 staticint ip_finish_output(struct sk_buff *skb)
- 223 {
- 224 #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
- 225 /* Policy lookup after SNAT yielded a new policy */
- 226 if (skb_dst(skb)->xfrm != NULL) {
- 227 IPCB(skb)->flags |= IPSKB_REROUTED;
- 228 return dst_output(skb);
- 229 }
- 230 #endif
- 231 if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
- 232 return ip_fragment(skb, ip_finish_output2);
- 233 else
- 234 return ip_finish_output2(skb);
- 235 }
ip_finish_output2對packet的頭進行處理,然後根據路由表尋找下一跳地址,這裡還涉及一層鄰居協議,這個沒有路由那麼龐大。
- 166 staticinlineint ip_finish_output2(struct sk_buff *skb)
- 167 {
- 196 nexthop = (__force u32) rt_nexthop(rt, ip_hdr(skb)->daddr);
- 197 neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
- 198 if (unlikely(!neigh))
- 199 neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
- 200 if (!IS_ERR(neigh)) {
- 201 int res = dst_neigh_output(dst, neigh, skb);
- 212 }
neigh是structneighbour型別的結構體,這個資料鄰居協議的範疇。197是鄰居協議查詢合適的網絡卡裝置,198行如果找不到則執行199行的函式建立一個,在獲得鄰居項之後執行201行的函式。該函式位於include/net/dst.h,該函式未定義於ipv4的目錄下,這也意味著資料將傳遞到主機到網路層了。
- 393 staticinlineint dst_neigh_output(struct dst_entry *dst, struct neighbour *n,
- 394 struct sk_buff *skb)
- 395 {
- 396 conststruct hh_cache *hh;
- 397
- 398 if (dst->pending_confirm) {
- 399 unsigned long now = jiffies;
- 400
- 401 dst->pending_confirm = 0;
- 402 /* avoid dirtying neighbour */
- 403 if (n->confirmed != now)
- 404 n->confirmed = now;
- 405 }
- 406
- 407 hh = &n->hh;
- 408 if ((n->nud_state & NUD_CONNECTED) && hh->hh_len)
- 409 return neigh_hh_output(hh, skb); //支援硬體快取頭方法的傳送
- 410 else
- 411 return n->output(n, skb); //不支援硬體快取頭的方法的傳送。
- 412 }
第411行呼叫的是neigh_resolve_output,該函式是通過路由表查到的,不論硬體支不支援硬體頭快取,neigh_resolve_output都會被呼叫到,它來源如下:
- staticconststruct neigh_ops arp_generic_ops = {
- .family = AF_INET,
- .solicit = arp_solicit,
- .error_report =arp_error_report,
- .output = neigh_resolve_output,
- .connected_output =
- neigh_connected_output,
- };
- //有硬體頭快取的函式操作集
- staticconststruct neigh_ops arp_hh_ops = {
- .family = AF_INET,
- .solicit = arp_solicit,
- .error_report =arp_error_report,
- .output = neigh_resolve_output,
- .connected_output =neigh_resolve_output,
- };
獲得上述output函式的過程就是路由的過程,還是來看看neigh_resolve_output,它定義於net/core/neighbour.c檔案。這時真正的離開了IP層,該函式的1310會呼叫之一文章講述的函式將資料實際的傳送出去。
- 1286 int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
- 1287 {
- 1288 struct dst_entry *dst = skb_dst(skb);
- 1289 int rc = 0;
- 1290
- 1291 if (!dst)
- 1292 goto discard;
- 1293
- 1294 if (!neigh_event_send(neigh, skb)) {
- 1295 int err;
- 1296 struct net_device *dev = neigh->dev;
- 1297 unsigned int seq;
- 1298
- 1299 if (dev->header_ops->cache && !neigh->hh.hh_len)
- 1300 neigh_hh_init(neigh, dst);
- 1301
- 1302 do {
- 1303 __skb_pull(skb, skb_network_offset(skb));
- 1304 seq = read_seqbegin(&neigh->ha_lock);
- 1305 err = dev_hard_header(skb, dev, ntohs(skb->protocol),
- 1306 neigh->ha, NULL, skb->len);
- 1307 } while (read_seqretry(&neigh->ha_lock, seq));
- 1308
- 1309 if (err >= 0)
- 1310 rc = dev_queue_xmit(skb);
- 1313 }
- }
最後還是看一張圖來回顧tcp/ip傳送資料的流程。
圖5.1 網路層函式呼叫流程