网络程序设计实验-TCP/IP协议栈源代码分析

TCP/IP协议栈源代码分析结论:

1. inet_init是如何被调用的?从start_kernel到inet_init调用路径

inet_init代码如下:


static int __init inet_init(void) 
{
    
struct sk_buff *dummy_skb; 
struct inet_protocol *p; 
struct inet_protosw *q; 
struct list_head *r;
printk(KERN_INFO "NET4: Linux TCP/IP 1.0 for NET4.0/n");
if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb)) {
    
  printk(KERN_CRIT "inet_proto_init: panic/n"); 
  return -EINVAL; 
}
/* 
  * Tell SOCKET that we are alive... 注册socket,告诉socket inet类型的地址族已经准备好了 
  */ 
   (void) sock_register(&inet_family_ops);
/* 
  * Add all the protocols. 包括arp,ip、ICMP、UPD、tcp_v4、tcp、igmp的初始化,主要初始化各种协议对应的inode和socket变量。
其中arp_init完成系统中路由部分neighbour表的初始化
ip_init完成ip协议的初始化。在这两个函数中,都通过定义一个packet_type结构的变量将这种数据包对应的协议发送数据、允许发送设备都做初始化。
  */
printk(KERN_INFO "IP Protocols: "); 
for (p = inet_protocol_base; p != NULL;) {
    
  struct inet_protocol *tmp = (struct inet_protocol *) p->next; 
  inet_add_protocol(p); 
  printk("%s%s",p->name,tmp?", ":"/n"); 
  p = tmp; 
}
/* Register the socket-side information for inet_create. */ 
for(r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r) 
  INIT_LIST_HEAD(r);
for(q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q) 
  inet_register_protosw(q);
/* 
  * Set the ARP module up  
  */
arp_init();
   /* 
    * Set the IP module up 
    */
ip_init();
tcp_v4_init(&inet_family_ops);
/* Setup TCP slab cache for open requests. */ 
tcp_init();
/* 
  * Set the ICMP layer up 
  */
icmp_init(&inet_family_ops);
/* I wish inet_add_protocol had no constructor hook... 
    I had to move IPIP from net/ipv4/protocol.c :-( --ANK 
  */ 
#ifdef CONFIG_NET_IPIP 
ipip_init(); 
#endif 
#ifdef CONFIG_NET_IPGRE 
ipgre_init(); 
#endif
/* 
  * Initialise the multicast router 
  */ 
#if defined(CONFIG_IP_MROUTE) 
ip_mr_init(); 
#endif
/* 
  * Create all the /proc entries. 
  */ 
#ifdef CONFIG_PROC_FS 
proc_net_create ("raw", 0, raw_get_info); 
proc_net_create ("netstat", 0, netstat_get_info); 
proc_net_create ("snmp", 0, snmp_get_info); 
proc_net_create ("sockstat", 0, afinet_get_info); 
proc_net_create ("tcp", 0, tcp_get_info); 
proc_net_create ("udp", 0, udp_get_info); 
#endif  /* CONFIG_PROC_FS */
ipfrag_init();
return 0; 
}  
module_init(inet_init);

调用inet_init的过程中,涉及到的函数如下:

1. start_kernel:

  • start_kernel是Linux内核的启动函数,定义在init/main.c文件中。
  • 在启动过程中,首先执行start_kernel,这个函数负责进行内核的初始化工作。

2. rest_init:

  • start_kernel中,会调用rest_init函数,该函数的主要作用是创建一个内核线程,并让这个线程执行kernel_init函数。

3. kernel_init:

  • kernel_init函数位于init/main.c文件中,主要负责完成内核的初始化。
  • kernel_init中,会调用do_basic_setup函数进行基本的系统设置。

4. do_basic_setup:

  • do_basic_setup函数中包含一系列基本的系统设置,最后调用kernel_init_freeable函数。

5. kernel_init_freeable:

  • kernel_init_freeable函数中,会调用kernel_init_freeable宏,其中包括了一系列的初始化函数,负责初始化各个子系统。
  • 其中会调用init_post函数,该函数位于kernel/init/main.c文件中。

6. init_post:

  • init_post函数中,会调用initcall_init函数,该函数位于init/main.c文件中。
    -initcall_init函数负责执行所有的初始化回调函数,这些回调函数在编译阶段被链接到.initcall.init节中。

7. do_initcalls:

  • initcall_init函数中,会调用do_initcalls函数,该函数位于init/main.c文件中。
  • do_initcalls函数会依次执行.initcall.init节中的所有初始化回调函数。

8. inet_init:

  • 在执行所有的初始化回调函数后,do_initcalls函数中会调用do_basic_setup函数,其中包括了对网络子系统的初始化。
  • 最终,do_basic_setup函数会调用inet_init函数,该函数位于net/ipv4/af_inet.c文件中。
  • inet_init函数负责初始化IPv4网络子系统。

2. 跟踪分析TCP/IP协议栈如何将自己与上层套接口与下层数据链路层关联起来的?

在Linux中,TCP/IP协议栈是在网络协议栈中的一部分,它负责处理网络通信的高层协议,如TCP和UDP。以下是对TCP/IP协议栈如何与上层套接口(Socket)和下层数据链路层关联的简要解释:

  1. 套接口(Socket):

    • 用户空间的应用程序通过套接口(Socket)与TCP/IP协议栈进行交互。套接口提供了一组系统调用,应用程序通过这些调用实现与协议栈的通信。
  2. 协议族和套接口的创建:

    • 在Linux中,协议栈通过协议族(AF_INET,AF_INET6等)和套接口(socket)的组合与上层应用程序建立联系。
    • 当应用程序调用socket系统调用时,它指定了协议族(如AF_INET)和套接口类型(如SOCK_STREAM)。
  3. 创建套接口数据结构:

    • 在协议栈内部,创建一个数据结构来表示套接口。这通常是一个结构体,包含与套接口相关的信息,如协议族、套接口类型、协议参数等。
  4. 协议栈初始化:

    • 在协议栈初始化的过程中,会注册相应的协议处理函数。例如,TCP/IP协议栈会注册TCP和UDP的处理函数。
  5. 协议与套接口关联:

    • 当应用程序通过bind系统调用将一个套接口绑定到一个特定的IP地址和端口上时,协议栈内部会将这个套接口与相应的协议(TCP或UDP)关联起来。
    • 这时,套接口数据结构中的一些字段会被设置,如本地IP地址、端口等。
  6. 监听和连接建立:

    • 对于TCP套接口,应用程序可能会调用listen系统调用,用于指示协议栈开始监听连接请求。当有新的连接请求到达时,协议栈会创建一个新的套接口数据结构来表示该连接。
  7. 数据的上交和下发:

    • 当应用程序通过套接口读取数据时,协议栈会调用相应协议的处理函数,将数据从协议栈上交给应用程序。
    • 当应用程序通过套接口发送数据时,协议栈会将数据传递给相应的协议处理函数,然后通过网络接口向下传递,最终交给数据链路层处理。
  8. 数据链路层关联:

    • 协议栈与下层数据链路层的关联通常是通过网络设备(如以太网接口)的注册和绑定来实现的。
    • 协议栈通过网络设备的数据结构,将数据链路层的功能与协议栈关联在一起。

协议栈通过数据结构的关联和相应的处理函数,将套接口与上层应用程序和下层数据链路层连接起来。这样的设计允许协议栈处理网络通信的细节,同时提供简单的套接口供应用程序使用。

3. TCP的三次握手源代码跟踪分析,跟踪找出设置和发送SYN/ACK的位置,以及状态转换的位置

在Linux内核中,TCP的三次握手过程的源代码涉及多个文件和函数。以下是对TCP三次握手过程的源代码分析,主要关注设置和发送SYN/ACK的位置以及状态转换的位置。

1. TCP的三次握手流程

三次握手的过程包括以下步骤:

  1. 客户端发送SYN:

    • 客户端向服务器发送一个SYN(同步)报文,请求建立连接。
  2. 服务器回应SYN/ACK:

    • 服务器收到客户端的SYN后,回应一个SYN/ACK(同步/确认)报文,表示接受连接。
  3. 客户端发送ACK:

    • 客户端收到服务器的SYN/ACK后,发送一个ACK(确认)报文,完成连接的建立。

2. 源代码分析

2.1. 设置和发送SYN/ACK

在Linux内核中,设置和发送SYN/ACK主要发生在服务器接收到客户端的SYN报文时。以下是相关源代码:

  • 文件: net/ipv4/tcp_ipv4.c

  • 函数: tcp_v4_syn_recv_sock

// net/ipv4/tcp_ipv4.c

static int tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                                 struct request_sock *req)
{
   
    // ... 省略部分代码

    /* Generate an ISN. The client will only send data when
     * ACKing the ISN + 1.
     */
    tcp_initialize_rcv_mss(sk);

    /* Send out a synack here. */
    tcp_send_synack(sk, req, req->rsk_rcv_wnd,
                    req->ts_recent,
                    req->ts_recent_stamp);

    // ... 省略部分代码

    /* Move to established and bump the window up. */
    tcp_set_state(sk, TCP_ESTABLISHED);
    tcp_initialize_rcv_mss(sk);
    tcp_ecn_rcv_synack(sk, skb);

    // ... 省略部分代码

    return 0;
}

tcp_send_synack 函数中,会设置并发送SYN/ACK报文。

2.2. 状态转换

状态转换主要发生在服务器收到客户端的ACK报文后。以下是相关源代码:

  • 文件: net/ipv4/tcp_input.c

  • 函数: tcp_rcv_synsent_state_process

// net/ipv4/tcp_input.c

int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
                                  struct tcphdr 

你可能感兴趣的:(网络,tcp/ip,网络协议)