udp socket的创建过程
上一篇分析了RAW Socket的创建、发送、接收过程,以及MAC驱动层的发送、接收。这一篇来讨论udp的发送接收,以及IP层的处理过程。
阅读之前,有几个问题值得关注:
1、udp发送是否像tcp一样将小包合并为大包发送?
2、udp发送缓冲区如何使用?
3、udp未进行bind情况下,如何获取src addr?
4、udp如何查找出口设备?
5、数据上行时如何查找入口设备?
6、数据上行时,若socket被占用,udp如何处理?
7、ip层如何调用udp层的接口?
1、 udp socket 用户层逻辑
首先看一段udp client的demo。
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
assert( sockfd != -1 );
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6666);
saddr.sin_addr.s_addr = inet_addr("192.168.0.10");
while(1)
{
int bufsize = 128;
char buff[bufsize]={0};
printf("input\n");
fgets(buff, bufsize, stdin);
if(strncmp(buff,"end",3)==0)
{
break;
}
sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&saddr,sizeof(saddr));
memset(&buff, bufsize,0);
int len=sizeof(saddr);
recvfrom(sockfd,buff,bufsize,0,(struct sockaddr*)&saddr,&len);
printf("recv:%s\n",buff);
}
close(sockfd);
exit(0);
2 、udp socket 创建
socket(AF_INET,SOCK_DGRAM,0)的系统调用如下:
SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
{
case SYS_SOCKET:
err = __sys_socket(a0, a1, a[2]);
break;
}
int __sys_socket(int family, int type, int protocol)
{
struct socket *sock;
retval = sock_create(family, type, protocol, &sock);
if (retval < 0)
return retval;
return sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
}
int family, int type, int protocol分别为AF_INET, SOCK_DGRAM, 0, current->nsproxy->net_ns该参数为当前进程命名空间的network结构体。
int __sock_create(struct net *net, int family, int type, int protocol, struct socket **res, int kern)
{
struct socket *sock;
const struct net_proto_family *pf;
err = security_socket_create(family, type, protocol, kern);
sock = sock_alloc();
sock->type = type;
//搜索net_families结构体指针数组里的 .family = family的 ops
pf = rcu_dereference(net_families[family]);
(net_families[family]);
err = pf->create(net, sock, protocol, kern);
err = security_socket_post_create(sock, family, type, protocol, kern);
*res = sock;
}
udp的ops操作集注册过程如下:
int __init inet_init(void)
{
rc = proto_register(&udp_prot, 1);
if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
pr_crit("%s: Cannot add UDP protocol\n", __func__);
(void)sock_register(&inet_family_ops);
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);
//会将ip_packet_type挂到ptype_base该链表数组上
dev_add_pack(&ip_packet_type);
}
udp_prot如下:
struct proto udp_prot = {
.name = "UDP",
.init = udp_init_sock,
.sendmsg = udp_sendmsg,
.recvmsg = udp_recvmsg,
...
}
udp_protocol,udp报文上行会调用udp_rcv:
static struct net_protocol udp_protocol = {
.early_demux = udp_v4_early_demux,
.early_demux_handler = udp_v4_early_demux,
.handler = udp_rcv,
.err_handler = udp_err,
.no_policy = 1,
.netns_ok = 1,
};
inet_family_ops如下:
struct net_proto_family inet_family_ops = {
.family = PF_INET,
.create = inet_create,
.owner = THIS_MODULE,
};
inetsw_array是一个结构体数组,成员为各个协议TCP、UDP、RAW的protocol。
struct inet_protosw inetsw_array[] =
{
{
.type = SOCK_DGRAM,
.protocol = IPPROTO_UDP,
.prot = &udp_prot,
.ops = &inet_dgram_ops,
.flags = INET_PROTOSW_PERMANENT,
},
}
inetsw是一个链表头数组:
struct list_head inetsw[SOCK_MAX];
ip_packet_type是packet_type类型,.func在网络数据接收时会用到。
struct packet_type ip_packet_type __read_mostly = {
.type = cpu_to_be16(ETH_P_IP),
.func = ip_rcv,
.list_func = ip_list_rcv,
};
将inetsw_array数组成员挂到inetsw数组type下标的位置。
inet_register_protosw(q);
因此
rcu_dereference(net_families[family])
获取到的pf为 inet_family_ops,因此pf->create = inet_create
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;
//从inetsw中获取下标为type的inet_protosw
list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {
…
}
//赋值操作集
sock->ops = answer->ops;
answer_prot = answer->prot;
answer_flags = answer->flags;
//申请sk,sk->sk_prot = answer_prot
sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot, kern);
inet = inet_sk(sk);
//初始化sk
sock_init_data(sock, sk);
sk->sk_protocol = protocol;
//调用udp_prot的init
if (sk->sk_prot->init) {
err = sk->sk_prot->init(sk);
}
}
获取到下标为type的inet_protosw后,进行赋值,申请sk,并初始化。
void sock_init_data(struct socket *sock, struct sock *sk)
{
sk->sk_type = sock->type;
//设置数据就绪回调函数
sk->sk_data_ready = sock_def_readable;
…
}
udp_prot的init为:
int udp_init_sock(struct sock *sk)
{
skb_queue_head_init(&udp_sk(sk)->reader_queue);
sk->sk_destruct = udp_destruct_sock;
return 0;
}
总结:
1.在inet_init过程中会注册inet_family_ops,创建tcp或udp的socket时会调用其create接口。
2.会在ptype_base注册ip_packet_type,在网络数据包上行时,会调用func。
3.会注册udp_prot,用于用户层的发送与接收。
4.注册udp_protocol,数据上行至传输层调用
udp socket创建过程中的数据结构关系如下: