socketpairシステムコール
socketpairはソケットオペレーションコールバックの.socketpairで、AF_UNIXでのみ実装され、引数に応じたの2つのソケットを作成し、そのソケットペアーに互いのソケット設定する事で、相互のやり取りを実現します。
protocol引数は、ソケット作成のstruct net_proto_family *net_families[NPROTO]コールバックで、NPROTOに応じたcreatコールバックに依存します。PF_UNIXのcreatコールバックは0/1(PF_UNIX)でないとエラーとなるだけで、掛かる引数に依存する実装はありません。
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); write(s_sockfd[0], msg0, strlen( msg0)); write(s_sockfd[1], msg1, strlen( msg1)); write(d_sockfd[0], msg0, strlen( msg0)); write(d_sockfd[1], msg1, strlen( msg1)); memset(buff0, 0, 16); memset(buff1, 0, 16); read(s_sockfd[0], buff0, sizeof( buff0)); read(s_sockfd[1], buff1, sizeof( buff1)); printf("SOCK_STREAM\n"); printf("write sock0:%s\n", msg0); printf("write sock1:%s\n", msg1); printf("read sock0:%s\n", buff0); printf("read sock1:%s\n", buff1); memset(buff0, 0, 16); memset(buff1, 0, 16); read(d_sockfd[0], buff0, sizeof( buff0)); read(d_sockfd[1], buff1, sizeof( buff1)); printf("SOCK_DGRAM\n"); 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