Linux IPv4代码分析系列(2)

来源:互联网 发布:域名从注册到使用 编辑:程序博客网 时间:2024/05/21 11:02
标签:Linux IPv4代码分析

原文出处:http://blog.chinaunix.net/uid-23392781-id-2426603.html

 

 在(一)中分析了inet_init的初始化部分,但在这之前还是有一部分的初始化需要说明一下,这个就是/net/socket.c/sock_init()。它的初始化在inet_init之前,详解对应函数的定义。

 

core_initcall(sock_init);/* early initcall */
fs_initcall(inet_init);
#define core_initcall(fn) __define_initcall("1",fn,1)
#define fs_initcall(fn) __define_initcall("5",fn,5)

 

下面简单分析一下sock_init

staticint __init sock_init(void)
{
/*
* Initialize sock SLAB cache.
*/

sk_init();
/*
* Initialize skbuff SLAB cache
*/

skb_init();
//这个函数的初始化比较有意思,因为它的内部申请了两个内存头节点。为了说明这个用处,给出一个链接,一句话还是不能说清楚的,内存管理的确比较复杂。
https:
//www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/


/*
* Initialize the protocols module.
*/

init_inodecache();//同上
register_filesystem(&sock_fs_type);
sock_mnt = kern_mount(&sock_fs_type);
//这里添加了socket文件系统的支持,可以cat /proc/filesystems查看当前支持多少种文件系统,其中的”nodev sockfs”就是要注册的文件系统。注册socket文件系统的好处就是可以像操作文件一样进行发送接收数据。看看这个文件:
srw-rw---- 1 root admin 0 Apr 20 03:46 networkd_uds

/* The real protocol initialization is performed in later initcalls.
*/


//下面这个功能就是大名鼎鼎的netfilter了,虽然是可选的组件,但现在的内核默认都是编译到内核中了,见arch/xx/xx_defconfig文件
CONFIG_NETFILTER=y
Netfilter功能强大, 支持L2,L3,L4层的包过滤,数据包修改,NAT,转发策略,拦截策略。另外可以自己写module实现很多其他的功能,比如xt_limit module能够实现简单的流量控制等功能。好东西啊。

#ifdef CONFIG_NETFILTER
netfilter_init();
#endif
return 0;
}


/net/socket.c这个文件结构很清晰,也比较好分析,都说socket编程,多少也应该了解一下实现吧。
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) 
首先SYSCALL_DEFINE3这个宏定义了socket系统调用的实现。而EXPORT_SYMBOL修饰的函数是供内核其他模块使用的。

SYSCALL_DEFINE3通过跟踪不难发现最终定义了sys_socket系统调用,并加入系统调用数组sys_call_table中,linux的宏定义还是比较烦的,估计玩过连连看的兄弟应该能很快的找到定义:)
a)#define SYSCALL_DEFINE3(name,...) SYSCALL_DEFINEx(3, _##name,__VA_ARGS__)
b)#define SYSCALL_DEFINEx(x, sname,...) \
__SYSCALL_DEFINEx(x, sname,__VA_ARGS__)
c)asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
再看include/asm-generic/unistd.h
#define __NR_socket 198
__SYSCALL(__NR_socket, sys_socket)

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)

SYSCALL_DEFINE3(socket,int, family,int, type,int, protocol)
{
int retval;
structsocket*sock;
int flags;

/* Check the SOCK_* constants for consistency. */
BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK)!= SOCK_TYPE_MASK);
BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);

flags = type &~SOCK_TYPE_MASK;
if(flags &~(SOCK_CLOEXEC | SOCK_NONBLOCK))
return-EINVAL;
type &= SOCK_TYPE_MASK;

if(SOCK_NONBLOCK != O_NONBLOCK &&(flags & SOCK_NONBLOCK))
flags =(flags &~SOCK_NONBLOCK)| O_NONBLOCK;

//sock_create->sock_alloc->new_inode 通过调用关系可以看出,这个函数的本质是申请一个inode节点;同时还需要注意的是,在__sock_create函数中调用了函数指针int (*create)(struct net *net, struct socket *sock, int protocol),用于各类型socket初始化及函数指针挂接。
retval = sock_create(family, type, protocol,&sock)


if(retval < 0)
goto out;

retval = sock_map_fd(sock, flags &(O_CLOEXEC | O_NONBLOCK)); //在当前进程中找到一个空闲的fd,把fd和inode关联起来。
if(retval < 0)
goto out_release;

out:
/* It may be already another descriptor 8) Not kernel problem. */
return retval;

out_release:
sock_release(sock);
return retval;
}

至于sys_bind, sys_listen, sys_connect, sys_send, sys_recv等相关函数这里并没有具体实现,因为socket类型有多种,这里基于可扩展性考虑,根据socket传入参数来定位socket类型,进而调用对应的函数指针。
sys_accept在这里的实现类似sys_socket。
socket类型的注册和卸载见这两个函数,
int sock_register(const struct net_proto_family *ops)
void sock_unregister(int family)
另外还有一些供kernel调用的函数见kernel_****,开头的函数。
由于目前只考虑ipv4的实现,那么在net/ipv4/下,调用sock_register的地方是在(一)中分析的inet_init函数里调用的。那么我们在用户进程中调用socket函数时,kernel调用了/net/ipv4/af_inet.c/inet_create函数,看看inet_create都干了什么。

/*
* Create an inet socket.
*/


staticint inet_create(struct net *net,structsocket*sock,int protocol)
{
struct sock *sk;
struct inet_protosw *answer;
struct inet_sock *inet;
struct proto *answer_prot;
unsignedchar answer_flags;
char answer_no_check;
int try_loading_module = 0;
int err;

if(unlikely(!inet_ehash_secret))
if(sock->type !=SOCK_RAW&& sock->type !=SOCK_DGRAM)
build_ehash_secret();

sock->state = SS_UNCONNECTED;//设置socket状态

/* Look for the requested type/protocol pair. */
lookup_protocol:
err =-ESOCKTNOSUPPORT;
rcu_read_lock();

//下面这部分片段定位到inet的socket子类型,TCP/UDP/IP
list_for_each_entry_rcu(answer,&inetsw[sock->type],list){

err = 0;
/* Check the non-wild match. */
if(protocol == answer->protocol){
if(protocol !=IPPROTO_IP)
break;
}else{
/* Check for the two wild cases. */
if(IPPROTO_IP== protocol){
protocol = answer->protocol;
break;
}
if(IPPROTO_IP== answer->protocol)
break;
}
err =-EPROTONOSUPPORT;
}

if(unlikely(err)){//这里面有一个自动加载的功能,如果功能模块在编译时指定为独立模块,kernel会按照下面的命名规则找到对应模块,并加载到内核当中。
if(try_loading_module < 2){
rcu_read_unlock();
/*
* Be more specific, e.g. net-pf-2-proto-132-type-1
* (net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM)
*/

if(++try_loading_module == 1)
request_module("net-pf-%d-proto-%d-type-%d",
PF_INET, protocol, sock->type);
/*
* Fall back to generic, e.g. net-pf-2-proto-132
* (net-pf-PF_INET-proto-IPPROTO_SCTP)
*/

else
request_module("net-pf-%d-proto-%d",
PF_INET, protocol);
goto lookup_protocol;
}else
goto out_rcu_unlock;
}

err =-EPERM;
if(answer->capability > 0 &&!capable(answer->capability))
goto out_rcu_unlock;

err =-EAFNOSUPPORT;
if(!inet_netns_ok(net, protocol))
goto out_rcu_unlock;

sock->ops = answer->ops; //这里挂接的函数指针。
answer_prot = answer->prot;
answer_no_check = answer->no_check;
answer_flags = answer->flags;
rcu_read_unlock();

WARN_ON(answer_prot->slab ==NULL);

err =-ENOBUFS;

//为sock结构分配内存,并初始化里面的各字段。每个字段的含义没有必要都要搞清楚
sk = sk_alloc(net,PF_INET, GFP_KERNEL, answer_prot);
if(sk ==NULL)
goto out;

err = 0;
sk->sk_no_check = answer_no_check;
if(INET_PROTOSW_REUSE & answer_flags)
sk->sk_reuse = 1;

inet = inet_sk(sk);
inet->is_icsk =(INET_PROTOSW_ICSK & answer_flags)!= 0;

if(SOCK_RAW== sock->type){
inet->num = protocol;
if(IPPROTO_RAW== protocol)
inet->hdrincl = 1;
}

if(ipv4_config.no_pmtu_disc)
inet->pmtudisc = IP_PMTUDISC_DONT;
else
inet->pmtudisc = IP_PMTUDISC_WANT;

inet->id = 0;

sock_init_data(sock, sk);

sk->sk_destruct = inet_sock_destruct;
sk->sk_protocol = protocol;
sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;

inet->uc_ttl =-1;
inet->mc_loop = 1;
inet->mc_ttl = 1;
inet->mc_all = 1;
inet->mc_index = 0;
inet->mc_list =NULL;

sk_refcnt_debug_inc(sk);

if(inet->num){
/* It assumes that any protocol which allows
* the user to assign a number at socket
* creation time automatically
* shares.
*/

inet->sport =htons(inet->num);
/* Add to protocol hash chains. */
sk->sk_prot->hash(sk);
}

if(sk->sk_prot->init){
err = sk->sk_prot->init(sk);
if(err)
sk_common_release(sk);
}
out:
return err;
out_rcu_unlock:
rcu_read_unlock();
goto out;
}