接上篇《socket地址绑定》
三、根据不同的协议来完成绑定
上面代码中的第3步是根据应用程序在创建socket时传递到内核的协议域及socket类型来决定调用采用哪个方法,具体可以参考
创建socket
一文,这里不再赘述;下面以AF_IENT及SOCK_STREAM为例来说明绑定的过程;
1、调用链:
net/Socket.c:sys_bind()->net/ipv4/Af_inet.c:inet_bind();
2
、inet_bind()逻辑:
1) 地址类型检查
chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
if (!sysctl_ip_nonlocal_bind &&
!inet->freebind &&
addr->sin_addr.s_addr != htonl(INADDR_ANY) &&
chk_addr_ret != RTN_LOCAL &&
chk_addr_ret != RTN_MULTICAST &&
chk_addr_ret != RTN_BROADCAST)
goto out;
inet_addr_type()函数根据设置的ip地址检查其类型:
static inline unsigned __inet_dev_addr_type(struct net *net,
const struct net_device *dev,
__be32 addr)
{
……
if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
return RTN_BROADCAST;
if (ipv4_is_multicast(addr))
return RTN_MULTICAST;
……
local_table = fib_get_table(net, RT_TABLE_LOCAL);
if (local_table) {
ret = RTN_UNICAST;
if (!local_table->tb_lookup(local_table, &fl, &res)) {
if (!dev || dev == res.fi->fib_dev)
ret = res.type;
fib_res_put(&res);
}
}
return ret;
}
其中:
a.
ipv4_is_zeronet()用于检查地址的高8位是否为0,即地址是否为0.x.x.x,这类地址称为零网地址,零网地址也属于广播地址;
b.
ipv4_is_lbcast()用于检查地址是否是广播地址(广播地址有两种,一种是有限广播,即255.255.255.255,它不会被路由但是会发送到物理网段上的所有主机;另一种是直接广播,该类地址的主机字段为255,如192.168.1.255,该广播会路由到192.168.1网段的所有主机上);这里只是检查是否是有限广播地址;
c.
ipv4_is_multicast()用于检查地址是否是多播地址,即224.x.x.x的D类地址;
当ip地址既不是多播,也不是广播时,需要通过查找路由表来确定地址的类型(关于路由表,后面再叙述);
拿到地址类型后,inet_bind()函数会检查地址是否是单播、多播或广播地址;否则就直接出错并返回;
2) 端口范围检查
snum = ntohs(addr->sin_port);
if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
goto out;
/* Sockets 0-1023 can't be bound to unless you are superuser */
#define PROT_SOCK 1024
这里检查如果端口小于1024,且具有超级用户权限,否则直接出错并返回;
3) 设置源地址和接收地址
if (sk->sk_state != TCP_CLOSE || inet->num)
goto out_release_sock;
inet->rcv_saddr = inet->saddr = addr->sin_addr.s_addr;
if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
inet->saddr = 0; /* Use device */
这里先检查sock的状态,如果不是TCP_CLOSE或端口为0,则出错返回(这里也映射到创建socket时要将sock结构体变量的状态设置为TCP_CLOSE上了);
如果地址类型是多播或广播,则源地址设置为0,而接收地址为设置的ip地址;
4) 检查端口是否被占用
if (sk->sk_prot->get_port(sk, snum)) {
inet->saddr = inet->rcv_saddr = 0;
err = -EADDRINUSE;
goto out_release_sock;
}
这里根据创建socket协议族初始化时设置的sk_prot来判断端口是否被占用,如果被占用则直接出错返回;关于端口是否被占用,后面会有专门的一章来描述;
5) 初始化目标地址和端口
inet->sport = htons(inet->num);
inet->daddr = 0;
inet->dport = 0;
至此,地址绑定就完成了。
总结:
1、 根据文件描述符从进程描述符中取出相应的文件,再得到socket结构;
2、 检查ip地址的类型是否是单播、多播或广播;
3、 检查端口是否被占用;
分享到:
相关推荐
详解Linux协议栈的数据流向,SOCKET的操作流程,unicast multicast等等的区别。
很清楚的分析了一个数据包如何通过查询路由进入内核ipsec协议栈的处理、Linux 内核ipsec协议栈详细的加解密流程以及加解密完后如何将数据包发送出去。 文档中前半部分主要介绍一些关键的数据结构,及其相互之间的...
linux内核分析----初始化 linux内核分析----初始化 linux内核分析----初始化 linux内核分析----初始化
《Linux内核网络栈源代码情景分析》主要对Linux1.2.13内核协议栈的全部源代码做了详细的分析,该版本所有代码都在一个文件夹中,每种协议的实现都只有一个文件与之对应,分析该版本源代码可以方便读者迅速掌握Linux...
本书主要对 Linux 1.2.13 内核协议栈的全部源代码做了详细的分析, 该版本所有代码都在一个文件夹中,每种协议的实现都只有一个文件与之对应,分析该版本源代码可以方便读者迅速掌握 Linux 网络协议结构。...
《linux内核网络部分源码分析》,很不粗的linux内核和网络的资料,希望对你的学习工作有所帮助。
Linux内核网络部分源码分析-唐文
3.3 timestack数据包-Wireshark3.4 内核协议栈相关主要源码Time_wait状态生成及快速回收相关代码:开启timestamps引起的丢
详细描述了linux内核协议栈的实现原理及相关数据结构,为linux内核协议栈分析人员提供了重要参考。
主要对Linux1.2.13内核协议栈的全部源代码做了详细的分析,该版本所有代码都在一个文件夹中,每种协议的实现都只有一个文件与之对应,分析该版本源代码可以方便读者迅速掌握Linux网络协议结构。 压缩包最后一部分。
---------------------------------------------------Linux 中的各种栈:进程栈 线程栈 内核栈 中断栈
基于Linux内核的用户态网络协议栈的实现.pdf
文件名:linux-5.15.118.tar.xz 文件发布日期: 2023-06-21 说明: 该版本是长期支持版本.
[Linux内核源码].linux-2.6.16.18.tar.bz2.part3.rar [Linux内核源码].linux-2.6.16.18.tar.bz2.part3.rar [Linux内核源码].linux-2.6.16.18.tar.bz2.part3.rar [Linux内核源码].linux-2.6.16.18.tar.bz2.part3.rar
此Linux内核学习资料包中有Linux内核--网络栈实现分析(二)--数据包的传递过程(上).pdf Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上).pdf Linux内核--网络栈实现分析(四)--网络层之IP协议(上)....
linux-2.6.18内核基础上分析网络协议栈,适合初学者看
linux内核模块编程,编写一个模块,实现文件的读取。
[Linux内核源码].linux-2.6.16.18.tar.bz2.part2.rar [Linux内核源码].linux-2.6.16.18.tar.bz2.part2.rar [Linux内核源码].linux-2.6.16.18.tar.bz2.part2.rar [Linux内核源码].linux-2.6.16.18.tar.bz2.part2.rar
深入,对于想深入了解linux内核协议栈的有帮助
Linux网络体系结构 Linux内核中网络协议的设计与实现,Linux网络体系结构 Linux内核中网络协议的设计与实现