本文主要介绍了getsockname()和getpeername()的内核实现。
内核版本:3.6
Author:zhangskd @ csdn blog
int getsockname(int s, struct sockaddr *name, socklen_t *namelen);
Get the current name for the specified socket.
获取本地套接口的名字,包括它的IP和端口。
int getpeername(int s, struct sockaddr *name, socklen_t *namelen);
Get the name of connected peer socket.
获取远程套接口的名字,包括它的IP和端口。
getsockname()在指定的套接口绑定地址和端口后才能调用,即服务器在bind()后可调用,
客户端在bind()或connect()之后可调用。getpeername()在连接建立之后才可调用。
getsockname()和getpeername()是由glibc提供的,声明位于include/sys/socket.h中,实现位于
sysdeps/mach/hurd/getsockname.c和sysdeps/mach/hurd/getpeername.c中。它们主要是用于从用户空间
进入名为sys_socketcall的系统调用,并传递参数。sys_socketcall()实际上是所有socket函数进入内核空间
的共同入口。
在sys_socketcall()中会调用sys_getsockname()和sys_getpeername()。
SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args) { ... switch(call) { case SYS_GETSOCKNAME: err = sys_getsockname(a0, (struct sockaddr __user *)a1, (int __user *)a[2]); break; case SYS_GETPEERNAME: err = sys_getpeername(a0, (struct sockaddr __user *)a1, (int __user *)a[2]); break; ... } return err; }
经过了Socket层的总入口sys_socketcall(),现在进入sys_getsockname()。
/* Get the local address ('name') of a socket object. * Move the obtained name to user space. */ SYSCALL_DEFINE3(getsockname, int, fd, struct sockaddr __user *, usockaddr, int __user *, usockaddr_len) { struct socket *sock; struct sockaddr_storage address; int len, err, fput_needed; /* 通过文件描述符fd,找到对应的socket。 * 以fd为索引从当前进程的文件描述符表files_struct中找到对应的file实例, * 然后从file实例的private_data成员中获取socket实例。 */ sock = sockfd_lookup_light(fd, &err, &fput_needed); if (! sock) goto out; err = security_socket_getsockname(sock); /* SELinux相关 */ if (err) goto out_put; /* SOCKET层的操作函数,如果是SOCK_STREAM,proto_ops为inet_stream_ops, * 接下来调用inet_getname()。 */ err = sock->ops->getname(sock, (struct sockaddr *)&address, &len, 0); if (err) goto out_put; /* 把内核空间的socket地址复制到用户空间 */ err = move_addr_to_user(&address, len, usockaddr, usockaddr_len); out_put: fput_light(sock->file, fput_needed); out: return err; }
static int move_addr_to_user(struct sockaddr_storage *kaddr, int klen, void __user *uaddr, int __user *ulen) { int err; int len; /* 把用户空间的地址长度保存到len */ err = get_user(len, ulen); if (err) return err; if (len > klen) len = klen; if (len < 0 || len > sizeof(struct sockaddr_storage)) return -EINVAL; if (len) { if (audit_sockaddr(klen, kaddr)) return -ENOMEM; if (copy_to_user(uaddr, kaddr, len)) /* 拷贝到用户空间 */ return -EFAULT; } return __put_user(klen, ulen); /* 保存socket地址长度到用户空间 */ }
sys_getpeername()和sys_getsockname()差不多。
/* Get the remote address ('name') of a socket object. * Move the obtained name to user space. */ SYSCALL_DEFINE3(getpeername, int, fd, struct sockaddr __user *, usockaddr, int __user *, usockaddr_len) { struct socket *sock; struct sockaddr_storage address; int len, err, fput_needed; /* 通过文件描述符fd,找到对应的socket。 * 以fd为索引从当前进程的文件描述符表files_struct中找到对应的file实例, * 然后从file实例的private_data成员中获取socket实例。 */ sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock != NULL) { err = security_socket_getpeername(sock); if (err) { fput_light(sock->file, fput_needed); return err; } /* SOCKET层的操作函数,如果是SOCK_STREAM,proto_ops为inet_stream_ops, * 接下来调用inet_getname()。 */ err = sock->ops->getname(sock, (struct sockaddr *)&address, &len, 1); if (! err) /* 把内核空间的socket地址复制到用户空间 */ err = move_addr_to_user(&address, len, usockaddr, usockaddr_len); fput_light(sock->file, fput_needed); } return err; }
SOCK_STREAM套接口的socket层操作函数集实例为inet_stream_ops。
getsockname()和getpeername()的socket层操作函数为inet_getname()。
const struct proto_ops inet_stream_ops = { .family = PF_INET, .owner = THIS_MODULE, ... .getname = inet_getname, ... };
如果是getsockname(),则peer为0。是getpeername(),则peer为1。
/* This does both peername and sockname. */ int inet_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { struct sock *sk = sock->sk; struct inet_sock *inet = inet_sk(sk); DECLARE_SOCKADDR(struct sockaddr_in *, sin, uaddr); sin->sin_family = AF_INET; if (peer) { /* 如果是getpeername,要求连接已经建立 */ if (! inet->inet_dport || (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_SYN_SENT)) && peer == 1)) return -ENOTCONN; /* Transport endpoint is not connected */ sin->sin_port = inet->inet_dport; /* 获取对端端口 */ sin->sin_addr.s_addr = inet->inet_daddr; /* 获取对端IP */ } else { __be32 addr = inet->inet_rcv_saddr; if (! addr) addr = inet->inet_saddr; sin->sin_port = inet->inet_sport; /* 获取本端端口 */ sin->sin_addr.s_addr = addr; /* 获取本端IP */ } memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); *uaddr_len = sizeof(*sin); return 0; } #define DECLARE_SOCKADDR(type, dst, src) \ type dst = ({ __sockaddr_check_size(sizeof(*dst)); (type) src; }) #define __sockaddr_check_size(size) \ BUILD_BUG_ON(((size) > sizeof(struct __kernel_sockaddr_storage)))