前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux tcp/ip 源码分析 - bind

Linux tcp/ip 源码分析 - bind

作者头像
KINGYT
发布2019-05-30 19:18:34
2.4K0
发布2019-05-30 19:18:34
举报

// net/socket.c SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen) { struct socket *sock; struct sockaddr_storage address; ... sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { err = move_addr_to_kernel(umyaddr, addrlen, &address); if (err >= 0) { ... if (!err) err = sock->ops->bind(sock, (struct sockaddr *) &address, addrlen); } ... } return err; }

方法描述

1. 调用sockfd_lookup_light方法,根据fd找到socket。

// net/socket.c static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed) { struct fd f = fdget(fd); struct socket *sock; ... if (f.file) { sock = sock_from_file(f.file, err); if (likely(sock)) { ... return sock; } ... } return NULL; }

该方法先根据fd找到file,再调用sock_from_file方法,从file中获取到socket。

// net/socket.c struct socket *sock_from_file(struct file *file, int *err) { if (file->f_op == &socket_file_ops) return file->private_data; /* set in sock_map_fd */ ... return NULL; } EXPORT_SYMBOL(sock_from_file);

2. 调用move_addr_to_kernel方法,拷贝用户提供的bind地址到address变量。

所以,当该bind方法调用结束后,用户提供的struct sockaddr参数变量还是可以继续使用的。

3. 调用sock->ops->bind方法,继续执行bind逻辑。

由上一篇文章我们可以知道,sock->ops指向的是&inet_stream_ops,所以sock->ops->bind方法即为inet_bind。

// net/ipv4/af_inet.c int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sockaddr_in *addr = (struct sockaddr_in *)uaddr; struct sock *sk = sock->sk; struct inet_sock *inet = inet_sk(sk); ... snum = ntohs(addr->sin_port); ... inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr; ... if ((snum || !inet->bind_address_no_port) && sk->sk_prot->get_port(sk, snum)) { ... } ... inet->inet_sport = htons(inet->inet_num); inet->inet_daddr = 0; inet->inet_dport = 0; ... err = 0; ... return err; } EXPORT_SYMBOL(inet_bind);

该方法设置struct inet_sock中的字段如下

inet->inet_saddr = addr->sin_addr.s_addr;

inet->inet_sport = htons(inet->inet_num);

inet->inet_daddr = 0;

inet->inet_dport = 0;

可见,目标地址和端口都设置为0,源地址为addr->sin_addr.s_addr,即用户传入的地址,源端口为inet->inet_num。

inet->inet_num的值是在sk->sk_prot->get_port(sk, snum)方法中设置的。

对比上一篇文章中的tcp_prot中各字段的值我们可以知道,sk->sk_prot->get_port指向的是inet_csk_get_port方法。

// net/ipv4/inet_connection_sock.c int inet_csk_get_port(struct sock *sk, unsigned short snum) { ... struct inet_hashinfo *hinfo = sk->sk_prot->h.hashinfo; int ret = 1, port = snum; struct inet_bind_hashbucket *head; struct net *net = sock_net(sk); struct inet_bind_bucket *tb = NULL; ... head = &hinfo->bhash[inet_bhashfn(net, port, hinfo->bhash_size)]; ... inet_bind_bucket_for_each(tb, &head->chain) if (net_eq(ib_net(tb), net) && tb->port == port) goto tb_found; tb_not_found: tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep, net, head, port); ... tb_found: if (!hlist_empty(&tb->owners)) { ... if (inet_csk_bind_conflict(sk, tb, true, true)) goto fail_unlock; } success: ... if (!inet_csk(sk)->icsk_bind_hash) inet_bind_hash(sk, tb, port); ... ret = 0; ... return ret; } EXPORT_SYMBOL_GPL(inet_csk_get_port);

方法描述

1. 在hinfo->bhash中找到net和port所属的bucket。

hinfo->bhash是个hashtable结构,对net和port进行hash,找到其所属的bucket列表,该列表头赋值给head字段,类型为struct inet_bind_hashbucket。

2. 遍历bucket列表,看是否存在net、port相同的条目。

3. 如果存在,检查是否冲突,不冲突则继续,冲突则抛错给上层。

4. 如果不存在,则创建一个新的inet_bind_bucket实例tb,并将其放入head指向的列表中,供后续做冲突检查。

5. 调用inet_bind_hash方法,设置sk为tb的owner。

// net/ipv4/inet_hashtables.c void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb, const unsigned short snum) { inet_sk(sk)->inet_num = snum; sk_add_bind_node(sk, &tb->owners); inet_csk(sk)->icsk_bind_hash = tb; }

这个方法里也同时设置了inet_sk(sk)->inet_num的值。

6. 返回err状态给上层。

综上,inet_bind方法中先调用sk->sk_prot->get_port(sk, snum)方法,将inet_sk(sk)->inet_num的值设置为snum,即用户传入的端口,然后再在本方法中将inet->inet_num的值赋值给inet->inet_sport,所以,inet->inet_sport的最终值就是用户传入的端口。

完。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linux内核及JVM底层相关技术研究 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档