Linux 源码分 之 udp 一

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创建过程中的数据结构关系如下:

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值