核心中的UDP socket流程(5)——inet_create
進入函數inet_create
static int inet_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
struct inet_protosw *answer;
struct inet_sock *inet;
struct proto *answer_prot;
unsigned char 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();
當socket為UDP時,sock->type = SOCK_DGRAM。是以這裡不會調用到huild_ehash_secret()————其實這個函數是為了産生一個随機的數賦給inet_ehash_secret————暫時還不知道這個變量的确切用途。
通過sock->type和protocol在inetsw連結清單中找到對應的proto;
/* Look for the requested type/protocol pair. */
lookup_protocol:
err = -ESOCKTNOSUPPORT;
rcu_read_lock();
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;
}
if (IPPROTO_IP == answer->protocol)
}
err = -EPROTONOSUPPORT;
}
從以上代碼可以看出,IPPROTO_IP是作為一個wild protocol使用的。首先是盡量尋找與參數protocol完全比對的協定,如果找不到完全比對的,如果參數是IPPROTO_IP,那麼任一protocol都可以使用,如果找到PPROTO_IP協定的話,就使用該協定。
inetsw是一個static的全局變量
static struct list_head inetsw[SOCK_MAX];
該全局變量在函數inet_init中初始化
static int __init inet_init(void)
/*
Skip some codes
*/
/* 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);
通過inetsw_array來初始化inetsw,那麼在inetsw中儲存的協定順序就與inetsw相同。
/* Upon startup we insert all the elements in inetsw_array[] into
* the linked list inetsw.
*/
static struct inet_protosw inetsw_array[] =
{
.type = SOCK_STREAM,
.protocol = IPPROTO_TCP,
.prot = &tcp_prot,
.ops = &inet_stream_ops,
.no_check = 0,
.flags = INET_PROTOSW_PERMANENT |
INET_PROTOSW_ICSK,
},
.type = SOCK_DGRAM,
.protocol = IPPROTO_UDP,
.prot = &udp_prot,
.ops = &inet_dgram_ops,
.no_check = UDP_CSUM_DEFAULT,
.flags = INET_PROTOSW_PERMANENT,
},
{
.type = SOCK_RAW,
.protocol = IPPROTO_IP, /* wild card */
.prot = &raw_prot,
.ops = &inet_sockraw_ops,
.no_check = UDP_CSUM_DEFAULT,
.flags = INET_PROTOSW_REUSE,
}
};
那麼對于UDP來說,再找到對應的UDP protocol後,answer就指向inetsw_array[1]。
sock->ops = answer->ops;
answer_prot = answer->prot;
answer_no_check = answer->no_check;
answer_flags = answer->flags;
rcu_read_unlock();
對于UDP來說,sock->ops就指向inet_dgram_ops,answer_prot就是udp_prot,answer_no_check為UDP_CSUM_DEFAULT,answer_flags為INET_PROTOSW_PERMANENT。
下面是inet_dgram_ops的定義
const struct proto_ops inet_dgram_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release,
.bind = inet_bind,
.connect = inet_dgram_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = inet_getname,
.poll = udp_poll,
.ioctl = inet_ioctl,
.listen = sock_no_listen,
.shutdown = inet_shutdown,
.setsockopt = sock_common_setsockopt,
.getsockopt = sock_common_getsockopt,
.sendmsg = inet_sendmsg,
.recvmsg = inet_recvmsg,
.mmap = sock_no_mmap,
.sendpage = inet_sendpage,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
.compat_getsockopt = compat_sock_common_getsockopt,
#endif
answer_prot的定義是
struct proto udp_prot = {
.name = "UDP",
.close = udp_lib_close,
.connect = ip4_datagram_connect,
.disconnect = udp_disconnect,
.ioctl = udp_ioctl,
.destroy = udp_destroy_sock,
.setsockopt = udp_setsockopt,
.getsockopt = udp_getsockopt,
.sendmsg = udp_sendmsg,
.recvmsg = udp_recvmsg,
.sendpage = udp_sendpage,
.backlog_rcv = __udp_queue_rcv_skb,
.hash = udp_lib_hash,
.unhash = udp_lib_unhash,
.rehash = udp_v4_rehash,
.get_port = udp_v4_get_port,
.memory_allocated = &udp_memory_allocated,
.sysctl_mem = sysctl_udp_mem,
.sysctl_wmem = &sysctl_udp_wmem_min,
.sysctl_rmem = &sysctl_udp_rmem_min,
.obj_size = sizeof(struct udp_sock),
.slab_flags = SLAB_DESTROY_BY_RCU,
.h.udp_table = &udp_table,
.compat_setsockopt = compat_udp_setsockopt,
.compat_getsockopt = compat_udp_getsockopt,
繼續後面的代碼
err = -ENOBUFS;
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;
首先申請一個struct sock的記憶體,然後sk->sk_no_check用于表示是否檢查checksum,1為不檢查,0為檢查。通過與INET_PROTOSW_REUSE的位與操作——該宏表示這個協定是自動重用socket。
inet = inet_sk(sk);
inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;
inet->nodefrag = 0;
首先,将通用sock結構用函數inet_sk轉為inet_sock——UDP的域是AF_INET,然後與宏INET_PROTOSW_ICSK相與來确定是否是基于連接配接——檢視inetsw_array,隻有TCP是有這個标志的。inet->nodefrag設為0,是允許分片。
if (SOCK_RAW == sock->type) {
inet->inet_num = protocol;
if (IPPROTO_RAW == protocol)
inet->hdrincl = 1;
}
如果建立的是RAW socket,那麼就以protocol協定号作為本地端口。如果protocol是IPPROTO_RAW,那麼就給inet->hdincl置1,表示由使用者來建立IP頭,而不再由核心添加IP頭。
if (ipv4_config.no_pmtu_disc)
inet->pmtudisc = IP_PMTUDISC_DONT;
else
inet->pmtudisc = IP_PMTUDISC_WANT;
inet->inet_id = 0;
設定MTU的政策——這裡說政策不太合适,IP_PMTUDISC_DONT表示不對frame幀分片,IP_PMTUDISC_WANT表示通過route轉發過來的包來确定最小的MTU。然後初始化inet_id.
sock_init_data(sock, sk);
sk->sk_destruct = inet_sock_destruct;
sk->sk_protocol = protocol;
sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
初始化sk的結構體。
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);
初始化剩餘的inet的成員變量,這些變量的含義可以直接看inet_sock結構體的注釋,很清晰。uc_ttl表示單點傳播ttl,mc_loop表示回環是否有效,mc_ttl表示多點傳播ttl,mc_all是為了支援新的socket option IP_MULTICAST_ALL,如果設定了這個标志,表示所有發往端口的多點傳播都會傳給這個socket,如果沒有設定,那麼隻有加入了的組的多點傳播才會傳給socket。
if (inet->inet_num) {
/* It assumes that any protocol which allows
* the user to assign a number at socket
* creation time automatically
* shares.
*/
inet->inet_sport = htons(inet->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);
當inet->inet_num不為0時,設定inet的source port,并把sk加到hash表中——對于UDP,不會執行這個。如果該協定有init函數,就調用init。通過inetsw_array可知,udp是沒有init函數的,而tcp和ip,raw的init函數分别為tcp_v4_init_sock,raw_init。
到此,UDP的socket已經建立成功了。