Linux从用户层到内核层系列 – TCP/IP协议栈部分系列10:linux内核协议栈中对于socket相关API的实现

释放双眼,带上耳机,听听看~!

linux内核协议栈中对于socket相关API的实现

首先应该做的事情

定义好了内核中断向量表之后,需要做的就是当用户层程序陷入到内核态之后,通过内核中断向量表找到了内核中对于该系统调用的实现。补充一下内核中SYSCALL_DEFINE的用法:

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol){ ….}

<=>
SYSCALL_DEFINEX(3,_socket,VA_ARGS)

<=>
_SYSCALL_DEFINE(3,_socket,VA_ARGS)

<=>
asmlinkage long sys_socket(int family,int type,int protocol)

SYSCALL_DEFINE* 把内核中断向量表和内核实现完美的衔接了起来。

用户层API内核是如何实现的

以socket相关的套接字编程接口为例(linux 3.9.3):

socket.c:1382:SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
socket.c:1423:SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol,
socket.c:1519:SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
socket.c:1548:SYSCALL_DEFINE2(listen, int, fd, int, backlog)
socket.c:1581:SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
socket.c:1662:SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,
socket.c:1680:SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,
socket.c:1712:SYSCALL_DEFINE3(getsockname, int, fd, struct sockaddr __user *, usockaddr,
socket.c:1743:SYSCALL_DEFINE3(getpeername, int, fd, struct sockaddr __user *, usockaddr,
socket.c:1775:SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
socket.c:1822:SYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len,
socket.c:1834:SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
socket.c:1890:SYSCALL_DEFINE5(setsockopt, int, fd, int, level, int, optname,
socket.c:1924:SYSCALL_DEFINE5(getsockopt, int, fd, int, level, int, optname,
socket.c:1954:SYSCALL_DEFINE2(shutdown, int, fd, int, how)
socket.c:2096:SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned int, flags)
socket.c:2171:SYSCALL_DEFINE4(sendmmsg, int, fd, struct mmsghdr __user *, mmsg,
socket.c:2269:SYSCALL_DEFINE3(recvmsg, int, fd, struct msghdr __user *, msg,
socket.c:2393:SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg,
socket.c:2435:SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)

然后我们看相关的源代码以socket和bind为例:

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{

int retval;

struct socket *sock;

int flags;

/* Check the SOCK_* constants for consistency.  */

BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);

BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);

BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);

BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);

flags = type & ~SOCK_TYPE_MASK;

if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))

return -EINVAL;

type &= SOCK_TYPE_MASK;

if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))

flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;

retval = sock_create(family, type, protocol, &sock);

if (retval < 0)

goto out;

retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));

if (retval < 0)

goto out_release;
out:

/* It may be already another descriptor 8) Not kernel problem. */

return retval;
out_release:

sock_release(sock);

return retval;
}

SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
{

struct socket *sock;

struct sockaddr_storage address;

int err, fput_needed;

sock = sockfd_lookup_light(fd, &err, &fput_needed);

if (sock) {

err = move_addr_to_kernel(umyaddr, addrlen, &address);

if (err >= 0) {

err = security_socket_bind(sock,

  (struct sockaddr *)&address,

  addrlen);

if (!err)

err = sock->ops->bind(sock,

     (struct sockaddr *)

     &address, addrlen);

}

fput_light(sock->file, fput_needed);

}

return err;
}

我们可以看到,只要抓住了主要的脉络,分析内核协议栈是很简单的事情,用侯捷先生的话说“源码在手,了无秘密”。

给TA打赏
共{{data.count}}人
人已打赏
安全运维

WordPress网站专用docker容器环境带Waf

2020-7-18 20:04:44

安全运维

运维安全-Gitlab管理员权限安全思考

2021-9-19 9:16:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索