socketpairシステムコール
Rev.2を表示中。最新版はこちら。
socketpairはソケットオペレーションコールバックの.socketpairで、AF_UNIXでのみ実装され、引数に応じたの2つのソケットを作成し、そのソケットペアーに互いのソケット設定する事で、相互のやり取りを実現します。sock1->->sk_pair= sock2 sock2->->sk_pair= sock1
newfile1->private_data = sock1 newfile2->private_data = sock2;
current->file[fd1] = newfile1 current->file[fd2] = newfile2;
usockvec[0] = fd1 usockvec[1] = fd2送受信はDGRAM/STREAMに応じたコールバックがコールされるだけで、互いのソケットに相手のソケット有してるのでサーバ(bind)/クライアント(connect)に掛かる実装は必要なく双方の送受が可能となります。
protocol引数は、ソケット作成のstruct net_proto_family *net_families[NPROTO]コールバックで、NPROTOに応じたcreatコールバックに依存します。PF_UNIXのcreatコールバックは0/1(PF_UNIX)でないとエラーとなるだけで、掛かる引数に依存する実装はありません。
SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol,
int __user *, usockvec)
{
struct socket *sock1, *sock2;
int fd1, fd2, err;
struct file *newfile1, *newfile2;
int flags;
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;
err = sock_create(family, type, protocol, &sock1);
if (err < 0)
goto out;
err = sock_create(family, type, protocol, &sock2);
if (err < 0)
goto out_release_1;
err = sock1->ops->socketpair(sock1, sock2);
if (err < 0)
goto out_release_both;
fd1 = sock_alloc_file(sock1, &newfile1, flags);
if (unlikely(fd1 < 0)) {
err = fd1;
goto out_release_both;
}
fd2 = sock_alloc_file(sock2, &newfile2, flags);
if (unlikely(fd2 < 0)) {
err = fd2;
fput(newfile1);
put_unused_fd(fd1);
sock_release(sock2);
goto out;
}
audit_fd_pair(fd1, fd2);
fd_install(fd1, newfile1);
fd_install(fd2, newfile2);
err = put_user(fd1, &usockvec[0]);
if (!err)
err = put_user(fd2, &usockvec[1]);
if (!err)
return 0;
sys_close(fd2);
sys_close(fd1);
return err;
out_release_both:
sock_release(sock2);
out_release_1:
sock_release(sock1);
out:
return err;
}
int sock_create(int family, int type, int protocol, struct socket **res)
{
return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}
int __sock_create(struct net *net, int family, int type, int protocol,
struct socket **res, int kern)
{
int err;
struct socket *sock;
const struct net_proto_family *pf;
if (family < 0 || family >= NPROTO)
return -EAFNOSUPPORT;
if (type < 0 || type >= SOCK_MAX)
return -EINVAL;
if (family == PF_INET && type == SOCK_PACKET) {
static int warned;
if (!warned) {
warned = 1;
printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n",
current->comm);
}
family = PF_PACKET;
}
err = security_socket_create(family, type, protocol, kern);
if (err)
return err;
sock = sock_alloc();
if (!sock) {
if (net_ratelimit())
printk(KERN_WARNING "socket: no more sockets\n");
return -ENFILE; /* Not exactly a match, but its the
closest posix thing */
}
sock->type = type;
#ifdef CONFIG_MODULES
if (rcu_access_pointer(net_families[family]) == NULL)
request_module("net-pf-%d", family);
#endif
rcu_read_lock();
pf = rcu_dereference(net_families[family]);
err = -EAFNOSUPPORT;
if (!pf)
goto out_release;
if (!try_module_get(pf->owner))
goto out_release;
rcu_read_unlock();
err = pf->create(net, sock, protocol, kern);
if (err < 0)
goto out_module_put;
if (!try_module_get(sock->ops->owner))
goto out_module_busy;
module_put(pf->owner);
err = security_socket_post_create(sock, family, type, protocol, kern);
if (err)
goto out_sock_release;
*res = sock;
return 0;
out_module_busy:
err = -EAFNOSUPPORT;
out_module_put:
sock->ops = NULL;
module_put(pf->owner);
out_sock_release:
sock_release(sock);
return err;
out_release:
rcu_read_unlock();
goto out_sock_release;
}
static int unix_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
if (protocol && protocol != PF_UNIX)
return -EPROTONOSUPPORT;
sock->state = SS_UNCONNECTED;
switch (sock->type) {
case SOCK_STREAM:
sock->ops = &unix_stream_ops;
break;
case SOCK_RAW:
sock->type = SOCK_DGRAM;
case SOCK_DGRAM:
sock->ops = &unix_dgram_ops;
break;
case SOCK_SEQPACKET:
sock->ops = &unix_seqpacket_ops;
break;
default:
return -ESOCKTNOSUPPORT;
}
return unix_create1(net, sock) ? 0 : -ENOMEM;
}
unix_stream_ops/unix_dgram_ops/unix_seqpacket_opsのコールバックで、socka->sk->sk_pair=sockb->sk sockb->sk->sk_pair=sockc->skとします。socka->sk->sk_state=TCP_ESTABLISHED socka->state=SS_CONNECTEDは、stream/seqpacketでは掛かる状態でないと送信できないからです。
#define unix_peer(sk) ((sk)->sk_pair)
static int unix_socketpair(struct socket *socka, struct socket *sockb)
{
struct sock *ska=socka->sk, *skb = sockb->sk;
sock_hold(ska);
sock_hold(skb);
unix_peer(ska)=skb;
unix_peer(skb)=ska;
ska->sk_peercred.pid = skb->sk_peercred.pid = current->tgid;
ska->sk_peercred.uid = skb->sk_peercred.uid = current->euid;
ska->sk_peercred.gid = skb->sk_peercred.gid = current->egid;
if (ska->sk_type != SOCK_DGRAM) {
ska->sk_state = TCP_ESTABLISHED;
skb->sk_state = TCP_ESTABLISHED;
socka->state = SS_CONNECTED;
sockb->state = SS_CONNECTED;
}
return 0;
}
検証サンプル
[root@localhost c]# cat socketpair.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
void main() {
char buff0[16], buff1[16];
int s_sockfd[2], d_sockfd[2];
const char *msg0 = "msg0";
const char *msg1 = "msg1";
socketpair(AF_UNIX, SOCK_STREAM, 0, s_sockfd);
socketpair(AF_UNIX, SOCK_DGRAM, 0, d_sockfd);
printf("SOCK_STREAM\n");
memset(buff0, 0, 16);
memset(buff1, 0, 16);
write(s_sockfd[0], msg0, strlen( msg0));
write(s_sockfd[1], msg1, strlen( msg1));
read(s_sockfd[0], buff0, sizeof( buff0));
read(s_sockfd[1], buff1, sizeof( buff1));
printf("write sock0:%s\n", msg0);
printf("write sock1:%s\n", msg1);
printf("read sock0:%s\n", buff0);
printf("read sock1:%s\n", buff1);
printf("SOCK_DGRAM\n");
memset(buff0, 0, 16);
memset(buff1, 0, 16);
write(d_sockfd[0], msg0, strlen( msg0));
write(d_sockfd[1], msg1, strlen( msg1));
read(d_sockfd[0], buff0, sizeof( buff0));
read(d_sockfd[1], buff1, sizeof( buff1));
printf("write sock0:%s\n", msg0);
printf("write sock1:%s\n", msg1);
printf("read sock0:%s\n", buff0);
printf("read sock1:%s\n", buff1);
}
結果[root@localhost c]# ./socketpair.o SOCK_STREAM write sock0:msg0 write sock1:msg1 read sock0:msg1 read sock1:msg0 SOCK_DGRAM write sock0:msg0 write sock1:msg1 read sock0:msg1 read sock1:msg0





