ソケットスペシャルファイル


sockfd_lookup_light()でfdからfile->private_dataのsockを取得し、sockコールバックバック.bindがコールされ、ソケットファイルが作成されます。

socketをソケットファイルinode番号をlist headとするunix_socket_table[]のインデックスにリストし、他プロセスがこのinode番号を介してsockの取得を可能とします。

なお、umyaddr->patname[0]=NULLまたはaddrlen=sizeof(short)の時、ソケットスペシャルファイルを作成する事なく、addrlen長でのumyaddrのチェックサム値をinode番号に代わるunix_socket_table[]のインデックスとします。

umyaddr->patname[0]=NUL;
strcpy(&umyaddr->patname[1], "babakaka");
addrlen= strlen("babakaka") + 1 + sizeof(short);
とするstruct sockaddrでbind/connectすると、ソケットファイルが作成される事なく、UNIXドメインソケットが利用できます。

また、addr_len == sizeof(short)ならunix_autobind()がコールされ、 sprintf(addr->name->sun_path+1, "%05x", ordernum++)とする、ハッシュとなります。addr->name->sun_path+1はaddr->name->sun_path[0]=NULLです。

umyaddrをカーネルメモリに転送し、そのアドレスをsock->sk->addrに設定されます。getsockname()システムコールはsock->sk->addrを取得し、子プロセスとUNIX ドメインソケットを介してやり取りする場合、ソケットファイルを作成することなく実装できます。
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, (struct sockaddr *)&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;
}
unix_bind()はUNIXドメインソケットの.bindコールバックです。addr_len == sizeof(short)の時、unix_autobind()で
動的にファイル名が作成され、uaddr->sun_path[0]=NULLの時、addr_lenとするuaddrをチェックサム値をhashとします。

そうでない場合、ソケットファイルが作成されます。kern_path_create()でソケットファイルの親ディレクトリpathを取得し、S_IFSOCKとするvfs_mknod()でソケットスペシャルファイルを作成、そのinode番号をインデックスとしてunix_socket_table[]をヘッドとするリストに登録します。
static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
       struct sock *sk = sock->sk;
       struct net *net = sock_net(sk);
       struct unix_sock *u = unix_sk(sk);
       struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
       char *sun_path = sunaddr->sun_path;
       struct dentry *dentry = NULL;
       struct path path;
       int err;
       unsigned hash;
       struct unix_address *addr;
       struct hlist_head *list;

       err = -EINVAL;
       if (sunaddr->sun_family != AF_UNIX)
               goto out;

       if (addr_len == sizeof(short)) {
               err = unix_autobind(sock);
               goto out;
       }

       err = unix_mkname(sunaddr, addr_len, &hash);
       if (err < 0)
               goto out;
       addr_len = err;

       mutex_lock(&u->readlock);

       err = -EINVAL;
       if (u->addr)
               goto out_up;

       err = -ENOMEM;
       addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL);
       if (!addr)
               goto out_up;

       memcpy(addr->name, sunaddr, addr_len);
       addr->len = addr_len;
       addr->hash = hash ^ sk->sk_type;
       atomic_set(&addr->refcnt, 1);

       if (sun_path[0]) {
               umode_t mode;
               err = 0;

               dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0);
               err = PTR_ERR(dentry);
               if (IS_ERR(dentry))
                       goto out_mknod_parent;

               mode = S_IFSOCK |
                      (SOCK_INODE(sock)->i_mode & ~current_umask());
               err = mnt_want_write(path.mnt);
               if (err)
                       goto out_mknod_dput;
               err = security_path_mknod(&path, dentry, mode, 0);
               if (err)
                       goto out_mknod_drop_write;
               err = vfs_mknod(path.dentry->d_inode, dentry, mode, 0);
out_mknod_drop_write:
               mnt_drop_write(path.mnt);
               if (err)
                       goto out_mknod_dput;
               mutex_unlock(&path.dentry->d_inode->i_mutex);
               dput(path.dentry);
               path.dentry = dentry;

               addr->hash = UNIX_HASH_SIZE;
       }

       spin_lock(&unix_table_lock);

       if (!sun_path[0]) {
               err = -EADDRINUSE;
               if (__unix_find_socket_byname(net, sunaddr, addr_len,
                                             sk->sk_type, hash)) {
                       unix_release_addr(addr);
                       goto out_unlock;
               }

               list = &unix_socket_table[addr->hash];
       } else {
               list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1)];
               u->dentry = path.dentry;
               u->mnt    = path.mnt;
       }

       err = 0;
       __unix_remove_socket(sk);
       u->addr = addr;
       __unix_insert_socket(list, sk);

out_unlock:
       spin_unlock(&unix_table_lock);
out_up:
       mutex_unlock(&u->readlock);
out:
       return err;

out_mknod_dput:
       dput(dentry);
       mutex_unlock(&path.dentry->d_inode->i_mutex);
       path_put(&path);
out_mknod_parent:
       if (err == -EEXIST)
               err = -EADDRINUSE;
       unix_release_addr(addr);
       goto out_up;
}
connectシステムコールは、ソケットの.connectコールバックがコールされ、unix_dgram_connect()はソケットタイプがSOCK_DGRAMの時は、unix_dgram_connect()がコールされます。

unix_find_other()でstruct sockaddr *addrのソケット(サーバサイド)otherを取得し、unix_peer(sk) = otherでsk->peer=otherとし、両ソケットを接続します。
static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
                             int alen, int flags)
{
       struct sock *sk = sock->sk;
       struct net *net = sock_net(sk);
       struct sockaddr_un *sunaddr = (struct sockaddr_un *)addr;
       struct sock *other;
       unsigned hash;
       int err;

       if (addr->sa_family != AF_UNSPEC) {
               err = unix_mkname(sunaddr, alen, &hash);
               if (err < 0)
                       goto out;
               alen = err;

               if (test_bit(SOCK_PASSCRED, &sock->flags) &&
                   !unix_sk(sk)->addr && (err = unix_autobind(sock)) != 0)
                       goto out;

restart:
               other = unix_find_other(net, sunaddr, alen, sock->type, hash, &err);
               if (!other)
                       goto out;

               unix_state_double_lock(sk, other);

               if (sock_flag(other, SOCK_DEAD)) {
                       unix_state_double_unlock(sk, other);
                       sock_put(other);
                       goto restart;
               }

               err = -EPERM;
               if (!unix_may_send(sk, other))
                       goto out_unlock;

               err = security_unix_may_send(sk->sk_socket, other->sk_socket);
               if (err)
                       goto out_unlock;

       } else {
               other = NULL;
               unix_state_double_lock(sk, other);
       }

       if (unix_peer(sk)) {
               struct sock *old_peer = unix_peer(sk);
               unix_peer(sk) = other;
               unix_state_double_unlock(sk, other);

               if (other != old_peer)
                       unix_dgram_disconnected(sk, old_peer);
               sock_put(old_peer);
       } else {
               unix_peer(sk) = other;
               unix_state_double_unlock(sk, other);
       }
       return 0;

out_unlock:
       unix_state_double_unlock(sk, other);
       sock_put(other);
out:
       return err;
}
unix_find_other()からコールされ、ソケットファイルinodeでのsock取得で、unix_socket_table[]リストのsock->sk->dentryのinodeと一致するsockを取得します。
static struct sock *unix_find_socket_byinode(struct inode *i)
{
       struct sock *s;
       struct hlist_node *node;

       spin_lock(&unix_table_lock);
       sk_for_each(s, node,
                   &unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]) {
               struct dentry *dentry = unix_sk(s)->dentry;

               if (dentry && dentry->d_inode == i) {
                       sock_hold(s);
                       goto found;
               }
       }
       s = NULL;
found:
       spin_unlock(&unix_table_lock);
       return s;
}
ハッシュによるsock取得は、unix_socket_table[]リストのsock->sk->addr->nameとstruct sockaddr_un *sunnameの一致するsockを取得します。
static struct sock *__unix_find_socket_byname(struct net *net,
                                             struct sockaddr_un *sunname,
                                             int len, int type, unsigned hash)
{
       struct sock *s;
       struct hlist_node *node;

       sk_for_each(s, node, &unix_socket_table[hash ^ type]) {
               struct unix_sock *u = unix_sk(s);

               if (!net_eq(sock_net(s), net))
                       continue;

               if (u->addr->len == len &&
                   !memcmp(u->addr->name, sunname, len))
                       goto found;
       }
       s = NULL;
found:
       return s;
}

static inline struct sock *unix_find_socket_byname(struct net *net,
                                                  struct sockaddr_un *sunname,
                                                  int len, int type,
                                                  unsigned hash)
{
       struct sock *s;

       spin_lock(&unix_table_lock);
       s = __unix_find_socket_byname(net, sunname, len, type, hash);
       if (s)
               sock_hold(s);
       spin_unlock(&unix_table_lock);
       return s;
}

補足

ソケットファイル作成は、 mode | S_IFSOCKとし、vfs_mknod()で作成されます。作成する親ディレクトリ.mknodコールバックがコールされ、ファイルシステム依存したinodeを取得し、init_special_inode()でinode->i_fop = &bad_sock_fopsとします。bad_sock_fopsの.openコールバックはエラーとし、ファイルとしての機能を有しません。

mknodシステムコールもvfs_mknod()でスペシャルファイルが作成されるため、mknodシステムコールからでもソケットファイルは作成可能ですが、vfs_mknod()をユーザ/カーネル両空間からコールする故、mknodシステムコールでソケットファイルの作成は実用故でありません。
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
       int error = may_create(dir, dentry);

       if (error)
               return error;

       if ((S_ISCHR(mode) || S_ISBLK(mode)) &&
           !ns_capable(inode_userns(dir), CAP_MKNOD))
               return -EPERM;

       if (!dir->i_op->mknod)
               return -EPERM;

       error = devcgroup_inode_mknod(mode, dev);
       if (error)
               return error;

       error = security_inode_mknod(dir, dentry, mode, dev);
       if (error)
               return error;

       error = dir->i_op->mknod(dir, dentry, mode, dev);
       if (!error)
               fsnotify_create(dir, dentry);
       return error;
}

const struct file_operations bad_sock_fops = {
       .owner = THIS_MODULE,
       .open = sock_no_open,
       .llseek = noop_llseek,
};

static int sock_no_open(struct inode *irrelevant, struct file *dontcare)
{
       return -ENXIO;
}

void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
       inode->i_mode = mode;
       if (S_ISCHR(mode)) {
               inode->i_fop = &def_chr_fops;
               inode->i_rdev = rdev;
       } else if (S_ISBLK(mode)) {
               inode->i_fop = &def_blk_fops;
               inode->i_rdev = rdev;
       } else if (S_ISFIFO(mode))
               inode->i_fop = &def_fifo_fops;
       else if (S_ISSOCK(mode))
               inode->i_fop = &bad_sock_fops;
       else
               printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
                                 " inode %s:%lu\n", mode, inode->i_sb->s_id,
                                 inode->i_ino);
}

補足

ソケットファイルはユーザが作成する意味はなく、またざくっと走査したレベルですが、カーネルとしても、UNIXドメインソケット以外では使用されないようです。


最終更新 2015/11/25 22:23:29 - north
(2015/11/25 22:23:29 作成)


検索

アクセス数
3585535
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。