大家都知道理解一个程序需要从其初始化看起,内核代码也是一样,不过内核是按模块初始化的,模块初始化这部分今天暂时先不讲,接下来会单独拎出来详细讲解。现在只需要知道TCP/IP协议栈的初始化是通过fs_initcall宏来完成的就行。
TCP/IP协议栈的初始化函数为
代码语言:javascript复制inet_init
static int __init inet_init(void)
{
struct inet_protosw *q;
struct list_head *r;
int rc = -EINVAL;
sock_skb_cb_check_size(sizeof(struct inet_skb_parm));
rc = proto_register(&tcp_prot, 1);
if (rc)
goto out;
rc = proto_register(&udp_prot, 1);
if (rc)
goto out_unregister_tcp_proto;
rc = proto_register(&raw_prot, 1);
if (rc)
goto out_unregister_udp_proto;
rc = proto_register(&ping_prot, 1);
if (rc)
goto out_unregister_raw_proto;
/*
* Tell SOCKET that we are alive...
*/
(void)sock_register(&inet_family_ops);
/proc 操作接口
#ifdef CONFIG_SYSCTL
ip_static_sysctl_init();
#endif
/*
* Add all the base protocols.
*/
if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
pr_crit("%s: Cannot add ICMP protocoln", __func__);
if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
pr_crit("%s: Cannot add UDP protocoln", __func__);
if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
pr_crit("%s: Cannot add TCP protocoln", __func__);
#ifdef CONFIG_IP_MULTICAST
if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
pr_crit("%s: Cannot add IGMP protocoln", __func__);
#endif
/* 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();
/* Setup TCP slab cache for open requests. */
tcp_init();
/* Setup UDP memory threshold */
udp_init();
/* Add UDP-Lite (RFC 3828) */
udplite4_register();
ping_init();
/*
* Set the ICMP layer up
*/
if (icmp_init() < 0)
panic("Failed to create the ICMP control socket.n");
/*
* Initialise the multicast router
*/
#if defined(CONFIG_IP_MROUTE)
if (ip_mr_init())
pr_crit("%s: Cannot init ipv4 mrouten", __func__);
#endif
if (init_inet_pernet_ops())
pr_crit("%s: Cannot init ipv4 inet pernet opsn", __func__);
/*
* Initialise per-cpu ipv4 mibs
*/
if (init_ipv4_mibs())
pr_crit("%s: Cannot init ipv4 mibsn", __func__);
ipv4_proc_init();
ipfrag_init();
dev_add_pack(&ip_packet_type);
ip_tunnel_core_init();
rc = 0;
out:
return rc;
out_unregister_raw_proto:
proto_unregister(&raw_prot);
out_unregister_udp_proto:
proto_unregister(&udp_prot);
out_unregister_tcp_proto:
proto_unregister(&tcp_prot);
goto out;
}
接下来我们看一下上述初始化函数用到的一些结构体及函数。
1.
proto_register 注册 struct proto 协议到static LIST_HEAD(proto_list);链。
struct proto 结构体比较庞大,这里不再展开,该结构定义了从 socket层到传输层的接口,开发过用户态网络相关的程序的相比大家对socket的 api应该都了解,close、connect、sendmsg、recvmsg这些接口的实现最终由struct proto中定义的方法完成。
2.
网络协议族接口,定义在
代码语言:javascript复制include/linux/net.h
struct net_proto_family {
int family;
int (*create)(struct net *net, struct socket *sock,
int protocol, int kern);
.....
};
family:网络协议族,目前支持的有43个,定义在include/linux/socket.h中,常用的有
PF_UNIX Unix domain sockets
PF_INET Internet IP Protocol
PF_NETLINK netlink协议
create: 协议的创建方法
3.
代码语言:javascript复制/* This is used to register protocols. */
struct net_protocol {
....
int (*handler)(struct sk_buff *skb);
void (*err_handler)(struct sk_buff *skb, u32 info);
....
};
handler 协议的处理函数
err_handler 协议错误处理函数
所有的基础协议icmp_protocol、udp_protocol、tcp_protocol、igmp_protocol通过inet_add_protocol接口注册到该结构体数组中,
const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;
4.
代码语言:javascript复制/* This is used to register socket interfaces for IP protocols. */
struct inet_protosw {
struct list_head list;
/* These two fields form the lookup key. */
unsigned short type; /* This is the 2nd argument to socket(2). */
unsigned short protocol; /* This is the L4 protocol number. */
struct proto *prot;
const struct proto_ops *ops;
....
};
list:为inetsw表的节点
/* The inetsw table contains everything that inet_create needs to
* build a new socket.
*/
static struct list_head inetsw[SOCK_MAX];
type:为socket的类型:
enum sock_type {
SOCK_STREAM = 1,
SOCK_DGRAM = 2,
SOCK_RAW = 3,
SOCK_RDM = 4,
SOCK_SEQPACKET = 5,
SOCK_DCCP = 6,
SOCK_PACKET = 10,
};
#define SOCK_MAX (SOCK_PACKET 1)
prot: 即为上面提到到通过 proto_register 的协议接口
ops :为协议的操作接口
所有的协议 inetsw_array 通过 inet_register_protosw 接口注册到 inetsw 表。
通过下图的socket结构与传输控制块之间的关系,我们可以对上面介绍的结构有个全局的把握,具体的这些关系在后续的篇章中会一层层的揭开它们神秘的面纱。
接下来就是各个模块的初始化:
代码语言:javascript复制arp_init arp初始化
ip_init ip层初始化 主要是路由模块
tcp_v4_init、tcp_init tcp相关初始化
udp_init、udplite4_register udp相关初始化
icmp_init icmp初始化
ip_mr_init 多播路由
init_ipv4_mibs mibs初始化
ipv4_proc_init proc初始化
ipfrag_init ip碎片
dev_add_pack 注册 a protocol handler,ip层为 ip_packet_type。
这个结构很关键,涉及到具体协议的处理入口
struct packet_type {
__be16 type; /* This is really htons(ether_type). */
int (*func) (struct sk_buff *,
struct net_device *,
struct packet_type *,
struct net_device *);
....
struct list_head list;
};
type :协议类型
func :协议处理函数
list : ptype_head 的节点,
ptype_head根据协议分区为两个,ptype_all为ETH_P_ALL协议的表,
struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; struct list_head ptype_all __read_mostly; /* Taps */
ip_tunnel_core_init tunnel模块初始化
上述每一个模块都需要单独一个篇章来写,不在这里展开。
至此,协议栈的初始化就完成了。