内核中的TCP的追踪分析-3-TCP(IPV4)的socket的地址绑定
来源:互联网 发布:苹果电脑做淘宝方便吗? 编辑:程序博客网 时间:2024/06/10 12:46
今天我们继续内核中的TCP的socket的学习,同样按照Unix那节中的socket地址绑定的路线我们来分析一下,我们看到在以前的练习中有这样的绑定代码
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
上面的练习请参考http://blog.chinaunix.net/u2/64681/showart_1280050.html2-socket的实践到内核--socket使用IP地址通讯的那节练习
首先也是进入系统调用的总入口处sys_socketcall()系统调用处 case SYS_BIND:
err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
break;
move_addr_to_kernel()拷贝到局部数组变量char address[MAX_SOCK_ADDR]中。我们看一下练习中的声明的
struct sockaddr_in server_address;我们以前说过unix的socket使用的地址结构是struct sockaddr_un,而结构struct sockaddr_in我们还没有看过/* Structure describing an Internet (IP) socket address. */
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
server_address.sin_port = 9734;
__pad
数组我们没看到对他的赋值和操作,这个数组主要作用填充保障我们的地址数据结构与通用的
sockaddr
数据结构保持相同的大小。而
sockaddr
结构我们在
http://blog.chinaunix.net/u2/64681/showart_1308464.html
章节中看到过了,不了解的朋友可以回过头去看一下,此后我们在应用程序中设置的地址结构的数据都复制到了
sys_bind()
函数中的数组变量
address
中了,接着我们看到会执行
sock->ops->bind(sock,(struct sockaddr *) address, addrlen);
在这里应该明白了上面__pad的作用了,确实是为了将我们的数据能够正确对应到sockaddr结构的大小起填充作用的。我们回忆一下昨天讲到的socket的创建过程中对ops钩子结构的操作,在那里将socket的ops通过answer结构变量转接入了
inet_stream_ops
,
所以这里会跳入这个钩子结构去执行,我们先看一下这个结构
const struct proto_ops inet_stream_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release,
.bind = inet_bind,
.connect = inet_stream_connect,
.socketpair = sock_no_socketpair,
.accept = inet_accept,
.getname = inet_getname,
.poll = tcp_poll,
.ioctl = inet_ioctl,
.listen = inet_listen,
.shutdown = inet_shutdown,
.setsockopt = sock_common_setsockopt,
.getsockopt = sock_common_getsockopt,
.sendmsg = tcp_sendmsg,
.recvmsg = sock_common_recvmsg,
.mmap = sock_no_mmap,
.sendpage = tcp_sendpage,
.splice_read = tcp_splice_read,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
.compat_getsockopt = compat_sock_common_getsockopt,
#endif
};
结构中其他的部分我们暂且不要关心,只注意
.bind= inet_bind这一句,也就是说会执行钩子函数inet_bind。我是无名小卒,请转载的朋友注明出处。这个函数在/net/ipv4/Af_inet.c中的449行处
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);
unsigned short snum;
int chk_addr_ret;
int err;
/* If the socket has its own bind function then use it. (RAW) */
if (sk->sk_prot->bind) {
err = sk->sk_prot->bind(sk, uaddr, addr_len);
goto out;
}
err = -EINVAL;
if (addr_len < sizeof(struct sockaddr_in))
goto out;
chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
/* Not specified by any standard per-se, however it breaks too
* many applications when removed. It is unfortunate since
* allowing applications to make a non-local bind solves
* several problems with systems using dynamic addressing.
* (ie. your servers still start up even if your ISDN link
* is temporarily down)
*/
err = -EADDRNOTAVAIL;
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;
snum = ntohs(addr->sin_port);
err = -EACCES;
if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
goto out;
/* We keep a pair of addresses. rcv_saddr is the one
* used by hash lookups, and saddr is used for transmit.
*
* In the BSD API these are the same except where it
* would be illegal to use them (multicast/broadcast) in
* which case the sending device address is used.
*/
lock_sock(sk);
/* Check these errors (active socket, double bind). */
err = -EINVAL;
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 */
/* Make sure we are allowed to bind here. */
if (sk->sk_prot->get_port(sk, snum)) {
inet->saddr = inet->rcv_saddr = 0;
err = -EADDRINUSE;
goto out_release_sock;
}
if (inet->rcv_saddr)
sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
if (snum)
sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
inet->sport = htons(inet->num);
inet->daddr = 0;
inet->dport = 0;
sk_dst_reset(sk);
err = 0;
out_release_sock:
release_sock(sk);
out:
return err;
}
可能函数看起来比较长,贴出完整的是为了朋友们在下面阅读的随时对照,但是请朋友们看完了我的分析自己再单独阅读一下上面的代码,这样有利于提高朋友阅读代码的能力同时也加深对函数的理解。在这里我们首先看到再次将通用的数据结构sockaddr转换成我们IP中作用的地址结构sockaddr_in,这个结构已经在上面看过了。并且从socket中取出sock结构转换成我们IP使用的sock专用的结构inet_sock。接着我们看到使用sock中的sk_prot钩子结构来调用bind,而sk_prot则在上一节创建socket时我们说到了他是如何挂钩的,这里直接进入挂入的
tcp_prot
结构变量,它是传输层的结构体,但是我们在那里没有看到他挂入的bind钩子函数。所以if (sk->sk_prot->bind) 语句就失效了。然而函数下面是检测一下地址长度是否正确。此后进入inet_addr_type来检查地址的类型
unsigned int inet_addr_type(struct net *net, __be32 addr)
{
return __inet_dev_addr_type(net, NULL, addr);
}
static inline unsigned __inet_dev_addr_type(struct net *net,
const struct net_device *dev,
__be32 addr)
{
struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
struct fib_result res;
unsigned ret = RTN_BROADCAST;
struct fib_table *local_table;
if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
return RTN_BROADCAST;
if (ipv4_is_multicast(addr))
return RTN_MULTICAST;
#ifdef CONFIG_IP_MULTIPLE_TABLES
res.r = NULL;
#endif
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;
}
在上面函数中有几个数据结构,首先是struct flowi结构用于路由的键值。我们看到他内部的nl_u是一个联合,联合内部有三个ip4_u、ip6_u以及dn_u结构体。所以上面的struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };用我们在练习中的
inet_addr("127.0.0.1")地址初始了这个路由键值结构变量fl。struct fib_result是返回路由查询结果用的,而struct fib_table则是路由表的结构体。函数中首先是检查确定使用本地的广播地址或者是多播地址。接着我们要结合参数net来分析,但是struct net我们还没有看过,我们看到上面传递下来的是sock_net(sk)给net的
static inline
struct net *sock_net(const struct sock *sk)
{
#ifdef CONFIG_NET_NS
return sk->sk_net;
#else
return &init_net;
#endif
}
Struct net
结构非常大,他是专门用于命名网络所使用的数据结构。我们这里不列出了,只不过根据上面sock_net取我们所使用的网络空间结构。我们在分配sock结构时在
sk_alloc
函数中曾经调用了
sock_net_set
对sk_net进行挂入操作
static inline
void sock_net_set(struct sock *sk, struct net *net)
{
#ifdef CONFIG_NET_NS
sk->sk_net = net;
#endif
}
而层层传递下来的net参数则是从在创建socket时
int sock_create(int family, int type, int protocol, struct socket **res)
{
return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}
这个current则是当前进程的task_struct结构,其内部有一个系统的命名空间的结构变量
/* namespaces */
struct nsproxy *nsproxy;
这个命名空间结构中则封装着系统进程所有的共享的命名空间
struct nsproxy {
atomic_t count;
struct uts_namespace *uts_ns;
struct ipc_namespace *ipc_ns;
struct mnt_namespace *mnt_ns;
struct pid_namespace *pid_ns;
struct user_namespace *user_ns;
struct net *net_ns;
};
在内核中CONFIG_NET_NS配置选项是为了让用户自定义自己的网络空间结构,即上面的net结构,可以看出2.6.26内核的灵活性,但是我们一般在内核中不会配置该项,所以这里应该是取得init_net,这个结构是什么时间被初始化的呢?明天继续本文。
转自:http://blog.chinaunix.net/uid-7960587-id-2035549.html
- 内核中的TCP的追踪分析-3-TCP(IPV4)的socket的地址绑定
- 内核中的TCP的追踪分析-5-再谈TCP(IPV4)的socket的地址绑定
- 内核中的TCP的追踪分析-4-TCP(IPV4)的socket的地址绑定-续
- 内核中的TCP的追踪分析-9-TCP(IPV4)的socket的地址绑定--续2
- 内核中的TCP的追踪分析-1-追踪TCP(IPV4)的socket的初始化
- 内核中的TCP的追踪分析-2-追踪TCP(IPV4)的socket的创建
- 内核中的TCP的追踪分析-6-TCP(IPV4)的socket的监听
- 内核中的TCP的追踪分析-7-TCP(IPV4)的socket接收连接
- 内核中的TCP的追踪分析-8-TCP(IPV4)的socket连接
- 内核中的TCP的追踪分析-9-TCP(IPV4)的socket连接-续1
- 内核中的TCP的追踪分析-10-TCP(IPV4)的socket连接-续2
- TCP socket ipv6与ipv4的区别
- 基于LINUX内核中的TCP/IP的核心过程分析
- 简单的tcp socket编程及分析
- 简单的tcp socket编程及分析
- 关于tcp socket的状态分析
- 3-socket的实践到内核--追踪socket到内核
- 3-socket的实践到内核--追踪socket到内核 .
- 米勒法则
- Android拍照,上传,预览综合
- JS代码大全
- WordPress SEO优化技巧
- 懒惰是最大的敌人
- 内核中的TCP的追踪分析-3-TCP(IPV4)的socket的地址绑定
- 找不到出路
- gamecenter peer - to -peer
- windows下输出tomcat应用日志到文件
- Target runtime Apache Tomcat v5.5 is not defined 错误维护
- 内核中的TCP的追踪分析-4-TCP(IPV4)的socket的地址绑定-续
- jrtplib介绍
- 全国省市数据库
- 排序总结+全排列