Linux内核网络协议栈6-socket地址绑定(2)

三、根据不同的协议来完成绑定  
上面代码中的第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、 检查端口是否被占用;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值