summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorBernard Xiong <[email protected]>2026-08-17 17:15:34 +0800
committerGitHub <[email protected]>2026-08-17 17:15:34 +0800
commit991f0ffca6ca475adcffc7833876bb8151f16e9c (patch)
tree5b69ef28de0b15e1a1d23e53136d37c5e86a8a59 /components
parentd2acd7587eec680b57f8777c5c7a8dce7369471e (diff)
Add AF_UNIX sockets implementation (#11681)
* components/dfs: support unix socket nodes AF_UNIX pathname binding and descriptor passing require DFSv2 socket nodes and retained open file descriptions across fd tables. Add socket-node creation for tmpfs and devtmpfs, fd reference helpers, and socket F_SETFL forwarding. Impact: DFSv2 socket nodes and descriptor reference handling. Validation: git diff --cached --check. * components/net/sal: support local protocol providers Local IPC protocol families do not have a backing network device. Store the selected provider in each SAL socket and add a local provider registry while preserving netdev checks for Internet sockets. Handle DFSv2 close semantics, socketpair flags, and MSG_CTRUNC for AF_UNIX integration. Impact: SAL protocol dispatch for all socket families. Validation: git diff --cached --check. * components/lwp: support unix socket messages Musl AF_UNIX addresses and ancillary data require explicit ABI and user-memory conversion at the LWP syscall boundary. Add the musl msghdr layout, bounded address and message copying, control-message level conversion, and MSG_CTRUNC translation. Correct receive buffer allocation and copy lengths while handling messages. Impact: LWP socket syscalls when SAL is enabled. Validation: git diff --cached --check. * components/net/af_unix: add local sockets Add an opt-in AF_UNIX provider for pathname-based local IPC without a synthetic network device. Support datagram and stream sockets, blocking and nonblocking I/O, timeouts, poll, socketpair, and SCM_RIGHTS descriptor passing. Include bounded Kconfig settings, component documentation, and utest coverage. Impact: enabled only by RT_USING_AF_UNIX and requires SAL POSIX with DFSv2. Validation: git diff --cached --check. * Fix AF_UNIX CI checks Apply the repository clang-format rules to the affected source lines. Suppress the cppcheck false positive for the devtmpfs list iterator. No functional behavior is changed. * components/lwp: preserve NULL optional msghdr buffers When msg_name or msg_control is NULL, keep the kernel pointer NULL instead of substituting an uninitialized buffer. Reject a NULL control buffer with a nonzero length as EFAULT. Impact: LWP sendmsg/recvmsg conversion on MMU targets. Validation: git diff --cached --check. * components/net/af_unix: fix poll UAF, namespace leak, and SCM_RIGHTS cycles Poll only the local wait queue and wake writers from the receiver. Free namespace entries on detach, and reject AF_UNIX descriptors in SCM_RIGHTS until cycle collection exists. Impact: AF_UNIX poll, bind lifetime, and descriptor passing. Validation: git diff --cached --check. * components/dfs: add DFSv2 AF_UNIX utest suite Add opt-in tests under dfs/utest/v2/af_unix for socket nodes, IPC, poll peer-close, namespace cleanup, and SCM_RIGHTS rejection. Sources are built only when RT_UTEST_TC_USING_DFS_V2_AF_UNIX and the selected test groups are enabled. Impact: DFS utest menu and build; no production AF_UNIX behavior change. Validation: git diff --cached --check.
Diffstat (limited to 'components')
-rw-r--r--components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c18
-rw-r--r--components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c16
-rw-r--r--components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h2
-rw-r--r--components/dfs/dfs_v2/include/dfs.h6
-rw-r--r--components/dfs/dfs_v2/include/dfs_file.h1
-rw-r--r--components/dfs/dfs_v2/src/dfs.c114
-rw-r--r--components/dfs/dfs_v2/src/dfs_file.c133
-rw-r--r--components/dfs/utest/Kconfig3
-rw-r--r--components/dfs/utest/SConscript6
-rw-r--r--components/dfs/utest/v2/af_unix/Kconfig95
-rw-r--r--components/dfs/utest/v2/af_unix/SConscript27
-rw-r--r--components/dfs/utest/v2/af_unix/tc_af_unix.h39
-rw-r--r--components/dfs/utest/v2/af_unix/tc_af_unix_common.c98
-rw-r--r--components/dfs/utest/v2/af_unix/tc_af_unix_ipc.c291
-rw-r--r--components/dfs/utest/v2/af_unix/tc_af_unix_namespace.c175
-rw-r--r--components/dfs/utest/v2/af_unix/tc_af_unix_node.c79
-rw-r--r--components/dfs/utest/v2/af_unix/tc_af_unix_poll.c211
-rw-r--r--components/dfs/utest/v2/af_unix/tc_af_unix_rights.c219
-rw-r--r--components/lwp/lwp_sys_socket.h27
-rw-r--r--components/lwp/lwp_syscall.c529
-rw-r--r--components/net/Kconfig1
-rw-r--r--components/net/af_unix/Kconfig42
-rw-r--r--components/net/af_unix/README.md57
-rw-r--r--components/net/af_unix/SConscript12
-rw-r--r--components/net/af_unix/include/af_unix.h20
-rw-r--r--components/net/af_unix/src/af_unix_core.c1149
-rw-r--r--components/net/af_unix/src/af_unix_dgram.c253
-rw-r--r--components/net/af_unix/src/af_unix_internal.h164
-rw-r--r--components/net/af_unix/src/af_unix_namespace.c292
-rw-r--r--components/net/af_unix/src/af_unix_rights.c364
-rw-r--r--components/net/af_unix/src/af_unix_stream.c483
-rw-r--r--components/net/af_unix/testcases/af_unix_test.c767
-rw-r--r--components/net/sal/include/sal_low_lvl.h5
-rw-r--r--components/net/sal/include/sal_socket.h3
-rw-r--r--components/net/sal/socket/net_sockets.c20
-rw-r--r--components/net/sal/src/sal_socket.c246
36 files changed, 5726 insertions, 241 deletions
diff --git a/components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c b/components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c
index 82ab422529..b60e5c5d0f 100644
--- a/components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c
+++ b/components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c
@@ -27,6 +27,7 @@
#define TMPFS_TYPE_FILE 0x00
#define TMPFS_TYPE_DIR 0x01
#define TMPFS_TYPE_DYN_DEV 0x02 /* dynamic device */
+#define TMPFS_TYPE_SOCKET 0x03
struct devtmpfs_sb;
@@ -223,6 +224,7 @@ find_subpath:
if (rt_strcmp(file->name, filename) == 0)
{
rt_spin_unlock(&superblock->lock);
+ /* cppcheck-suppress uninitvar */
return file;
}
}
@@ -325,6 +327,10 @@ static int devtmpfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_
{
d->d_type = DT_DIR;
}
+ if (n_file->type == TMPFS_TYPE_SOCKET)
+ {
+ d->d_type = DT_SOCK;
+ }
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
rt_strncpy(d->d_name, n_file->name, DIRENT_NAME_MAX);
@@ -540,6 +546,14 @@ static struct dfs_vnode *devtmpfs_create_vnode(struct dfs_dentry *dentry, int ty
vnode->mode &= ~S_IFMT;
vnode->mode |= S_IFDIR;
}
+ else if (type == FT_SOCKET ||
+ (type == FT_REGULAR && S_ISSOCK(mode)))
+ {
+ d_file->type = TMPFS_TYPE_SOCKET;
+ vnode->type = FT_SOCKET;
+ vnode->mode &= ~S_IFMT;
+ vnode->mode |= S_IFSOCK;
+ }
else
{
d_file->type = TMPFS_TYPE_FILE;
@@ -585,6 +599,10 @@ static struct dfs_vnode *devtmpfs_lookup(struct dfs_dentry *dentry)
{
vnode->type = FT_DIRECTORY;
}
+ else if (d_file->type == TMPFS_TYPE_SOCKET)
+ {
+ vnode->type = FT_SOCKET;
+ }
else if (d_file->link)
{
vnode->type = FT_SYMLINK;
diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c
index 011e6e14c2..cc511415a3 100644
--- a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c
+++ b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c
@@ -535,6 +535,10 @@ static int dfs_tmpfs_getdents(struct dfs_file *file,
{
d->d_type = DT_DIR;
}
+ if (n_file->type == TMPFS_TYPE_SOCKET)
+ {
+ d->d_type = DT_SOCK;
+ }
d->d_namlen = RT_NAME_MAX;
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
rt_strncpy(d->d_name, n_file->name, TMPFS_NAME_MAX);
@@ -664,6 +668,11 @@ static struct dfs_vnode *_dfs_tmpfs_lookup(struct dfs_dentry *dentry)
vnode->mode = S_IFDIR | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
vnode->type = FT_DIRECTORY;
}
+ else if (d_file->type == TMPFS_TYPE_SOCKET)
+ {
+ vnode->mode = S_IFSOCK | (S_IRWXU | S_IRWXG | S_IRWXO);
+ vnode->type = FT_SOCKET;
+ }
else
{
vnode->mode = S_IFREG | (S_IRWXU | S_IRWXG | S_IRWXO);
@@ -750,6 +759,13 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t
vnode->mode = S_IFDIR | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
vnode->type = FT_DIRECTORY;
}
+ else if (type == FT_SOCKET ||
+ (type == FT_REGULAR && S_ISSOCK(mode)))
+ {
+ d_file->type = TMPFS_TYPE_SOCKET;
+ vnode->mode = S_IFSOCK | (mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+ vnode->type = FT_SOCKET;
+ }
else
{
d_file->type = TMPFS_TYPE_FILE;
diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h
index 8f822c6f78..0807f06d19 100644
--- a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h
+++ b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h
@@ -19,6 +19,7 @@
#define TMPFS_TYPE_FILE 0x00
#define TMPFS_TYPE_DIR 0x01
+#define TMPFS_TYPE_SOCKET 0x02
struct tmpfs_sb;
@@ -46,4 +47,3 @@ struct tmpfs_sb
int dfs_tmpfs_init(void);
#endif
-
diff --git a/components/dfs/dfs_v2/include/dfs.h b/components/dfs/dfs_v2/include/dfs.h
index 582ec428ac..a5128b2678 100644
--- a/components/dfs/dfs_v2/include/dfs.h
+++ b/components/dfs/dfs_v2/include/dfs.h
@@ -134,6 +134,12 @@ int fdt_fd_associate_file(struct dfs_fdtable *fdt, int fd, struct dfs_file *file
struct dfs_file *fd_get(int fd);
void fd_release(int fd);
+/* Reference helpers used when an open file description crosses fd tables. */
+int dfs_file_get_refs(const int *fds, size_t count, struct dfs_file **files);
+/* Successful installation transfers the supplied references to the fd table. */
+int dfs_file_install_refs(struct dfs_file **files, size_t count, int *fds);
+void dfs_file_put_ref(struct dfs_file *file);
+
void fd_init(struct dfs_file *fd);
struct dfs_fdtable *dfs_fdtable_get(void);
diff --git a/components/dfs/dfs_v2/include/dfs_file.h b/components/dfs/dfs_v2/include/dfs_file.h
index 0e609e9eef..280d0dbcb9 100644
--- a/components/dfs/dfs_v2/include/dfs_file.h
+++ b/components/dfs/dfs_v2/include/dfs_file.h
@@ -149,6 +149,7 @@ void dfs_file_init(struct dfs_file *file);
void dfs_file_deinit(struct dfs_file *file);
int dfs_file_open(struct dfs_file *file, const char *path, int flags, mode_t mode);
+int dfs_file_mknod(const char *path, int type, mode_t mode);
int dfs_file_close(struct dfs_file *file);
off_t dfs_file_get_fpos(struct dfs_file *file);
diff --git a/components/dfs/dfs_v2/src/dfs.c b/components/dfs/dfs_v2/src/dfs.c
index de1ea79edc..92c688a8a0 100644
--- a/components/dfs/dfs_v2/src/dfs.c
+++ b/components/dfs/dfs_v2/src/dfs.c
@@ -349,23 +349,29 @@ int fdt_fd_new(struct dfs_fdtable *fdt)
*/
void fdt_fd_release(struct dfs_fdtable *fdt, int fd)
{
- if (fd < fdt->maxfd)
+ if (fdt == RT_NULL || dfs_file_lock() != RT_EOK)
+ {
+ return;
+ }
+
+ if (fd >= 0 && fd < (int)fdt->maxfd)
{
struct dfs_file *file;
file = fdt_get_file(fdt, fd);
- if (file && file->ref_count == 1)
+ if (file != RT_NULL && file->ref_count == 1)
{
dfs_file_destroy(file);
}
- else
+ else if (file != RT_NULL)
{
rt_atomic_sub(&(file->ref_count), 1);
}
fdt->fds[fd] = RT_NULL;
}
+ dfs_file_unlock();
}
/**
@@ -493,6 +499,106 @@ struct dfs_file *fd_get(int fd)
return fdt_get_file(fdt, fd);
}
+int dfs_file_get_refs(const int *fds, size_t count, struct dfs_file **files)
+{
+ size_t index;
+ struct dfs_fdtable *fdt;
+
+ if ((count != 0 && (fds == RT_NULL || files == RT_NULL)) ||
+ dfs_file_lock() != RT_EOK)
+ {
+ return -EINVAL;
+ }
+
+ fdt = dfs_fdtable_get();
+ for (index = 0; index < count; index++)
+ {
+ files[index] = fdt_get_file(fdt, fds[index]);
+ if (files[index] == RT_NULL ||
+ (files[index]->dentry == RT_NULL && files[index]->vnode == RT_NULL))
+ {
+ dfs_file_unlock();
+ return -EBADF;
+ }
+ }
+
+ for (index = 0; index < count; index++)
+ {
+ rt_atomic_add(&files[index]->ref_count, 1);
+ }
+ dfs_file_unlock();
+ return 0;
+}
+
+int dfs_file_install_refs(struct dfs_file **files, size_t count, int *fds)
+{
+ int fd;
+ int startfd;
+ size_t index;
+ struct dfs_fdtable *fdt;
+
+ if ((count != 0 && (files == RT_NULL || fds == RT_NULL)) ||
+ dfs_file_lock() != RT_EOK)
+ {
+ return -EINVAL;
+ }
+
+ fdt = dfs_fdtable_get();
+ startfd = (fdt == &_fdtab) ? DFS_STDIO_OFFSET : 0;
+ for (index = 0; index < count; index++)
+ {
+ if (files[index] == RT_NULL || files[index]->magic != DFS_FD_MAGIC)
+ {
+ break;
+ }
+
+ fd = _fdt_slot_alloc(fdt, startfd);
+ if (fd < 0)
+ {
+ break;
+ }
+ fdt->fds[fd] = files[index];
+ fds[index] = fd;
+ }
+
+ if (index != count)
+ {
+ while (index > 0)
+ {
+ index--;
+ fdt->fds[fds[index]] = RT_NULL;
+ }
+ dfs_file_unlock();
+ return -EMFILE;
+ }
+
+ dfs_file_unlock();
+ return 0;
+}
+
+void dfs_file_put_ref(struct dfs_file *file)
+{
+ if (file == RT_NULL || dfs_file_lock() != RT_EOK)
+ {
+ return;
+ }
+
+ if (file->magic == DFS_FD_MAGIC &&
+ rt_atomic_load(&file->ref_count) > 0 &&
+ dfs_file_close(file) == 0)
+ {
+ if (rt_atomic_load(&file->ref_count) == 1)
+ {
+ dfs_file_destroy(file);
+ }
+ else
+ {
+ rt_atomic_sub(&file->ref_count, 1);
+ }
+ }
+ dfs_file_unlock();
+}
+
/**
* This function will get the file descriptor table of current process.
*/
@@ -1238,4 +1344,4 @@ MSH_CMD_EXPORT(dfs_dlog, dfs dlog on|off);
#endif
#endif
-/** @} */ \ No newline at end of file
+/** @} */
diff --git a/components/dfs/dfs_v2/src/dfs_file.c b/components/dfs/dfs_v2/src/dfs_file.c
index 32a18dab3d..6b16159396 100644
--- a/components/dfs/dfs_v2/src/dfs_file.c
+++ b/components/dfs/dfs_v2/src/dfs_file.c
@@ -823,6 +823,128 @@ _ERR_RET:
return ret;
}
+int dfs_file_mknod(const char *path, int type, mode_t mode)
+{
+ int ret = -EINVAL;
+ int create_type = type;
+ mode_t create_mode = mode;
+ char *fullpath;
+ struct dfs_mnt *mnt;
+ struct dfs_dentry *dentry;
+
+ if (path == RT_NULL || type < FT_REGULAR || type > FT_NONLOCK)
+ {
+ return -EINVAL;
+ }
+
+ fullpath = dfs_normalize_path(RT_NULL, path);
+ if (fullpath == RT_NULL)
+ {
+ return -ENOMEM;
+ }
+
+ mnt = dfs_mnt_lookup(fullpath);
+ if (mnt == RT_NULL)
+ {
+ ret = -ENOENT;
+ goto __exit;
+ }
+
+ {
+ char *realpath;
+
+ realpath = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
+ if (realpath != RT_NULL)
+ {
+ rt_free(fullpath);
+ fullpath = realpath;
+ }
+ }
+
+ if (strcmp(mnt->fullpath, fullpath) == 0)
+ {
+ ret = -EEXIST;
+ goto __exit;
+ }
+
+ dentry = dfs_dentry_lookup(mnt, fullpath, 0);
+ if (dentry != RT_NULL)
+ {
+ dfs_dentry_unref(dentry);
+ ret = -EEXIST;
+ goto __exit;
+ }
+
+ if (mnt->fs_ops->create_vnode == RT_NULL)
+ {
+ ret = -ENOSYS;
+ goto __exit;
+ }
+
+ if (type == FT_SOCKET)
+ {
+ create_type = FT_REGULAR;
+ create_mode = (mode & ~S_IFMT) | S_IFSOCK;
+ }
+
+ ret = dfs_file_lock();
+ if (ret != RT_EOK)
+ {
+ goto __exit;
+ }
+
+ dentry = dfs_dentry_create(mnt, fullpath);
+ if (dentry != RT_NULL)
+ {
+ struct dfs_vnode *vnode = RT_NULL;
+
+ if (dfs_is_mounted(mnt) == 0)
+ {
+ vnode = mnt->fs_ops->create_vnode(dentry, create_type,
+ create_mode);
+ }
+ if (vnode != RT_NULL)
+ {
+ if (type == FT_SOCKET && !S_ISSOCK(vnode->mode))
+ {
+ if (mnt->fs_ops->unlink != RT_NULL)
+ {
+ dentry->vnode = vnode;
+ (void)mnt->fs_ops->unlink(dentry);
+ dentry->vnode = RT_NULL;
+ }
+ dfs_vnode_unref(vnode);
+ ret = -EOPNOTSUPP;
+ }
+ else
+ {
+ vnode->type = type;
+ dentry->vnode = vnode;
+ dfs_dentry_insert(dentry);
+ ret = RT_EOK;
+ }
+ }
+ else
+ {
+ ret = -ENOENT;
+ }
+ }
+ else
+ {
+ ret = -ENOMEM;
+ }
+ dfs_file_unlock();
+
+ if (dentry != RT_NULL)
+ {
+ dfs_dentry_unref(dentry);
+ }
+
+__exit:
+ rt_free(fullpath);
+ return ret;
+}
+
/**
* @brief Close a file and release associated resources
*
@@ -1588,6 +1710,15 @@ int dfs_file_fcntl(int fd, int cmd, unsigned long arg)
O_APPEND | O_NONBLOCK;
flags &= mask;
+ if (file->vnode->type == FT_SOCKET && file->fops != RT_NULL &&
+ file->fops->ioctl != RT_NULL)
+ {
+ ret = file->fops->ioctl(file, F_SETFL, (void *)(rt_base_t)flags);
+ if (ret < 0)
+ {
+ break;
+ }
+ }
file->flags &= ~mask;
file->flags |= flags;
break;
@@ -3035,4 +3166,4 @@ void copy(const char *src, const char *dst)
}
FINSH_FUNCTION_EXPORT(copy, copy file or dir)
-#endif \ No newline at end of file
+#endif
diff --git a/components/dfs/utest/Kconfig b/components/dfs/utest/Kconfig
index dde4cffca0..32acadbf1a 100644
--- a/components/dfs/utest/Kconfig
+++ b/components/dfs/utest/Kconfig
@@ -40,5 +40,8 @@ if RT_USING_DFS
This device will be formatted and mounted during testing
endif
+
+ rsource "v2/af_unix/Kconfig"
+
endmenu
endif
diff --git a/components/dfs/utest/SConscript b/components/dfs/utest/SConscript
index 2375461741..bb8fa66a35 100644
--- a/components/dfs/utest/SConscript
+++ b/components/dfs/utest/SConscript
@@ -1,5 +1,6 @@
Import('rtconfig')
from building import *
+import os
cwd = GetCurrentDir()
src = []
@@ -15,6 +16,7 @@ if GetDepend('RT_UTEST_TC_USING_DFS_API'):
src += ['tc_posix_api.c']
# Define the test group with proper dependencies
-group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES', 'RT_USING_DFS'], CPPPATH = CPPPATH)
+objs = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES', 'RT_USING_DFS'], CPPPATH = CPPPATH)
+objs = objs + SConscript(os.path.join('v2', 'af_unix', 'SConscript'))
-Return('group')
+Return('objs')
diff --git a/components/dfs/utest/v2/af_unix/Kconfig b/components/dfs/utest/v2/af_unix/Kconfig
new file mode 100644
index 0000000000..775daf5814
--- /dev/null
+++ b/components/dfs/utest/v2/af_unix/Kconfig
@@ -0,0 +1,95 @@
+menuconfig RT_UTEST_TC_USING_DFS_V2_AF_UNIX
+ bool "DFSv2 AF_UNIX Test"
+ default n
+ depends on RT_USING_DFS_V2
+ depends on RT_USING_AF_UNIX
+ depends on SAL_USING_POSIX
+ help
+ Enable DFSv2 AF_UNIX unit tests. Sources are compiled only when
+ this option and the selected test groups below are enabled.
+
+ Coverage includes socket-node creation, transferable file
+ references, datagram and stream IPC, poll readiness, pathname
+ lifetime, namespace cleanup, and SCM_RIGHTS handling.
+
+if RT_UTEST_TC_USING_DFS_V2_AF_UNIX
+
+config RT_UTEST_DFS_V2_AF_UNIX_DIR
+ string "Directory for AF_UNIX pathname tests"
+ default "/dev"
+ help
+ Directory used to create temporary AF_UNIX pathname nodes.
+ The filesystem mounted here must support S_IFSOCK nodes
+ through create_vnode(), such as tmpfs or devtmpfs.
+
+config RT_UTEST_DFS_V2_AF_UNIX_NODE
+ bool "Socket node and file-reference tests"
+ default y
+ help
+ Build tests for DFSv2 socket nodes and transferable file
+ references:
+ * dfs_file_mknod(FT_SOCKET) creates an S_ISSOCK node
+ * creating the same path again returns EEXIST
+ * unlink() removes the socket node
+ * dfs_file_get_refs() rejects an invalid fd
+ * get_refs() + put_ref() keeps the original fd usable
+ * get_refs() + install_refs() transfers a pipe and shares
+ the open file description
+
+config RT_UTEST_DFS_V2_AF_UNIX_IPC
+ bool "Datagram, stream and socketpair IPC tests"
+ default y
+ help
+ Build tests for AF_UNIX IPC operations:
+ * SOCK_DGRAM bind, connect, send and recvfrom
+ * SOCK_STREAM listen, accept, send, recv and shutdown
+ * socketpair() for both SOCK_DGRAM and SOCK_STREAM
+ * sendmsg()/recvmsg() scatter-gather I/O
+ * connected sendmsg() with a NULL msg_name
+ * sendmsg() with a NULL msg_control and nonzero length
+ * listen() on SOCK_DGRAM returns EOPNOTSUPP
+ * connect() to a missing pathname returns ENOENT
+ * non-blocking recv() returns EAGAIN
+
+config RT_UTEST_DFS_V2_AF_UNIX_POLL
+ bool "Poll readiness and peer-close tests"
+ default y
+ help
+ Build tests for AF_UNIX poll/select readiness:
+ * empty stream socket reports POLLOUT and not POLLIN
+ * received data reports POLLIN
+ * filling the stream buffer clears POLLOUT; a peer recv
+ makes POLLOUT ready again
+ * blocking poll(POLLIN) wakes with POLLHUP/POLLIN when
+ the peer is closed (covers the peer-queue UAF case)
+ * datagram queue-full and POLLIN readiness
+
+config RT_UTEST_DFS_V2_AF_UNIX_NAMESPACE
+ bool "Pathname bind, unlink and namespace tests"
+ default y
+ help
+ Build tests for pathname lifetime and namespace cleanup:
+ * bind() creates a persistent S_IFSOCK node
+ * close() leaves the pathname in the filesystem
+ * rebinding without unlink() returns EADDRINUSE
+ * unlink() then bind() succeeds
+ * connect() after close without unlink() returns
+ ECONNREFUSED
+ * connect() after unlink() returns ENOENT
+ * repeated bind/close/unlink on distinct paths does not
+ leak namespace entries
+
+config RT_UTEST_DFS_V2_AF_UNIX_RIGHTS
+ bool "SCM_RIGHTS transfer and rejection tests"
+ default y
+ help
+ Build tests for SCM_RIGHTS descriptor passing:
+ * transfer pipe descriptors over SOCK_DGRAM and
+ SOCK_STREAM
+ * recvmsg() without a control buffer reports MSG_CTRUNC
+ * MSG_PEEK does not install or consume descriptors
+ * an invalid fd returns EBADF
+ * passing an AF_UNIX socket descriptor returns
+ EOPNOTSUPP and does not create a reference cycle
+
+endif
diff --git a/components/dfs/utest/v2/af_unix/SConscript b/components/dfs/utest/v2/af_unix/SConscript
new file mode 100644
index 0000000000..19cbb9e8e3
--- /dev/null
+++ b/components/dfs/utest/v2/af_unix/SConscript
@@ -0,0 +1,27 @@
+Import('rtconfig')
+from building import *
+
+cwd = GetCurrentDir()
+src = []
+CPPPATH = [cwd]
+
+if GetDepend('RT_UTEST_TC_USING_DFS_V2_AF_UNIX'):
+ cases = []
+ if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_NODE'):
+ cases += ['tc_af_unix_node.c']
+ if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_IPC'):
+ cases += ['tc_af_unix_ipc.c']
+ if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_POLL'):
+ cases += ['tc_af_unix_poll.c']
+ if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_NAMESPACE'):
+ cases += ['tc_af_unix_namespace.c']
+ if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_RIGHTS'):
+ cases += ['tc_af_unix_rights.c']
+ if cases:
+ src += ['tc_af_unix_common.c'] + cases
+
+group = DefineGroup('utestcases', src,
+ depend = ['RT_USING_UTESTCASES', 'RT_USING_DFS_V2', 'RT_USING_AF_UNIX'],
+ CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/components/dfs/utest/v2/af_unix/tc_af_unix.h b/components/dfs/utest/v2/af_unix/tc_af_unix.h
new file mode 100644
index 0000000000..334ec22973
--- /dev/null
+++ b/components/dfs/utest/v2/af_unix/tc_af_unix.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef TC_AF_UNIX_H__
+#define TC_AF_UNIX_H__
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <dfs.h>
+#include <dfs_file.h>
+#include <rtthread.h>
+#include <utest.h>
+
+#ifndef RT_UTEST_DFS_V2_AF_UNIX_DIR
+#define RT_UTEST_DFS_V2_AF_UNIX_DIR "/dev"
+#endif
+
+#define TC_AF_UNIX_PATH_MAX 64
+
+void tc_af_unix_path(char *buf, rt_size_t size, const char *name);
+void tc_af_unix_make_address(struct sockaddr_un *address, const char *path);
+void tc_af_unix_closesocket(int *fd);
+void tc_af_unix_close(int *fd);
+int tc_af_unix_send_rights(int socket_fd, const int *fds, size_t fd_count,
+ char payload);
+int tc_af_unix_receive_rights(int socket_fd, int flags, size_t control_length,
+ int *fds, size_t *fd_count, int *message_flags,
+ char *payload);
+
+#endif /* TC_AF_UNIX_H__ */
diff --git a/components/dfs/utest/v2/af_unix/tc_af_unix_common.c b/components/dfs/utest/v2/af_unix/tc_af_unix_common.c
new file mode 100644
index 0000000000..faa206cee4
--- /dev/null
+++ b/components/dfs/utest/v2/af_unix/tc_af_unix_common.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "tc_af_unix.h"
+
+void tc_af_unix_path(char *buf, rt_size_t size, const char *name)
+{
+ rt_snprintf(buf, size, "%s/%s", RT_UTEST_DFS_V2_AF_UNIX_DIR, name);
+}
+
+void tc_af_unix_make_address(struct sockaddr_un *address, const char *path)
+{
+ rt_memset(address, 0, sizeof(*address));
+ address->sa_family = AF_UNIX;
+ rt_strncpy(address->sun_path, path, sizeof(address->sun_path) - 1);
+}
+
+void tc_af_unix_closesocket(int *fd)
+{
+ if (*fd >= 0)
+ {
+ closesocket(*fd);
+ *fd = -1;
+ }
+}
+
+void tc_af_unix_close(int *fd)
+{
+ if (*fd >= 0)
+ {
+ close(*fd);
+ *fd = -1;
+ }
+}
+
+int tc_af_unix_send_rights(int socket_fd, const int *fds, size_t fd_count,
+ char payload)
+{
+ char control[CMSG_SPACE(2 * sizeof(int))];
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ struct msghdr message;
+
+ rt_memset(&message, 0, sizeof(message));
+ rt_memset(control, 0, sizeof(control));
+ iov.iov_base = &payload;
+ iov.iov_len = sizeof(payload);
+ message.msg_iov = &iov;
+ message.msg_iovlen = 1;
+ message.msg_control = control;
+ message.msg_controllen = CMSG_SPACE(fd_count * sizeof(int));
+ cmsg = CMSG_FIRSTHDR(&message);
+ cmsg->cmsg_len = CMSG_LEN(fd_count * sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ rt_memcpy(CMSG_DATA(cmsg), fds, fd_count * sizeof(int));
+ return sendmsg(socket_fd, &message, 0);
+}
+
+int tc_af_unix_receive_rights(int socket_fd, int flags, size_t control_length,
+ int *fds, size_t *fd_count, int *message_flags,
+ char *payload)
+{
+ char control[CMSG_SPACE(2 * sizeof(int))];
+ int result;
+ size_t count = 0;
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ struct msghdr message;
+
+ rt_memset(&message, 0, sizeof(message));
+ rt_memset(control, 0, sizeof(control));
+ iov.iov_base = payload;
+ iov.iov_len = sizeof(*payload);
+ message.msg_iov = &iov;
+ message.msg_iovlen = 1;
+ message.msg_control = control_length != 0 ? control : RT_NULL;
+ message.msg_controllen = control_length;
+
+ result = recvmsg(socket_fd, &message, flags);
+ if (result >= 0)
+ {
+ cmsg = CMSG_FIRSTHDR(&message);
+ if (cmsg != RT_NULL && cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS &&
+ cmsg->cmsg_len >= CMSG_LEN(sizeof(int)))
+ {
+ count = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(int);
+ rt_memcpy(fds, CMSG_DATA(cmsg), count * sizeof(int));
+ }
+ *fd_count = count;
+ *message_flags = message.msg_flags;
+ }
+ return result;
+}
diff --git a/components/dfs/utest/v2/af_unix/tc_af_unix_ipc.c b/components/dfs/utest/v2/af_unix/tc_af_unix_ipc.c
new file mode 100644
index 0000000000..26fde60454
--- /dev/null
+++ b/components/dfs/utest/v2/af_unix/tc_af_unix_ipc.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "tc_af_unix.h"
+
+static void tc_af_unix_dgram_ipc(void)
+{
+ char buffer[16];
+ char client_path[TC_AF_UNIX_PATH_MAX];
+ char server_path[TC_AF_UNIX_PATH_MAX];
+ const char payload[] = "datagram";
+ int client = -1;
+ int error;
+ int result;
+ int server = -1;
+ socklen_t source_length;
+ struct sockaddr_un client_address;
+ struct sockaddr_un server_address;
+ struct sockaddr_un source_address;
+
+ tc_af_unix_path(server_path, sizeof(server_path), "utafudgs");
+ tc_af_unix_path(client_path, sizeof(client_path), "utafudgc");
+ (void)unlink(server_path);
+ (void)unlink(client_path);
+ tc_af_unix_make_address(&server_address, server_path);
+ tc_af_unix_make_address(&client_address, client_path);
+
+ server = socket(AF_UNIX, SOCK_DGRAM, 0);
+ client = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(server >= 0);
+ uassert_true(client >= 0);
+ if (server < 0 || client < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(bind(server, (struct sockaddr *)&server_address,
+ sizeof(server_address)),
+ 0);
+ uassert_int_equal(bind(client, (struct sockaddr *)&client_address,
+ sizeof(client_address)),
+ 0);
+ uassert_int_equal(connect(client, (struct sockaddr *)&server_address,
+ sizeof(server_address)),
+ 0);
+ uassert_int_equal(send(client, payload, sizeof(payload), 0),
+ sizeof(payload));
+
+ source_length = sizeof(source_address);
+ result = recvfrom(server, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&source_address, &source_length);
+ uassert_int_equal(result, sizeof(payload));
+ uassert_buf_equal(buffer, payload, sizeof(payload));
+ uassert_str_equal(source_address.sun_path, client_path);
+
+ result = fcntl(server, F_SETFL, O_NONBLOCK);
+ uassert_int_equal(result, 0);
+ result = recv(server, buffer, sizeof(buffer), 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_true(error == EAGAIN || error == EWOULDBLOCK);
+
+ result = listen(server, 1);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EOPNOTSUPP);
+
+__exit:
+ tc_af_unix_closesocket(&client);
+ tc_af_unix_closesocket(&server);
+ (void)unlink(client_path);
+ (void)unlink(server_path);
+}
+
+static void tc_af_unix_stream_ipc(void)
+{
+ char accepted_path[TC_AF_UNIX_PATH_MAX];
+ char buffer[16];
+ const char request[] = "request";
+ const char response[] = "response";
+ int accepted = -1;
+ int client = -1;
+ int listener = -1;
+ struct sockaddr_un address;
+ struct sockaddr_un local_address;
+ struct sockaddr_un peer_address;
+ socklen_t local_length;
+ socklen_t peer_length;
+
+ tc_af_unix_path(accepted_path, sizeof(accepted_path), "utafustr");
+ (void)unlink(accepted_path);
+ tc_af_unix_make_address(&address, accepted_path);
+
+ listener = socket(AF_UNIX, SOCK_STREAM, 0);
+ client = socket(AF_UNIX, SOCK_STREAM, 0);
+ uassert_true(listener >= 0);
+ uassert_true(client >= 0);
+ if (listener < 0 || client < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(bind(listener, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ uassert_int_equal(listen(listener, 2), 0);
+ uassert_int_equal(connect(client, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ accepted = accept(listener, RT_NULL, RT_NULL);
+ uassert_true(accepted >= 0);
+ if (accepted < 0)
+ {
+ goto __exit;
+ }
+
+ local_length = sizeof(local_address);
+ peer_length = sizeof(peer_address);
+ uassert_int_equal(getsockname(accepted, (struct sockaddr *)&local_address,
+ &local_length),
+ 0);
+ uassert_int_equal(getpeername(client, (struct sockaddr *)&peer_address,
+ &peer_length),
+ 0);
+ uassert_str_equal(local_address.sun_path, accepted_path);
+ uassert_str_equal(peer_address.sun_path, accepted_path);
+
+ uassert_int_equal(send(client, request, sizeof(request), 0),
+ sizeof(request));
+ uassert_int_equal(recv(accepted, buffer, sizeof(buffer), 0),
+ sizeof(request));
+ uassert_buf_equal(buffer, request, sizeof(request));
+ uassert_int_equal(send(accepted, response, sizeof(response), 0),
+ sizeof(response));
+ uassert_int_equal(recv(client, buffer, sizeof(buffer), 0),
+ sizeof(response));
+ uassert_buf_equal(buffer, response, sizeof(response));
+
+ uassert_int_equal(shutdown(client, SHUT_WR), 0);
+ uassert_int_equal(recv(accepted, buffer, sizeof(buffer), 0), 0);
+
+__exit:
+ tc_af_unix_closesocket(&accepted);
+ tc_af_unix_closesocket(&client);
+ tc_af_unix_closesocket(&listener);
+ (void)unlink(accepted_path);
+}
+
+static void tc_af_unix_socketpair_msg(void)
+{
+ char buffer[8];
+ char first_part[3];
+ char second_part[3];
+ const char payload[] = "pair";
+ int sockets[2] = { -1, -1 };
+ int type_index;
+ int types[2] = { SOCK_DGRAM, SOCK_STREAM };
+ struct iovec receive_iov[2];
+ struct iovec send_iov[2];
+ struct msghdr receive_message;
+ struct msghdr send_message;
+
+ for (type_index = 0; type_index < 2; type_index++)
+ {
+ uassert_int_equal(socketpair(AF_UNIX, types[type_index], 0, sockets),
+ 0);
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+ uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
+ sizeof(payload));
+ uassert_buf_equal(buffer, payload, sizeof(payload));
+
+ rt_memset(&send_message, 0, sizeof(send_message));
+ send_iov[0].iov_base = (void *)payload;
+ send_iov[0].iov_len = 2;
+ send_iov[1].iov_base = (void *)(payload + 2);
+ send_iov[1].iov_len = sizeof(payload) - 2;
+ send_message.msg_iov = send_iov;
+ send_message.msg_iovlen = 2;
+ send_message.msg_name = RT_NULL;
+ send_message.msg_namelen = sizeof(struct sockaddr_un);
+ uassert_int_equal(sendmsg(sockets[0], &send_message, 0),
+ sizeof(payload));
+
+ rt_memset(&receive_message, 0, sizeof(receive_message));
+ receive_iov[0].iov_base = first_part;
+ receive_iov[0].iov_len = sizeof(first_part);
+ receive_iov[1].iov_base = second_part;
+ receive_iov[1].iov_len = sizeof(second_part);
+ receive_message.msg_iov = receive_iov;
+ receive_message.msg_iovlen = 2;
+ uassert_int_equal(recvmsg(sockets[1], &receive_message, 0),
+ sizeof(payload));
+ uassert_buf_equal(first_part, payload, sizeof(first_part));
+ uassert_buf_equal(second_part, payload + sizeof(first_part),
+ sizeof(payload) - sizeof(first_part));
+
+ send_message.msg_control = RT_NULL;
+ send_message.msg_controllen = CMSG_SPACE(sizeof(int));
+ uassert_int_equal(sendmsg(sockets[0], &send_message, 0), -1);
+ uassert_int_equal(rt_get_errno(), EINVAL);
+
+ tc_af_unix_closesocket(&sockets[0]);
+ tc_af_unix_closesocket(&sockets[1]);
+ }
+}
+
+static void tc_af_unix_connect_missing(void)
+{
+ char path[TC_AF_UNIX_PATH_MAX];
+ int client = -1;
+ int error;
+ int result;
+ struct sockaddr_un address;
+
+ tc_af_unix_path(path, sizeof(path), "utafumiss");
+ (void)unlink(path);
+ tc_af_unix_make_address(&address, path);
+
+ client = socket(AF_UNIX, SOCK_STREAM, 0);
+ uassert_true(client >= 0);
+ if (client < 0)
+ {
+ return;
+ }
+
+ result = connect(client, (struct sockaddr *)&address, sizeof(address));
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, ENOENT);
+
+ tc_af_unix_closesocket(&client);
+}
+
+static void tc_af_unix_pending_close(void)
+{
+ char path[TC_AF_UNIX_PATH_MAX];
+ const char payload[] = "pending";
+ int client = -1;
+ int error;
+ int listener = -1;
+ int result;
+ struct sockaddr_un address;
+
+ tc_af_unix_path(path, sizeof(path), "utafupend");
+ (void)unlink(path);
+ tc_af_unix_make_address(&address, path);
+
+ listener = socket(AF_UNIX, SOCK_STREAM, 0);
+ client = socket(AF_UNIX, SOCK_STREAM, 0);
+ uassert_true(listener >= 0);
+ uassert_true(client >= 0);
+ if (listener < 0 || client < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(bind(listener, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ uassert_int_equal(listen(listener, 1), 0);
+ uassert_int_equal(connect(client, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ tc_af_unix_closesocket(&listener);
+
+ result = send(client, payload, sizeof(payload), 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_true(error == ENOTCONN || error == EPIPE);
+
+__exit:
+ tc_af_unix_closesocket(&client);
+ tc_af_unix_closesocket(&listener);
+ (void)unlink(path);
+}
+
+static void tc_af_unix_ipc_testcase(void)
+{
+ UTEST_UNIT_RUN(tc_af_unix_dgram_ipc);
+ UTEST_UNIT_RUN(tc_af_unix_stream_ipc);
+ UTEST_UNIT_RUN(tc_af_unix_socketpair_msg);
+ UTEST_UNIT_RUN(tc_af_unix_connect_missing);
+ UTEST_UNIT_RUN(tc_af_unix_pending_close);
+}
+
+UTEST_TC_EXPORT(tc_af_unix_ipc_testcase, "components.dfs.v2.af_unix.ipc",
+ RT_NULL, RT_NULL, 15);
diff --git a/components/dfs/utest/v2/af_unix/tc_af_unix_namespace.c b/components/dfs/utest/v2/af_unix/tc_af_unix_namespace.c
new file mode 100644
index 0000000000..557d4e879c
--- /dev/null
+++ b/components/dfs/utest/v2/af_unix/tc_af_unix_namespace.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "tc_af_unix.h"
+
+#define TC_AF_UNIX_NAMESPACE_REPEAT 8
+
+static void tc_af_unix_pathname_lifetime(void)
+{
+ char path[TC_AF_UNIX_PATH_MAX];
+ int error;
+ int first = -1;
+ int result;
+ int second = -1;
+ struct sockaddr_un address;
+ struct stat file_stat;
+
+ tc_af_unix_path(path, sizeof(path), "utafupath");
+ (void)unlink(path);
+ tc_af_unix_make_address(&address, path);
+
+ first = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(first >= 0);
+ if (first < 0)
+ {
+ goto __exit;
+ }
+ uassert_int_equal(bind(first, (struct sockaddr *)&address, sizeof(address)),
+ 0);
+ uassert_int_equal(stat(path, &file_stat), 0);
+ uassert_true(S_ISSOCK(file_stat.st_mode));
+
+ tc_af_unix_closesocket(&first);
+ uassert_int_equal(stat(path, &file_stat), 0);
+ uassert_true(S_ISSOCK(file_stat.st_mode));
+
+ second = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(second >= 0);
+ if (second < 0)
+ {
+ goto __exit;
+ }
+ result = bind(second, (struct sockaddr *)&address, sizeof(address));
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EADDRINUSE);
+
+ uassert_int_equal(unlink(path), 0);
+ uassert_int_equal(bind(second, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+
+__exit:
+ tc_af_unix_closesocket(&second);
+ tc_af_unix_closesocket(&first);
+ (void)unlink(path);
+}
+
+static void tc_af_unix_lookup_after_close(void)
+{
+ char path[TC_AF_UNIX_PATH_MAX];
+ int client = -1;
+ int error;
+ int result;
+ int server = -1;
+ struct sockaddr_un address;
+
+ tc_af_unix_path(path, sizeof(path), "utafuref");
+ (void)unlink(path);
+ tc_af_unix_make_address(&address, path);
+
+ server = socket(AF_UNIX, SOCK_STREAM, 0);
+ client = socket(AF_UNIX, SOCK_STREAM, 0);
+ uassert_true(server >= 0);
+ uassert_true(client >= 0);
+ if (server < 0 || client < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(bind(server, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ uassert_int_equal(listen(server, 1), 0);
+ tc_af_unix_closesocket(&server);
+
+ result = connect(client, (struct sockaddr *)&address, sizeof(address));
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, ECONNREFUSED);
+
+ uassert_int_equal(unlink(path), 0);
+ result = connect(client, (struct sockaddr *)&address, sizeof(address));
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, ENOENT);
+
+__exit:
+ tc_af_unix_closesocket(&client);
+ tc_af_unix_closesocket(&server);
+ (void)unlink(path);
+}
+
+static void tc_af_unix_namespace_reuse(void)
+{
+ char name[16];
+ char path[TC_AF_UNIX_PATH_MAX];
+ int client = -1;
+ int error;
+ int index;
+ int result;
+ int sock = -1;
+ struct sockaddr_un address;
+
+ for (index = 0; index < TC_AF_UNIX_NAMESPACE_REPEAT; index++)
+ {
+ rt_snprintf(name, sizeof(name), "utafuns%d", index);
+ tc_af_unix_path(path, sizeof(path), name);
+ (void)unlink(path);
+ tc_af_unix_make_address(&address, path);
+
+ sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(sock >= 0);
+ if (sock < 0)
+ {
+ return;
+ }
+ uassert_int_equal(bind(sock, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ tc_af_unix_closesocket(&sock);
+ uassert_int_equal(unlink(path), 0);
+ }
+
+ tc_af_unix_path(path, sizeof(path), "utafuns0");
+ tc_af_unix_make_address(&address, path);
+ client = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(client >= 0);
+ if (client < 0)
+ {
+ return;
+ }
+ result = connect(client, (struct sockaddr *)&address, sizeof(address));
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, ENOENT);
+ tc_af_unix_closesocket(&client);
+
+ tc_af_unix_path(path, sizeof(path), "utafunew");
+ (void)unlink(path);
+ tc_af_unix_make_address(&address, path);
+ sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(sock >= 0);
+ if (sock < 0)
+ {
+ return;
+ }
+ uassert_int_equal(bind(sock, (struct sockaddr *)&address, sizeof(address)),
+ 0);
+ tc_af_unix_closesocket(&sock);
+ (void)unlink(path);
+}
+
+static void tc_af_unix_namespace_testcase(void)
+{
+ UTEST_UNIT_RUN(tc_af_unix_pathname_lifetime);
+ UTEST_UNIT_RUN(tc_af_unix_lookup_after_close);
+ UTEST_UNIT_RUN(tc_af_unix_namespace_reuse);
+}
+
+UTEST_TC_EXPORT(tc_af_unix_namespace_testcase,
+ "components.dfs.v2.af_unix.namespace", RT_NULL, RT_NULL, 15);
diff --git a/components/dfs/utest/v2/af_unix/tc_af_unix_node.c b/components/dfs/utest/v2/af_unix/tc_af_unix_node.c
new file mode 100644
index 0000000000..c55679c654
--- /dev/null
+++ b/components/dfs/utest/v2/af_unix/tc_af_unix_node.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "tc_af_unix.h"
+
+static void tc_af_unix_mknod_socket(void)
+{
+ char path[TC_AF_UNIX_PATH_MAX];
+ int result;
+ struct stat file_stat;
+
+ tc_af_unix_path(path, sizeof(path), "utafumkn");
+ (void)unlink(path);
+
+ result = dfs_file_mknod(path, FT_SOCKET, S_IRWXU | S_IRWXG | S_IRWXO);
+ uassert_int_equal(result, 0);
+ if (result != 0)
+ {
+ return;
+ }
+
+ uassert_int_equal(stat(path, &file_stat), 0);
+ uassert_true(S_ISSOCK(file_stat.st_mode));
+
+ result = dfs_file_mknod(path, FT_SOCKET, S_IRWXU | S_IRWXG | S_IRWXO);
+ uassert_int_equal(result, -EEXIST);
+
+ uassert_int_equal(unlink(path), 0);
+ uassert_true(stat(path, &file_stat) < 0);
+}
+
+static void tc_af_unix_file_refs(void)
+{
+ char data[4];
+ int installed = -1;
+ int invalid_fd = -1;
+ int pipe_fds[2] = { -1, -1 };
+ struct dfs_file *files[1];
+
+ files[0] = RT_NULL;
+ uassert_int_equal(dfs_file_get_refs(&invalid_fd, 1, files), -EBADF);
+ uassert_true(files[0] == RT_NULL);
+
+ uassert_int_equal(pipe(pipe_fds), 0);
+ if (pipe_fds[0] < 0)
+ {
+ return;
+ }
+
+ uassert_int_equal(write(pipe_fds[1], "abcd", 4), 4);
+ uassert_int_equal(dfs_file_get_refs(&pipe_fds[0], 1, files), 0);
+ uassert_true(files[0] != RT_NULL);
+
+ dfs_file_put_ref(files[0]);
+ uassert_int_equal(read(pipe_fds[0], data, 2), 2);
+ uassert_buf_equal(data, "ab", 2);
+
+ uassert_int_equal(dfs_file_get_refs(&pipe_fds[0], 1, files), 0);
+ uassert_int_equal(dfs_file_install_refs(files, 1, &installed), 0);
+ uassert_true(installed >= 0);
+ uassert_int_equal(read(installed, data, 2), 2);
+ uassert_buf_equal(data, "cd", 2);
+
+ tc_af_unix_close(&installed);
+ tc_af_unix_close(&pipe_fds[0]);
+ tc_af_unix_close(&pipe_fds[1]);
+}
+
+static void tc_af_unix_node_testcase(void)
+{
+ UTEST_UNIT_RUN(tc_af_unix_mknod_socket);
+ UTEST_UNIT_RUN(tc_af_unix_file_refs);
+}
+
+UTEST_TC_EXPORT(tc_af_unix_node_testcase, "components.dfs.v2.af_unix.node",
+ RT_NULL, RT_NULL, 10);
diff --git a/components/dfs/utest/v2/af_unix/tc_af_unix_poll.c b/components/dfs/utest/v2/af_unix/tc_af_unix_poll.c
new file mode 100644
index 0000000000..95d29f078d
--- /dev/null
+++ b/components/dfs/utest/v2/af_unix/tc_af_unix_poll.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "tc_af_unix.h"
+
+#define TC_AF_UNIX_POLL_STACK 4096
+#define TC_AF_UNIX_POLL_PRIO 20
+
+struct tc_af_unix_poll_ctx
+{
+ int fd;
+ int result;
+ short revents;
+ struct rt_event done;
+};
+
+static void tc_af_unix_poll_entry(void *parameter)
+{
+ struct pollfd poll_fd;
+ struct tc_af_unix_poll_ctx *ctx;
+
+ ctx = (struct tc_af_unix_poll_ctx *)parameter;
+ poll_fd.fd = ctx->fd;
+ poll_fd.events = POLLIN;
+ poll_fd.revents = 0;
+ ctx->result = poll(&poll_fd, 1, 3000);
+ ctx->revents = poll_fd.revents;
+ rt_event_send(&ctx->done, 0x1);
+}
+
+static void tc_af_unix_stream_readiness(void)
+{
+ char buffer[8];
+ char *fill;
+ const char payload[] = "poll";
+ int result;
+ int sockets[2] = { -1, -1 };
+ rt_size_t sent = 0;
+ struct pollfd poll_fd;
+
+ result = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ uassert_int_equal(result, 0);
+ if (result < 0)
+ {
+ return;
+ }
+
+ poll_fd.fd = sockets[1];
+ poll_fd.events = POLLIN | POLLOUT;
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLOUT) != 0);
+ uassert_true((poll_fd.revents & POLLIN) == 0);
+
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLIN) != 0);
+ uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
+ sizeof(payload));
+
+ fill = (char *)rt_malloc(AF_UNIX_STREAM_BUFFER_SIZE);
+ uassert_true(fill != RT_NULL);
+ if (fill != RT_NULL)
+ {
+ rt_memset(fill, 'x', AF_UNIX_STREAM_BUFFER_SIZE);
+ uassert_int_equal(fcntl(sockets[0], F_SETFL, O_NONBLOCK), 0);
+ while (sent < AF_UNIX_STREAM_BUFFER_SIZE)
+ {
+ result = send(sockets[0], fill + sent,
+ AF_UNIX_STREAM_BUFFER_SIZE - sent, 0);
+ if (result <= 0)
+ {
+ break;
+ }
+ sent += (rt_size_t)result;
+ }
+ uassert_int_equal(sent, AF_UNIX_STREAM_BUFFER_SIZE);
+
+ poll_fd.fd = sockets[0];
+ poll_fd.events = POLLOUT;
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 0);
+
+ uassert_int_equal(recv(sockets[1], fill, 64, 0), 64);
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLOUT) != 0);
+ rt_free(fill);
+ }
+
+ uassert_int_equal(shutdown(sockets[0], SHUT_WR), 0);
+ poll_fd.fd = sockets[1];
+ poll_fd.events = POLLIN;
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLIN) != 0);
+
+ tc_af_unix_closesocket(&sockets[0]);
+ tc_af_unix_closesocket(&sockets[1]);
+}
+
+static void tc_af_unix_dgram_readiness(void)
+{
+ char buffer[2];
+ const char payload[] = "x";
+ int error;
+ int index;
+ int result;
+ int sockets[2] = { -1, -1 };
+ struct pollfd poll_fd;
+
+ result = socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, sockets);
+ uassert_int_equal(result, 0);
+ if (result < 0)
+ {
+ return;
+ }
+
+ poll_fd.fd = sockets[1];
+ poll_fd.events = POLLIN;
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 0);
+
+ for (index = 0; index < AF_UNIX_DGRAM_QUEUE_LEN; index++)
+ {
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+ }
+ result = send(sockets[0], payload, sizeof(payload), 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EAGAIN);
+
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLIN) != 0);
+ uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
+ sizeof(payload));
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+
+ result = send(sockets[0], payload, AF_UNIX_DGRAM_MAX_SIZE + 1, 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EMSGSIZE);
+
+ tc_af_unix_closesocket(&sockets[0]);
+ tc_af_unix_closesocket(&sockets[1]);
+}
+
+static void tc_af_unix_poll_peer_close(void)
+{
+ int sockets[2] = { -1, -1 };
+ rt_err_t wait;
+ rt_thread_t thread;
+ rt_uint32_t recved;
+ struct tc_af_unix_poll_ctx ctx;
+
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), 0);
+ if (sockets[0] < 0)
+ {
+ return;
+ }
+
+ rt_memset(&ctx, 0, sizeof(ctx));
+ ctx.fd = sockets[0];
+ ctx.result = -2;
+ uassert_int_equal(rt_event_init(&ctx.done, "utafup", RT_IPC_FLAG_PRIO),
+ RT_EOK);
+
+ thread = rt_thread_create("utafup", tc_af_unix_poll_entry, &ctx,
+ TC_AF_UNIX_POLL_STACK, TC_AF_UNIX_POLL_PRIO, 10);
+ uassert_true(thread != RT_NULL);
+ if (thread == RT_NULL)
+ {
+ rt_event_detach(&ctx.done);
+ tc_af_unix_closesocket(&sockets[0]);
+ tc_af_unix_closesocket(&sockets[1]);
+ return;
+ }
+
+ rt_thread_startup(thread);
+ rt_thread_mdelay(50);
+ tc_af_unix_closesocket(&sockets[1]);
+
+ wait = rt_event_recv(&ctx.done, 0x1,
+ RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
+ rt_tick_from_millisecond(3000), &recved);
+ uassert_int_equal(wait, RT_EOK);
+ uassert_int_equal(ctx.result, 1);
+ uassert_true((ctx.revents & (POLLIN | POLLHUP)) != 0);
+
+ rt_event_detach(&ctx.done);
+ tc_af_unix_closesocket(&sockets[0]);
+ tc_af_unix_closesocket(&sockets[1]);
+}
+
+static void tc_af_unix_poll_testcase(void)
+{
+ UTEST_UNIT_RUN(tc_af_unix_stream_readiness);
+ UTEST_UNIT_RUN(tc_af_unix_dgram_readiness);
+ UTEST_UNIT_RUN(tc_af_unix_poll_peer_close);
+}
+
+UTEST_TC_EXPORT(tc_af_unix_poll_testcase, "components.dfs.v2.af_unix.poll",
+ RT_NULL, RT_NULL, 20);
diff --git a/components/dfs/utest/v2/af_unix/tc_af_unix_rights.c b/components/dfs/utest/v2/af_unix/tc_af_unix_rights.c
new file mode 100644
index 0000000000..fab056e735
--- /dev/null
+++ b/components/dfs/utest/v2/af_unix/tc_af_unix_rights.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "tc_af_unix.h"
+
+static void tc_af_unix_rights_transfer(void)
+{
+ char data;
+ char payload;
+ int message_flags;
+ int pipe_fds[2][2];
+ int received_fds[2];
+ int send_fds[2];
+ int sockets[2];
+ int type_index;
+ int types[2] = { SOCK_DGRAM, SOCK_STREAM };
+ size_t fd_count;
+
+ for (type_index = 0; type_index < 2; type_index++)
+ {
+ received_fds[0] = -1;
+ received_fds[1] = -1;
+ pipe_fds[0][0] = -1;
+ pipe_fds[0][1] = -1;
+ pipe_fds[1][0] = -1;
+ pipe_fds[1][1] = -1;
+ sockets[0] = -1;
+ sockets[1] = -1;
+
+ uassert_int_equal(socketpair(AF_UNIX, types[type_index], 0, sockets),
+ 0);
+ uassert_int_equal(pipe(pipe_fds[0]), 0);
+ uassert_int_equal(pipe(pipe_fds[1]), 0);
+ if (sockets[0] < 0 || pipe_fds[0][0] < 0 || pipe_fds[1][0] < 0)
+ {
+ goto __iteration_exit;
+ }
+
+ uassert_int_equal(write(pipe_fds[0][1], "a", 1), 1);
+ uassert_int_equal(write(pipe_fds[1][1], "b", 1), 1);
+ send_fds[0] = pipe_fds[0][0];
+ send_fds[1] = pipe_fds[1][0];
+ uassert_int_equal(tc_af_unix_send_rights(sockets[0], send_fds, 2, 'r'),
+ 1);
+ tc_af_unix_close(&pipe_fds[0][0]);
+ tc_af_unix_close(&pipe_fds[1][0]);
+
+ fd_count = 0;
+ message_flags = 0;
+ uassert_int_equal(tc_af_unix_receive_rights(
+ sockets[1], 0, CMSG_SPACE(2 * sizeof(int)),
+ received_fds, &fd_count, &message_flags,
+ &payload),
+ 1);
+ uassert_int_equal(payload, 'r');
+ uassert_int_equal(fd_count, 2);
+ uassert_int_equal(message_flags & MSG_CTRUNC, 0);
+ uassert_int_equal(read(received_fds[0], &data, 1), 1);
+ uassert_int_equal(data, 'a');
+ uassert_int_equal(read(received_fds[1], &data, 1), 1);
+ uassert_int_equal(data, 'b');
+
+ __iteration_exit:
+ tc_af_unix_close(&received_fds[0]);
+ tc_af_unix_close(&received_fds[1]);
+ tc_af_unix_close(&pipe_fds[0][0]);
+ tc_af_unix_close(&pipe_fds[0][1]);
+ tc_af_unix_close(&pipe_fds[1][0]);
+ tc_af_unix_close(&pipe_fds[1][1]);
+ tc_af_unix_closesocket(&sockets[0]);
+ tc_af_unix_closesocket(&sockets[1]);
+ }
+}
+
+static void tc_af_unix_rights_truncation(void)
+{
+ char payload;
+ int message_flags = 0;
+ int pipe_fds[2][2] = { { -1, -1 }, { -1, -1 } };
+ int received_fds[2] = { -1, -1 };
+ int send_fds[2];
+ int sockets[2] = { -1, -1 };
+ size_t fd_count = 0;
+
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets), 0);
+ uassert_int_equal(pipe(pipe_fds[0]), 0);
+ uassert_int_equal(pipe(pipe_fds[1]), 0);
+ if (sockets[0] < 0 || pipe_fds[0][0] < 0 || pipe_fds[1][0] < 0)
+ {
+ goto __exit;
+ }
+
+ send_fds[0] = pipe_fds[0][0];
+ send_fds[1] = pipe_fds[1][0];
+ uassert_int_equal(tc_af_unix_send_rights(sockets[0], send_fds, 2, 't'), 1);
+ uassert_int_equal(tc_af_unix_receive_rights(sockets[1], 0, 0, received_fds,
+ &fd_count, &message_flags,
+ &payload),
+ 1);
+ uassert_int_equal(fd_count, 0);
+ uassert_true((message_flags & MSG_CTRUNC) != 0);
+
+__exit:
+ tc_af_unix_close(&received_fds[0]);
+ tc_af_unix_close(&pipe_fds[0][0]);
+ tc_af_unix_close(&pipe_fds[0][1]);
+ tc_af_unix_close(&pipe_fds[1][0]);
+ tc_af_unix_close(&pipe_fds[1][1]);
+ tc_af_unix_closesocket(&sockets[0]);
+ tc_af_unix_closesocket(&sockets[1]);
+}
+
+static void tc_af_unix_rights_peek_and_invalid(void)
+{
+ char payload;
+ int error;
+ int invalid_fd = -1;
+ int message_flags = 0;
+ int pipe_fds[2] = { -1, -1 };
+ int received_fds[2] = { -1, -1 };
+ int result;
+ int sockets[2] = { -1, -1 };
+ size_t fd_count = 0;
+
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
+ sockets),
+ 0);
+ uassert_int_equal(pipe(pipe_fds), 0);
+ if (sockets[0] < 0 || pipe_fds[0] < 0)
+ {
+ goto __exit;
+ }
+
+ result = tc_af_unix_send_rights(sockets[0], &invalid_fd, 1, 'x');
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EBADF);
+
+ uassert_int_equal(tc_af_unix_send_rights(sockets[0], pipe_fds, 1, 'p'), 1);
+ fd_count = 0;
+ uassert_int_equal(tc_af_unix_receive_rights(
+ sockets[1], MSG_PEEK, CMSG_SPACE(sizeof(int)),
+ received_fds, &fd_count, &message_flags, &payload),
+ 1);
+ uassert_int_equal(payload, 'p');
+ uassert_int_equal(fd_count, 0);
+
+ fd_count = 0;
+ message_flags = 0;
+ uassert_int_equal(tc_af_unix_receive_rights(
+ sockets[1], 0, CMSG_SPACE(sizeof(int)),
+ received_fds, &fd_count, &message_flags, &payload),
+ 1);
+ uassert_int_equal(fd_count, 1);
+
+__exit:
+ tc_af_unix_close(&received_fds[0]);
+ tc_af_unix_close(&pipe_fds[0]);
+ tc_af_unix_close(&pipe_fds[1]);
+ tc_af_unix_closesocket(&sockets[0]);
+ tc_af_unix_closesocket(&sockets[1]);
+}
+
+static void tc_af_unix_rights_reject_socket(void)
+{
+ char payload;
+ int carrier[2] = { -1, -1 };
+ int error;
+ int message_flags = 0;
+ int passed[2] = { -1, -1 };
+ int received_fds[2] = { -1, -1 };
+ int result;
+ size_t fd_count = 0;
+
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM, 0, carrier), 0);
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_DGRAM, 0, passed), 0);
+ if (carrier[0] < 0 || passed[0] < 0)
+ {
+ goto __exit;
+ }
+
+ result = tc_af_unix_send_rights(carrier[0], &passed[1], 1, 's');
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EOPNOTSUPP);
+
+ result = tc_af_unix_receive_rights(carrier[1], MSG_DONTWAIT,
+ CMSG_SPACE(sizeof(int)), received_fds,
+ &fd_count, &message_flags, &payload);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_true(error == EAGAIN || error == EWOULDBLOCK);
+ uassert_int_equal(fd_count, 0);
+
+ uassert_int_equal(send(passed[0], "q", 1, 0), 1);
+ uassert_int_equal(recv(passed[1], &payload, 1, 0), 1);
+ uassert_int_equal(payload, 'q');
+
+__exit:
+ tc_af_unix_close(&received_fds[0]);
+ tc_af_unix_closesocket(&passed[0]);
+ tc_af_unix_closesocket(&passed[1]);
+ tc_af_unix_closesocket(&carrier[0]);
+ tc_af_unix_closesocket(&carrier[1]);
+}
+
+static void tc_af_unix_rights_testcase(void)
+{
+ UTEST_UNIT_RUN(tc_af_unix_rights_transfer);
+ UTEST_UNIT_RUN(tc_af_unix_rights_truncation);
+ UTEST_UNIT_RUN(tc_af_unix_rights_peek_and_invalid);
+ UTEST_UNIT_RUN(tc_af_unix_rights_reject_socket);
+}
+
+UTEST_TC_EXPORT(tc_af_unix_rights_testcase, "components.dfs.v2.af_unix.rights",
+ RT_NULL, RT_NULL, 15);
diff --git a/components/lwp/lwp_sys_socket.h b/components/lwp/lwp_sys_socket.h
index 7f1fc457da..6a690e3cc9 100644
--- a/components/lwp/lwp_sys_socket.h
+++ b/components/lwp/lwp_sys_socket.h
@@ -108,6 +108,33 @@ struct musl_sockaddr
char sa_data[14];
};
+struct musl_msghdr
+{
+ void *msg_name;
+ socklen_t msg_namelen;
+ struct iovec *msg_iov;
+#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
+ __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ int padding1;
+#endif
+ int msg_iovlen;
+#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
+ __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ int padding1;
+#endif
+ void *msg_control;
+#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
+ __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ int padding2;
+#endif
+ socklen_t msg_controllen;
+#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
+ __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ int padding2;
+#endif
+ int msg_flags;
+};
+
struct musl_ifmap {
unsigned long int mem_start;
unsigned long int mem_end;
diff --git a/components/lwp/lwp_syscall.c b/components/lwp/lwp_syscall.c
index f3538c2cb4..ea096f2a90 100644
--- a/components/lwp/lwp_syscall.c
+++ b/components/lwp/lwp_syscall.c
@@ -299,7 +299,7 @@ static void convert_sockopt(int *level, int *optname)
}
#endif /* RT_USING_SAL */
-#if defined(RT_USING_LWIP) || defined(SAL_USING_UNET)
+#if defined(RT_USING_LWIP) || defined(SAL_USING_UNET) || defined(RT_USING_SAL)
static void sockaddr_tolwip(const struct musl_sockaddr *std, struct sockaddr *lwip)
{
if (std && lwip)
@@ -5065,6 +5065,74 @@ rt_ssize_t sys_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, r
#ifdef RT_USING_SAL
/* network interfaces */
+union lwp_sockaddr_buffer
+{
+ struct sockaddr address;
+ struct sockaddr_un unix_address;
+};
+
+static int lwp_sockaddr_from_user(union lwp_sockaddr_buffer *kernel_address,
+ const struct musl_sockaddr *user_address,
+ socklen_t length)
+{
+ uint16_t family;
+ struct musl_sockaddr musl_address;
+
+ if (user_address == RT_NULL || length < sizeof(family) ||
+ length > sizeof(*kernel_address))
+ {
+ return -EINVAL;
+ }
+ if (!lwp_user_accessable((void *)user_address, length))
+ {
+ return -EFAULT;
+ }
+
+ rt_memset(kernel_address, 0, sizeof(*kernel_address));
+ lwp_get_from_user(&family, (void *)user_address, sizeof(family));
+ if (family == AF_UNIX || family == AF_NETLINK)
+ {
+ lwp_get_from_user(kernel_address, (void *)user_address, length);
+ }
+ else
+ {
+ if (length > sizeof(musl_address))
+ {
+ return -EINVAL;
+ }
+ rt_memset(&musl_address, 0, sizeof(musl_address));
+ lwp_get_from_user(&musl_address, (void *)user_address, length);
+ sockaddr_tolwip(&musl_address, &kernel_address->address);
+ }
+ return 0;
+}
+
+static socklen_t lwp_sockaddr_to_user(
+ struct musl_sockaddr *user_address, socklen_t user_length,
+ const union lwp_sockaddr_buffer *kernel_address,
+ socklen_t kernel_length)
+{
+ socklen_t actual_length;
+ socklen_t copy_length;
+ struct musl_sockaddr musl_address;
+
+ if (kernel_address->unix_address.sa_family == AF_UNIX)
+ {
+ actual_length = kernel_length;
+ copy_length = user_length < actual_length ? user_length : actual_length;
+ lwp_put_to_user(user_address, (void *)kernel_address, copy_length);
+ }
+ else
+ {
+ rt_memset(&musl_address, 0, sizeof(musl_address));
+ sockaddr_tomusl(&kernel_address->address, &musl_address);
+ actual_length = sizeof(musl_address);
+ copy_length = user_length < actual_length ? user_length : actual_length;
+ lwp_put_to_user(user_address, &musl_address, copy_length);
+ }
+ return actual_length;
+}
+
/**
* @brief Accepts a connection on a socket.
*
@@ -5102,8 +5170,7 @@ rt_ssize_t sys_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, r
sysret_t sys_accept(int socket, struct musl_sockaddr *addr, socklen_t *addrlen)
{
int ret = -1;
- struct sockaddr ksa;
- struct musl_sockaddr kmusladdr;
+ union lwp_sockaddr_buffer kernel_address;
socklen_t uaddrlen;
socklen_t kaddrlen;
@@ -5125,19 +5192,16 @@ sysret_t sys_accept(int socket, struct musl_sockaddr *addr, socklen_t *addrlen)
}
}
- kaddrlen = sizeof(struct sockaddr);
- ret = accept(socket, &ksa, &kaddrlen);
+ rt_memset(&kernel_address, 0, sizeof(kernel_address));
+ kaddrlen = sizeof(kernel_address);
+ ret = accept(socket, &kernel_address.address, &kaddrlen);
if (ret >= 0)
{
if (addr)
{
- sockaddr_tomusl(&ksa, &kmusladdr);
- if (uaddrlen > sizeof(struct musl_sockaddr))
- {
- uaddrlen = sizeof(struct musl_sockaddr);
- }
- lwp_put_to_user(addr, &kmusladdr, uaddrlen);
- lwp_put_to_user(addrlen, &uaddrlen, sizeof(socklen_t));
+ kaddrlen = lwp_sockaddr_to_user(addr, uaddrlen,
+ &kernel_address, kaddrlen);
+ lwp_put_to_user(addrlen, &kaddrlen, sizeof(socklen_t));
}
}
return ret;
@@ -5171,34 +5235,15 @@ sysret_t sys_accept(int socket, struct musl_sockaddr *addr, socklen_t *addrlen)
*/
sysret_t sys_bind(int socket, const struct musl_sockaddr *name, socklen_t namelen)
{
- rt_err_t ret = 0;
- struct sockaddr sa;
- struct sockaddr_un un_addr;
- struct musl_sockaddr kname;
- rt_uint16_t family = 0;
-
- if (!lwp_user_accessable((void *)name, namelen))
- {
- return -EFAULT;
- }
+ int ret;
+ union lwp_sockaddr_buffer kernel_address;
- lwp_get_from_user(&family, (void *)name, 2);
- if (family == AF_UNIX)
- {
- lwp_get_from_user(&un_addr, (void *)name, sizeof(struct sockaddr_un));
- ret = bind(socket, (struct sockaddr *)&un_addr, namelen);
- }
- else if (family == AF_NETLINK)
- {
- lwp_get_from_user(&sa, (void *)name, namelen);
- ret = bind(socket, &sa, namelen);
- }
- else
+ ret = lwp_sockaddr_from_user(&kernel_address, name, namelen);
+ if (ret < 0)
{
- lwp_get_from_user(&kname, (void *)name, namelen);
- sockaddr_tolwip(&kname, &sa);
- ret = bind(socket, &sa, namelen);
+ return ret;
}
+ ret = bind(socket, &kernel_address.address, namelen);
return (ret < 0 ? GET_ERRNO() : ret);
}
@@ -5263,8 +5308,7 @@ sysret_t sys_shutdown(int socket, int how)
sysret_t sys_getpeername(int socket, struct musl_sockaddr *name, socklen_t *namelen)
{
int ret = -1;
- struct sockaddr sa;
- struct musl_sockaddr kname;
+ union lwp_sockaddr_buffer kernel_address;
socklen_t unamelen;
socklen_t knamelen;
@@ -5283,18 +5327,15 @@ sysret_t sys_getpeername(int socket, struct musl_sockaddr *name, socklen_t *name
return -EFAULT;
}
- knamelen = sizeof(struct sockaddr);
- ret = getpeername(socket, &sa, &knamelen);
+ rt_memset(&kernel_address, 0, sizeof(kernel_address));
+ knamelen = sizeof(kernel_address);
+ ret = getpeername(socket, &kernel_address.address, &knamelen);
if (ret == 0)
{
- sockaddr_tomusl(&sa, &kname);
- if (unamelen > sizeof(struct musl_sockaddr))
- {
- unamelen = sizeof(struct musl_sockaddr);
- }
- lwp_put_to_user(name, &kname, unamelen);
- lwp_put_to_user(namelen, &unamelen, sizeof(socklen_t));
+ knamelen = lwp_sockaddr_to_user(name, unamelen,
+ &kernel_address, knamelen);
+ lwp_put_to_user(namelen, &knamelen, sizeof(socklen_t));
}
else
{
@@ -5331,8 +5372,7 @@ sysret_t sys_getpeername(int socket, struct musl_sockaddr *name, socklen_t *name
sysret_t sys_getsockname(int socket, struct musl_sockaddr *name, socklen_t *namelen)
{
int ret = -1;
- struct sockaddr sa;
- struct musl_sockaddr kname;
+ union lwp_sockaddr_buffer kernel_address;
socklen_t unamelen;
socklen_t knamelen;
@@ -5351,17 +5391,14 @@ sysret_t sys_getsockname(int socket, struct musl_sockaddr *name, socklen_t *name
return -EFAULT;
}
- knamelen = sizeof(struct sockaddr);
- ret = getsockname(socket, &sa, &knamelen);
+ rt_memset(&kernel_address, 0, sizeof(kernel_address));
+ knamelen = sizeof(kernel_address);
+ ret = getsockname(socket, &kernel_address.address, &knamelen);
if (ret == 0)
{
- sockaddr_tomusl(&sa, &kname);
- if (unamelen > sizeof(struct musl_sockaddr))
- {
- unamelen = sizeof(struct musl_sockaddr);
- }
- lwp_put_to_user(name, &kname, unamelen);
- lwp_put_to_user(namelen, &unamelen, sizeof(socklen_t));
+ knamelen = lwp_sockaddr_to_user(name, unamelen,
+ &kernel_address, knamelen);
+ lwp_put_to_user(namelen, &knamelen, sizeof(socklen_t));
}
else
{
@@ -5517,36 +5554,16 @@ sysret_t sys_setsockopt(int socket, int level, int optname, const void *optval,
*/
sysret_t sys_connect(int socket, const struct musl_sockaddr *name, socklen_t namelen)
{
- int ret = 0;
- rt_uint16_t family = 0;
- struct sockaddr sa;
- struct musl_sockaddr kname;
- struct sockaddr_un addr_un;
-
- if (!lwp_user_accessable((void *)name, namelen))
- {
- return -EFAULT;
- }
-
- lwp_get_from_user(&family, (void *)name, 2);
- if (family == AF_UNIX)
- {
- if (!lwp_user_accessable((void *)name, sizeof(struct sockaddr_un)))
- {
- return -EFAULT;
- }
+ int ret;
+ union lwp_sockaddr_buffer kernel_address;
- lwp_get_from_user(&addr_un, (void *)name, sizeof(struct sockaddr_un));
- ret = connect(socket, (struct sockaddr *)(&addr_un), namelen);
- }
- else
+ ret = lwp_sockaddr_from_user(&kernel_address, name, namelen);
+ if (ret < 0)
{
- lwp_get_from_user(&kname, (void *)name, namelen);
- sockaddr_tolwip(&kname, &sa);
- ret = connect(socket, &sa, namelen);
+ return ret;
}
-
- return ret;
+ ret = connect(socket, &kernel_address.address, namelen);
+ return (ret < 0 ? GET_ERRNO() : ret);
}
/**
@@ -5577,6 +5594,7 @@ sysret_t sys_listen(int socket, int backlog)
#define MUSLC_MSG_OOB 0x0001
#define MUSLC_MSG_PEEK 0x0002
+#define MUSLC_MSG_CTRUNC 0x0008
#define MUSLC_MSG_DONTWAIT 0x0040
#define MUSLC_MSG_WAITALL 0x0100
#define MUSLC_MSG_MORE 0x8000
@@ -5613,21 +5631,106 @@ static int netflags_muslc_2_lwip(int flags)
return flgs;
}
+static int netflags_lwip_2_muslc(int flags)
+{
+ int flgs = 0;
+
+ if (flags & MSG_CTRUNC)
+ {
+ flgs |= MUSLC_MSG_CTRUNC;
+ }
+ return flgs;
+}
+
+static void cmsg_level_muslc_2_lwip(struct msghdr *message)
+{
+ struct cmsghdr *cmsg;
+
+ for_each_cmsghdr(cmsg, message)
+ {
+ if (!CMSG_OK(message, cmsg))
+ {
+ break;
+ }
+ if (cmsg->cmsg_level == INTF_SOL_SOCKET)
+ {
+ cmsg->cmsg_level = IMPL_SOL_SOCKET;
+ }
+ }
+}
+
+static void cmsg_level_lwip_2_muslc(struct msghdr *message)
+{
+ struct cmsghdr *cmsg;
+
+ for_each_cmsghdr(cmsg, message)
+ {
+ if (!CMSG_OK(message, cmsg))
+ {
+ break;
+ }
+ if (cmsg->cmsg_level == IMPL_SOL_SOCKET)
+ {
+ cmsg->cmsg_level = INTF_SOL_SOCKET;
+ }
+ }
+}
+
#ifdef ARCH_MM_MMU
-static int copy_msghdr_from_user(struct msghdr *kmsg, struct msghdr *umsg,
- struct iovec **out_iov, void **out_msg_control)
+static int copy_msghdr_from_user(struct msghdr *kmsg,
+ struct musl_msghdr *umsg,
+ struct iovec **out_iov, void **out_msg_control,
+ void **out_msg_name, void **out_buffer)
{
+ int index;
size_t iovs_size;
+ struct musl_msghdr user_message;
struct iovec *uiov, *kiov;
size_t iovs_buffer_size = 0;
- void *iovs_buffer;
+ char *iovs_buffer;
+ char *buffer_cursor;
- if (!lwp_user_accessable(umsg, sizeof(*umsg)))
+ if (!lwp_user_accessable(umsg, sizeof(user_message)))
{
return -EFAULT;
}
- lwp_get_from_user(kmsg, umsg, sizeof(*kmsg));
+ lwp_get_from_user(&user_message, umsg, sizeof(user_message));
+ kmsg->msg_name = user_message.msg_name;
+ kmsg->msg_namelen = user_message.msg_namelen;
+ kmsg->msg_iov = user_message.msg_iov;
+ kmsg->msg_iovlen = user_message.msg_iovlen;
+ kmsg->msg_control = user_message.msg_control;
+ kmsg->msg_controllen = user_message.msg_controllen;
+ kmsg->msg_flags = user_message.msg_flags;
+
+ if (kmsg->msg_iovlen < 0 ||
+ (size_t)kmsg->msg_iovlen > SIZE_MAX / sizeof(*kmsg->msg_iov) ||
+ kmsg->msg_namelen > sizeof(union lwp_sockaddr_buffer))
+ {
+ return -EINVAL;
+ }
+ if (kmsg->msg_name != RT_NULL &&
+ !lwp_user_accessable(kmsg->msg_name, kmsg->msg_namelen))
+ {
+ return -EFAULT;
+ }
+ if (kmsg->msg_control != RT_NULL &&
+ !lwp_user_accessable(kmsg->msg_control, kmsg->msg_controllen))
+ {
+ return -EFAULT;
+ }
+ if (kmsg->msg_control == RT_NULL)
+ {
+ if (kmsg->msg_controllen != 0)
+ {
+ return -EFAULT;
+ }
+ }
+ if (kmsg->msg_name == RT_NULL)
+ {
+ kmsg->msg_namelen = 0;
+ }
iovs_size = sizeof(*kmsg->msg_iov) * kmsg->msg_iovlen;
if (!lwp_user_accessable(kmsg->msg_iov, iovs_size))
@@ -5651,7 +5754,7 @@ static int copy_msghdr_from_user(struct msghdr *kmsg, struct msghdr *umsg,
}
kmsg->msg_iov = kiov;
- for (int i = 0; i < kmsg->msg_iovlen; ++i)
+ for (index = 0; index < kmsg->msg_iovlen; ++index)
{
/*
* We MUST check we can copy data to user after socket done in uiov
@@ -5664,6 +5767,11 @@ static int copy_msghdr_from_user(struct msghdr *kmsg, struct msghdr *umsg,
return -EPERM;
}
+ if (SIZE_MAX - iovs_buffer_size < uiov->iov_len)
+ {
+ kmem_put(kmsg->msg_iov);
+ return -EINVAL;
+ }
iovs_buffer_size += uiov->iov_len;
kiov->iov_len = uiov->iov_len;
@@ -5672,7 +5780,15 @@ static int copy_msghdr_from_user(struct msghdr *kmsg, struct msghdr *umsg,
}
/* msg_iov and msg_control */
- iovs_buffer = kmem_get(iovs_buffer_size + kmsg->msg_controllen);
+ if (SIZE_MAX - iovs_buffer_size < kmsg->msg_controllen ||
+ SIZE_MAX - iovs_buffer_size - kmsg->msg_controllen <
+ kmsg->msg_namelen)
+ {
+ kmem_put(kmsg->msg_iov);
+ return -EINVAL;
+ }
+ iovs_buffer = kmem_get(iovs_buffer_size + kmsg->msg_controllen +
+ kmsg->msg_namelen);
if (!iovs_buffer)
{
@@ -5681,18 +5797,33 @@ static int copy_msghdr_from_user(struct msghdr *kmsg, struct msghdr *umsg,
return -ENOMEM;
}
+ *out_buffer = iovs_buffer;
+ buffer_cursor = iovs_buffer;
kiov = kmsg->msg_iov;
- for (int i = 0; i < kmsg->msg_iovlen; ++i)
+ for (index = 0; index < kmsg->msg_iovlen; ++index)
{
- kiov->iov_base = iovs_buffer;
- iovs_buffer += kiov->iov_len;
+ kiov->iov_base = buffer_cursor;
+ buffer_cursor += kiov->iov_len;
++kiov;
}
*out_msg_control = kmsg->msg_control;
- /* msg_control is the end of the iovs_buffer */
- kmsg->msg_control = iovs_buffer;
+ if (kmsg->msg_control != RT_NULL)
+ {
+ kmsg->msg_control = buffer_cursor;
+ buffer_cursor += kmsg->msg_controllen;
+ }
+ *out_msg_name = kmsg->msg_name;
+ if (kmsg->msg_name != RT_NULL && kmsg->msg_namelen != 0)
+ {
+ kmsg->msg_name = buffer_cursor;
+ }
+ else
+ {
+ kmsg->msg_name = RT_NULL;
+ kmsg->msg_namelen = 0;
+ }
return 0;
}
@@ -5728,12 +5859,17 @@ static int copy_msghdr_from_user(struct msghdr *kmsg, struct msghdr *umsg,
*
* @see sys_sendmsg()
*/
-sysret_t sys_recvmsg(int socket, struct msghdr *msg, int flags)
+sysret_t sys_recvmsg(int socket, struct musl_msghdr *msg, int flags)
{
int flgs, ret = -1;
struct msghdr kmsg;
#ifdef ARCH_MM_MMU
+ int index;
+ size_t remaining;
+ socklen_t user_name_length;
+ void *buffer;
void *msg_control;
+ void *msg_name;
struct iovec *uiov, *kiov;
#endif
@@ -5745,10 +5881,12 @@ sysret_t sys_recvmsg(int socket, struct msghdr *msg, int flags)
flgs = netflags_muslc_2_lwip(flags);
#ifdef ARCH_MM_MMU
- ret = copy_msghdr_from_user(&kmsg, msg, &uiov, &msg_control);
+ ret = copy_msghdr_from_user(&kmsg, msg, &uiov, &msg_control,
+ &msg_name, &buffer);
if (!ret)
{
+ user_name_length = kmsg.msg_namelen;
ret = recvmsg(socket, &kmsg, flgs);
if (ret < 0)
@@ -5757,24 +5895,57 @@ sysret_t sys_recvmsg(int socket, struct msghdr *msg, int flags)
}
kiov = kmsg.msg_iov;
+ remaining = (size_t)ret;
- for (int i = 0; i < kmsg.msg_iovlen; ++i)
+ for (index = 0; index < kmsg.msg_iovlen && remaining != 0; ++index)
{
- lwp_put_to_user(uiov->iov_base, kiov->iov_base, kiov->iov_len);
+ size_t copy_length = kiov->iov_len;
+
+ if (copy_length > remaining)
+ {
+ copy_length = remaining;
+ }
+ lwp_put_to_user(uiov->iov_base, kiov->iov_base, copy_length);
+ remaining -= copy_length;
++kiov;
++uiov;
}
- lwp_put_to_user(msg_control, kmsg.msg_control, kmsg.msg_controllen);
+ if (msg_control != RT_NULL && kmsg.msg_controllen != 0)
+ {
+ cmsg_level_lwip_2_muslc(&kmsg);
+ lwp_put_to_user(msg_control, kmsg.msg_control,
+ kmsg.msg_controllen);
+ }
+ if (msg_name != RT_NULL && kmsg.msg_name != RT_NULL)
+ {
+ socklen_t name_length;
+
+ name_length = lwp_sockaddr_to_user(
+ (struct musl_sockaddr *)msg_name, user_name_length,
+ (const union lwp_sockaddr_buffer *)kmsg.msg_name,
+ kmsg.msg_namelen);
+ lwp_put_to_user(&msg->msg_namelen, &name_length,
+ sizeof(name_length));
+ }
+ kmsg.msg_flags = netflags_lwip_2_muslc(kmsg.msg_flags);
lwp_put_to_user(&msg->msg_flags, &kmsg.msg_flags, sizeof(kmsg.msg_flags));
+ lwp_put_to_user(&msg->msg_controllen, &kmsg.msg_controllen,
+ sizeof(kmsg.msg_controllen));
_free_res:
- kmem_put(kmsg.msg_iov->iov_base);
+ kmem_put(buffer);
kmem_put(kmsg.msg_iov);
}
#else
- rt_memcpy(&kmsg, msg, sizeof(kmsg));
+ kmsg.msg_name = msg->msg_name;
+ kmsg.msg_namelen = msg->msg_namelen;
+ kmsg.msg_iov = msg->msg_iov;
+ kmsg.msg_iovlen = msg->msg_iovlen;
+ kmsg.msg_control = msg->msg_control;
+ kmsg.msg_controllen = msg->msg_controllen;
+ kmsg.msg_flags = msg->msg_flags;
ret = recvmsg(socket, &kmsg, flgs);
@@ -5825,18 +5996,33 @@ sysret_t sys_recvfrom(int socket, void *mem, size_t len, int flags,
struct musl_sockaddr *from, socklen_t *fromlen)
{
int flgs = 0;
-#ifdef ARCH_MM_MMU
int ret = -1;
+ socklen_t user_from_length = 0;
+ socklen_t kernel_from_length = 0;
+ union lwp_sockaddr_buffer kernel_address;
+#ifdef ARCH_MM_MMU
void *kmem = RT_NULL;
#endif
flgs = netflags_muslc_2_lwip(flags);
-#ifdef ARCH_MM_MMU
- if (!len)
+ if (from != RT_NULL)
{
- return -EINVAL;
+ if (fromlen == RT_NULL ||
+ !lwp_user_accessable(fromlen, sizeof(*fromlen)))
+ {
+ return -EFAULT;
+ }
+ lwp_get_from_user(&user_from_length, fromlen,
+ sizeof(user_from_length));
+ if (user_from_length == 0 ||
+ !lwp_user_accessable(from, user_from_length))
+ {
+ return -EFAULT;
+ }
+ rt_memset(&kernel_address, 0, sizeof(kernel_address));
+ kernel_from_length = sizeof(kernel_address);
}
-
+#ifdef ARCH_MM_MMU
if (!lwp_user_accessable((void *)mem, len))
{
return -EFAULT;
@@ -5848,26 +6034,30 @@ sysret_t sys_recvfrom(int socket, void *mem, size_t len, int flags,
return -ENOMEM;
}
- if (flags == 0x2)
- {
- flags = 0x1;
- }
-
if (from)
{
- struct sockaddr sa;
-
- ret = recvfrom(socket, kmem, len, flgs, &sa, fromlen);
- sockaddr_tomusl(&sa, from);
+ ret = recvfrom(socket, kmem, len, flgs,
+ &kernel_address.address, &kernel_from_length);
}
else
{
ret = recvfrom(socket, kmem, len, flgs, NULL, NULL);
}
- if (ret > 0)
+ if (ret >= 0)
{
- lwp_put_to_user(mem, kmem, len);
+ if (ret > 0)
+ {
+ lwp_put_to_user(mem, kmem, ret);
+ }
+ if (from != RT_NULL)
+ {
+ kernel_from_length = lwp_sockaddr_to_user(
+ from, user_from_length, &kernel_address,
+ kernel_from_length);
+ lwp_put_to_user(fromlen, &kernel_from_length,
+ sizeof(kernel_from_length));
+ }
}
if (ret < 0)
@@ -5879,17 +6069,22 @@ sysret_t sys_recvfrom(int socket, void *mem, size_t len, int flags,
return ret;
#else
- int ret = -1;
if (from)
{
- struct sockaddr sa = { 0 };
-
- ret = recvfrom(socket, mem, len, flgs, &sa, fromlen);
- sockaddr_tomusl(&sa, from);
+ ret = recvfrom(socket, mem, len, flgs,
+ &kernel_address.address, &kernel_from_length);
+ if (ret >= 0)
+ {
+ kernel_from_length = lwp_sockaddr_to_user(
+ from, user_from_length, &kernel_address,
+ kernel_from_length);
+ lwp_put_to_user(fromlen, &kernel_from_length,
+ sizeof(kernel_from_length));
+ }
}
else
{
- ret = recvfrom(socket, mem, len, flags, NULL, NULL);
+ ret = recvfrom(socket, mem, len, flgs, NULL, NULL);
}
return (ret < 0 ? GET_ERRNO() : ret);
#endif
@@ -5930,7 +6125,7 @@ sysret_t sys_recv(int socket, void *mem, size_t len, int flags)
if (!lwp_user_accessable((void *)mem, len))
return -EFAULT;
- kmem = kmem_get(sizeof(*kmem));
+ kmem = kmem_get(len);
if (kmem == RT_NULL)
{
return -ENOMEM;
@@ -5939,7 +6134,10 @@ sysret_t sys_recv(int socket, void *mem, size_t len, int flags)
flgs = netflags_muslc_2_lwip(flags);
ret = recvfrom(socket, kmem, len, flgs, NULL, NULL);
- lwp_put_to_user((void *)mem, kmem, len);
+ if (ret > 0)
+ {
+ lwp_put_to_user((void *)mem, kmem, ret);
+ }
kmem_put(kmem);
return (ret < 0 ? GET_ERRNO() : ret);
@@ -5977,12 +6175,15 @@ sysret_t sys_recv(int socket, void *mem, size_t len, int flags)
*
* @see sys_send(), sys_sendto(), sys_recvmsg()
*/
-sysret_t sys_sendmsg(int socket, const struct msghdr *msg, int flags)
+sysret_t sys_sendmsg(int socket, const struct musl_msghdr *msg, int flags)
{
int flgs, ret = -1;
struct msghdr kmsg;
#ifdef ARCH_MM_MMU
+ int index;
+ void *buffer;
void *msg_control;
+ void *msg_name;
struct iovec *uiov, *kiov;
#endif
if (!msg)
@@ -5993,13 +6194,14 @@ sysret_t sys_sendmsg(int socket, const struct msghdr *msg, int flags)
flgs = netflags_muslc_2_lwip(flags);
#ifdef ARCH_MM_MMU
- ret = copy_msghdr_from_user(&kmsg, (struct msghdr *)msg, &uiov, &msg_control);
+ ret = copy_msghdr_from_user(&kmsg, (struct musl_msghdr *)msg, &uiov,
+ &msg_control, &msg_name, &buffer);
if (!ret)
{
kiov = kmsg.msg_iov;
- for (int i = 0; i < kmsg.msg_iovlen; ++i)
+ for (index = 0; index < kmsg.msg_iovlen; ++index)
{
lwp_get_from_user(kiov->iov_base, uiov->iov_base, kiov->iov_len);
@@ -6007,22 +6209,44 @@ sysret_t sys_sendmsg(int socket, const struct msghdr *msg, int flags)
++uiov;
}
- lwp_get_from_user(kmsg.msg_control, msg_control, kmsg.msg_controllen);
+ if (msg_control != RT_NULL && kmsg.msg_controllen != 0)
+ {
+ lwp_get_from_user(kmsg.msg_control, msg_control,
+ kmsg.msg_controllen);
+ cmsg_level_muslc_2_lwip(&kmsg);
+ }
+ if (msg_name != RT_NULL && kmsg.msg_namelen != 0)
+ {
+ union lwp_sockaddr_buffer kernel_address;
+
+ ret = lwp_sockaddr_from_user(&kernel_address,
+ (struct musl_sockaddr *)msg_name,
+ kmsg.msg_namelen);
+ if (ret < 0)
+ {
+ kmem_put(buffer);
+ kmem_put(kmsg.msg_iov);
+ return ret;
+ }
+ rt_memcpy(kmsg.msg_name, &kernel_address, kmsg.msg_namelen);
+ }
ret = sendmsg(socket, &kmsg, flgs);
- kmem_put(kmsg.msg_iov->iov_base);
+ kmem_put(buffer);
kmem_put(kmsg.msg_iov);
}
#else
- rt_memcpy(&kmsg, msg, sizeof(kmsg));
+ kmsg.msg_name = msg->msg_name;
+ kmsg.msg_namelen = msg->msg_namelen;
+ kmsg.msg_iov = msg->msg_iov;
+ kmsg.msg_iovlen = msg->msg_iovlen;
+ kmsg.msg_control = msg->msg_control;
+ kmsg.msg_controllen = msg->msg_controllen;
+ kmsg.msg_flags = msg->msg_flags;
ret = sendmsg(socket, &kmsg, flgs);
- if (!ret)
- {
- msg->msg_flags = kmsg.msg_flags;
- }
#endif /* ARCH_MM_MMU */
return (ret < 0 ? GET_ERRNO() : ret);
@@ -6061,18 +6285,22 @@ sysret_t sys_sendto(int socket, const void *dataptr, size_t size, int flags,
const struct musl_sockaddr *to, socklen_t tolen)
{
int flgs = 0;
-#ifdef ARCH_MM_MMU
int ret = -1;
+ union lwp_sockaddr_buffer kernel_address;
+#ifdef ARCH_MM_MMU
void *kmem = RT_NULL;
#endif
flgs = netflags_muslc_2_lwip(flags);
-#ifdef ARCH_MM_MMU
- if (!size)
+ if (to != RT_NULL)
{
- return -EINVAL;
+ ret = lwp_sockaddr_from_user(&kernel_address, to, tolen);
+ if (ret < 0)
+ {
+ return ret;
+ }
}
-
+#ifdef ARCH_MM_MMU
if (!lwp_user_accessable((void *)dataptr, size))
{
return -EFAULT;
@@ -6088,10 +6316,8 @@ sysret_t sys_sendto(int socket, const void *dataptr, size_t size, int flags,
if (to)
{
- struct sockaddr sa;
- sockaddr_tolwip(to, &sa);
-
- ret = sendto(socket, kmem, size, flgs, &sa, tolen);
+ ret = sendto(socket, kmem, size, flgs,
+ &kernel_address.address, tolen);
}
else
{
@@ -6107,13 +6333,10 @@ sysret_t sys_sendto(int socket, const void *dataptr, size_t size, int flags,
return ret;
#else
- int ret;
if (to)
{
- struct sockaddr sa;
- sockaddr_tolwip(to, &sa);
-
- ret = sendto(socket, dataptr, size, flgs, &sa, tolen);
+ ret = sendto(socket, dataptr, size, flgs,
+ &kernel_address.address, tolen);
}
else
{
diff --git a/components/net/Kconfig b/components/net/Kconfig
index 7dfd1b101b..621448375f 100644
--- a/components/net/Kconfig
+++ b/components/net/Kconfig
@@ -1,6 +1,7 @@
menu "Network"
rsource "sal/Kconfig"
+rsource "af_unix/Kconfig"
rsource "netdev/Kconfig"
rsource "lwip/Kconfig"
rsource "at/Kconfig"
diff --git a/components/net/af_unix/Kconfig b/components/net/af_unix/Kconfig
new file mode 100644
index 0000000000..b078ae4534
--- /dev/null
+++ b/components/net/af_unix/Kconfig
@@ -0,0 +1,42 @@
+menuconfig RT_USING_AF_UNIX
+ bool "AF_UNIX local sockets"
+ depends on RT_USING_SAL
+ depends on SAL_USING_POSIX
+ depends on RT_USING_DFS_V2
+ default n
+ help
+ Enable pathname-based AF_UNIX SOCK_DGRAM and SOCK_STREAM sockets.
+
+if RT_USING_AF_UNIX
+
+config AF_UNIX_DGRAM_MAX_SIZE
+ int "Maximum datagram size"
+ range 128 65535
+ default 4096
+
+config AF_UNIX_DGRAM_QUEUE_LEN
+ int "Datagram receive queue length"
+ range 1 256
+ default 16
+
+config AF_UNIX_STREAM_BUFFER_SIZE
+ int "Stream receive buffer size"
+ range 256 65535
+ default 4096
+
+config AF_UNIX_LISTEN_BACKLOG_MAX
+ int "Maximum stream listen backlog"
+ range 1 128
+ default 16
+
+config AF_UNIX_RIGHTS_MAX
+ int "Maximum file descriptors per SCM_RIGHTS message"
+ range 1 253
+ default 16
+
+config RT_AF_UNIX_USING_TESTCASES
+ bool "Build AF_UNIX test cases"
+ depends on RT_USING_UTESTCASES
+ default n
+
+endif
diff --git a/components/net/af_unix/README.md b/components/net/af_unix/README.md
new file mode 100644
index 0000000000..6301402635
--- /dev/null
+++ b/components/net/af_unix/README.md
@@ -0,0 +1,57 @@
+# AF_UNIX local sockets
+
+The AF_UNIX component provides pathname-based local IPC through the existing
+SAL and POSIX socket APIs. It supports `SOCK_DGRAM`, `SOCK_STREAM`, and
+`socketpair()` for both socket types.
+
+## Configuration
+
+Enable `RT_USING_AF_UNIX`. The component requires `RT_USING_SAL`,
+`SAL_USING_POSIX`, and `RT_USING_DFS_V2`.
+
+- `AF_UNIX_DGRAM_MAX_SIZE` bounds one datagram.
+- `AF_UNIX_DGRAM_QUEUE_LEN` bounds queued datagrams per socket.
+- `AF_UNIX_STREAM_BUFFER_SIZE` bounds each stream receive buffer.
+- `AF_UNIX_LISTEN_BACKLOG_MAX` caps the stream accept queue.
+- `AF_UNIX_RIGHTS_MAX` bounds the file descriptors in one `SCM_RIGHTS` send.
+- `RT_AF_UNIX_USING_TESTCASES` builds the component utest suite.
+
+## Pathname behavior
+
+`bind()` creates an `S_IFSOCK` node through DFSv2. The mounted filesystem must
+support special nodes through `create_vnode()`; tmpfs and devtmpfs support
+socket nodes directly. Closing a bound socket leaves its pathname in the
+filesystem. Applications should call `unlink()` before rebinding, which
+matches common Unix daemon behavior.
+
+Removing a pathname prevents new lookups. Existing stream connections and
+connected datagram endpoints continue to reference their established peers.
+
+## Descriptor passing
+
+`sendmsg()` and `recvmsg()` support one or more file descriptors in
+`SOL_SOCKET`/`SCM_RIGHTS` control messages. The queued reference remains valid
+after the sender closes its descriptor. On receive, each reference is installed
+as a new descriptor in the receiving process and retains the same open file
+description, including its shared file position. Passing an AF_UNIX socket
+descriptor is not supported and returns `EOPNOTSUPP`.
+
+For datagram sockets, the control message is atomic with its datagram. For
+stream sockets, it is associated with the first byte written by `sendmsg()` and
+is delivered when a receive consumes that byte. A receive without a control
+buffer discards associated descriptors and reports `MSG_CTRUNC`; `MSG_PEEK`
+does not install or consume descriptors. At least one payload byte is required
+when sending `SCM_RIGHTS`.
+
+Only `SCM_RIGHTS` ancillary data is supported. Credentials and other control
+message types return `EOPNOTSUPP`.
+
+## Supported operations
+
+The component implements bind, connect, listen, accept, send/receive,
+sendto/recvfrom, sendmsg/recvmsg with descriptor passing, shutdown, socket
+options, nonblocking I/O, timeouts, poll/select readiness, address queries, and
+socketpair.
+
+Linux abstract namespace addresses and credential ancillary data are not
+supported. The component requires DFSv2.
diff --git a/components/net/af_unix/SConscript b/components/net/af_unix/SConscript
new file mode 100644
index 0000000000..c85fea549f
--- /dev/null
+++ b/components/net/af_unix/SConscript
@@ -0,0 +1,12 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('src/*.c')
+CPPPATH = [cwd + '/include', cwd + '/src']
+
+if GetDepend('RT_AF_UNIX_USING_TESTCASES'):
+ src += Glob('testcases/*.c')
+
+group = DefineGroup('AF_UNIX', src, depend=['RT_USING_AF_UNIX'], CPPPATH=CPPPATH)
+
+Return('group')
diff --git a/components/net/af_unix/include/af_unix.h b/components/net/af_unix/include/af_unix.h
new file mode 100644
index 0000000000..cd14d0efe3
--- /dev/null
+++ b/components/net/af_unix/include/af_unix.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef AF_UNIX_H__
+#define AF_UNIX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int af_unix_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AF_UNIX_H__ */
diff --git a/components/net/af_unix/src/af_unix_core.c b/components/net/af_unix/src/af_unix_core.c
new file mode 100644
index 0000000000..a6880f5c63
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_core.c
@@ -0,0 +1,1149 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <af_unix.h>
+#include "af_unix_internal.h"
+
+static struct rt_mutex af_unix_core_lock;
+static struct af_unix_socket *af_unix_handles[SAL_SOCKETS_NUM];
+
+int af_unix_error(int error)
+{
+ rt_set_errno(error);
+ return -1;
+}
+
+void af_unix_lock(void)
+{
+ rt_mutex_take(&af_unix_core_lock, RT_WAITING_FOREVER);
+}
+
+void af_unix_unlock(void)
+{
+ rt_mutex_release(&af_unix_core_lock);
+}
+
+int af_unix_is_nonblocking(const struct af_unix_socket *sock, int flags)
+{
+ return ((sock->flags & O_NONBLOCK) != 0 || (flags & MSG_DONTWAIT) != 0);
+}
+
+int af_unix_wait(rt_wqueue_t *queue, int timeout)
+{
+ int result;
+
+ result = rt_wqueue_wait_interruptible(queue, 0, timeout);
+ if (result == -RT_EINTR)
+ {
+ return af_unix_error(EINTR);
+ }
+ if (result != RT_EOK)
+ {
+ return af_unix_error(EAGAIN);
+ }
+ return 0;
+}
+
+static void af_unix_message_queue_clear_locked(struct af_unix_socket *sock)
+{
+ while (!rt_list_isempty(&sock->message_queue))
+ {
+ struct af_unix_message *message;
+
+ message = rt_list_entry(sock->message_queue.next,
+ struct af_unix_message, node);
+ rt_list_remove(&message->node);
+ if (message->rights != RT_NULL)
+ {
+ af_unix_rights_defer_locked(message->rights);
+ }
+ rt_free(message);
+ }
+ sock->message_count = 0;
+}
+
+static void af_unix_socket_destroy_locked(struct af_unix_socket *sock)
+{
+ af_unix_message_queue_clear_locked(sock);
+ af_unix_rights_list_defer_locked(&sock->rights_queue);
+ if (sock->stream_buffer != RT_NULL)
+ {
+ rt_free(sock->stream_buffer);
+ }
+ rt_free(sock);
+}
+
+void af_unix_socket_ref_locked(struct af_unix_socket *sock)
+{
+ RT_ASSERT(sock != RT_NULL);
+ RT_ASSERT(sock->ref_count > 0);
+ sock->ref_count++;
+}
+
+void af_unix_socket_unref_locked(struct af_unix_socket *sock)
+{
+ RT_ASSERT(sock != RT_NULL);
+ RT_ASSERT(sock->ref_count > 0);
+
+ sock->ref_count--;
+ if (sock->ref_count == 0)
+ {
+ af_unix_socket_destroy_locked(sock);
+ }
+}
+
+struct af_unix_socket *af_unix_socket_create_locked(int type)
+{
+ struct af_unix_socket *sock;
+
+ sock = (struct af_unix_socket *)rt_calloc(1, sizeof(*sock));
+ if (sock == RT_NULL)
+ {
+ return RT_NULL;
+ }
+
+ if (type == SOCK_STREAM)
+ {
+ sock->stream_buffer = (char *)rt_malloc(AF_UNIX_STREAM_BUFFER_SIZE);
+ if (sock->stream_buffer == RT_NULL)
+ {
+ rt_free(sock);
+ return RT_NULL;
+ }
+ }
+
+ sock->handle = -1;
+ sock->type = type;
+ sock->ref_count = 1;
+ sock->receive_timeout = RT_WAITING_FOREVER;
+ sock->send_timeout = RT_WAITING_FOREVER;
+ rt_wqueue_init(&sock->wait_queue);
+ rt_list_init(&sock->message_queue);
+ rt_list_init(&sock->rights_queue);
+ rt_list_init(&sock->pending_queue);
+ rt_list_init(&sock->pending_node);
+ af_unix_address_set(&sock->local_address, &sock->local_length, RT_NULL);
+ af_unix_address_set(&sock->peer_address, &sock->peer_length, RT_NULL);
+
+ return sock;
+}
+
+int af_unix_handle_alloc_locked(struct af_unix_socket *sock)
+{
+ int handle;
+
+ for (handle = 0; handle < SAL_SOCKETS_NUM; handle++)
+ {
+ if (af_unix_handles[handle] == RT_NULL)
+ {
+ af_unix_handles[handle] = sock;
+ sock->handle = handle;
+ return handle;
+ }
+ }
+
+ return -1;
+}
+
+void af_unix_handle_remove_locked(struct af_unix_socket *sock)
+{
+ int handle;
+
+ handle = sock->handle;
+ if (handle >= 0 && handle < SAL_SOCKETS_NUM &&
+ af_unix_handles[handle] == sock)
+ {
+ af_unix_handles[handle] = RT_NULL;
+ sock->handle = -1;
+ af_unix_socket_unref_locked(sock);
+ }
+}
+
+struct af_unix_socket *af_unix_socket_get(int handle)
+{
+ struct af_unix_socket *sock = RT_NULL;
+
+ af_unix_lock();
+ if (handle >= 0 && handle < SAL_SOCKETS_NUM)
+ {
+ sock = af_unix_handles[handle];
+ if (sock != RT_NULL)
+ {
+ af_unix_socket_ref_locked(sock);
+ }
+ }
+ af_unix_unlock();
+
+ if (sock == RT_NULL)
+ {
+ af_unix_error(EBADF);
+ }
+ return sock;
+}
+
+void af_unix_socket_put(struct af_unix_socket *sock)
+{
+ int error;
+
+ error = rt_get_errno();
+ af_unix_lock();
+ af_unix_socket_unref_locked(sock);
+ af_unix_unlock();
+ af_unix_rights_drain();
+ rt_set_errno(error);
+}
+
+static void af_unix_disconnect_locked(struct af_unix_socket *sock)
+{
+ int reciprocal;
+ struct af_unix_socket *peer;
+
+ peer = sock->peer;
+ if (peer == RT_NULL)
+ {
+ return;
+ }
+
+ rt_wqueue_wakeup_all(&sock->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT | POLLHUP));
+ rt_wqueue_wakeup_all(&peer->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT | POLLHUP));
+
+ reciprocal = (peer->peer == sock);
+ sock->peer = RT_NULL;
+ if (reciprocal)
+ {
+ peer->peer = RT_NULL;
+ af_unix_socket_unref_locked(sock);
+ }
+ af_unix_socket_unref_locked(peer);
+}
+
+void af_unix_set_peer_locked(struct af_unix_socket *sock,
+ struct af_unix_socket *peer)
+{
+ if (sock->peer == peer)
+ {
+ return;
+ }
+ if (sock->peer != RT_NULL)
+ {
+ af_unix_disconnect_locked(sock);
+ }
+ if (peer != RT_NULL)
+ {
+ af_unix_socket_ref_locked(peer);
+ sock->peer = peer;
+ }
+}
+
+int af_unix_connect_peers_locked(struct af_unix_socket *first,
+ struct af_unix_socket *second)
+{
+ if (first->peer != RT_NULL || second->peer != RT_NULL)
+ {
+ return af_unix_error(EISCONN);
+ }
+
+ af_unix_socket_ref_locked(second);
+ first->peer = second;
+ af_unix_socket_ref_locked(first);
+ second->peer = first;
+ first->connected = 1;
+ second->connected = 1;
+ return 0;
+}
+
+void af_unix_socket_close_locked(struct af_unix_socket *sock)
+{
+ if (sock->closed)
+ {
+ return;
+ }
+
+ sock->closed = 1;
+ sock->read_shutdown = 1;
+ sock->write_shutdown = 1;
+ af_unix_namespace_detach_locked(sock);
+
+ while (!rt_list_isempty(&sock->pending_queue))
+ {
+ struct af_unix_socket *pending;
+
+ pending = rt_list_entry(sock->pending_queue.next,
+ struct af_unix_socket, pending_node);
+ rt_list_remove(&pending->pending_node);
+ pending->pending = 0;
+ sock->pending_count--;
+ af_unix_handle_remove_locked(pending);
+ af_unix_socket_close_locked(pending);
+ }
+
+ af_unix_message_queue_clear_locked(sock);
+ rt_wqueue_wakeup_all(&sock->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT | POLLHUP | POLLERR));
+ af_unix_disconnect_locked(sock);
+}
+
+void af_unix_wakeup_writable_locked(struct af_unix_socket *sock)
+{
+ int handle;
+ struct af_unix_socket *other;
+
+ rt_wqueue_wakeup_all(&sock->wait_queue, (void *)(rt_ubase_t)POLLOUT);
+ for (handle = 0; handle < SAL_SOCKETS_NUM; handle++)
+ {
+ other = af_unix_handles[handle];
+ if (other != RT_NULL && other->peer == sock)
+ {
+ rt_wqueue_wakeup_all(&other->wait_queue,
+ (void *)(rt_ubase_t)POLLOUT);
+ }
+ }
+}
+
+static int af_unix_socket_create(int domain, int type, int protocol)
+{
+ int handle;
+ struct af_unix_socket *sock;
+
+ if (domain != AF_UNIX || protocol != 0 ||
+ (type != SOCK_DGRAM && type != SOCK_STREAM))
+ {
+ return af_unix_error(EPROTONOSUPPORT);
+ }
+
+ af_unix_lock();
+ sock = af_unix_socket_create_locked(type);
+ if (sock == RT_NULL)
+ {
+ af_unix_unlock();
+ return af_unix_error(ENOMEM);
+ }
+
+ handle = af_unix_handle_alloc_locked(sock);
+ if (handle < 0)
+ {
+ af_unix_socket_unref_locked(sock);
+ af_unix_unlock();
+ return af_unix_error(EMFILE);
+ }
+ af_unix_unlock();
+ return handle;
+}
+
+static int af_unix_close(int handle)
+{
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+
+ af_unix_lock();
+ af_unix_handle_remove_locked(sock);
+ af_unix_socket_close_locked(sock);
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return 0;
+}
+
+static int af_unix_bind(int handle, const struct sockaddr *address,
+ socklen_t length)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ result = af_unix_namespace_bind(sock, address, length);
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_listen(int handle, int backlog)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ result = af_unix_stream_listen(sock, backlog);
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_connect(int handle, const struct sockaddr *address,
+ socklen_t length)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ if (sock->type == SOCK_DGRAM)
+ {
+ result = af_unix_dgram_connect(sock, address, length);
+ }
+ else
+ {
+ result = af_unix_stream_connect(sock, address, length);
+ }
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_accept(int handle, struct sockaddr *address,
+ socklen_t *length)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ result = af_unix_stream_accept(sock, address, length);
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_sendto_with_rights(
+ int handle, const void *data, size_t size, int flags,
+ const struct sockaddr *to, socklen_t to_length,
+ struct af_unix_rights *rights)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ if (sock->type == SOCK_DGRAM)
+ {
+ result = af_unix_dgram_send(sock, data, size, flags, to, to_length,
+ rights);
+ }
+ else
+ {
+ if (to != RT_NULL)
+ {
+ result = af_unix_error(EISCONN);
+ }
+ else
+ {
+ result = af_unix_stream_send(sock, data, size, flags, rights);
+ }
+ }
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_sendto(int handle, const void *data, size_t size, int flags,
+ const struct sockaddr *to, socklen_t to_length)
+{
+ return af_unix_sendto_with_rights(handle, data, size, flags, to,
+ to_length, RT_NULL);
+}
+
+static int af_unix_recvfrom_with_rights(
+ int handle, void *data, size_t size, int flags, struct sockaddr *from,
+ socklen_t *from_length, rt_list_t *rights)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ if (sock->type == SOCK_DGRAM)
+ {
+ result = af_unix_dgram_receive(sock, data, size, flags,
+ from, from_length, rights);
+ }
+ else
+ {
+ result = af_unix_stream_receive(sock, data, size, flags, rights);
+ if (result >= 0 && from != RT_NULL)
+ {
+ (void)af_unix_address_copy(from, from_length,
+ &sock->peer_address,
+ sock->peer_length);
+ }
+ }
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_recvfrom(int handle, void *data, size_t size, int flags,
+ struct sockaddr *from, socklen_t *from_length)
+{
+ int result;
+ rt_list_t rights;
+
+ rt_list_init(&rights);
+ result = af_unix_recvfrom_with_rights(handle, data, size, flags, from,
+ from_length, &rights);
+ af_unix_rights_list_release(&rights);
+ return result;
+}
+
+static int af_unix_sendmsg(int handle, const struct msghdr *message, int flags)
+{
+ int error;
+ int result;
+ int index;
+ size_t total = 0;
+ size_t offset = 0;
+ char *buffer = RT_NULL;
+ struct af_unix_rights *rights = RT_NULL;
+
+ if (message == RT_NULL || message->msg_iov == RT_NULL ||
+ message->msg_iovlen < 0)
+ {
+ return af_unix_error(EINVAL);
+ }
+ for (index = 0; index < message->msg_iovlen; index++)
+ {
+ if (SIZE_MAX - total < message->msg_iov[index].iov_len)
+ {
+ return af_unix_error(EMSGSIZE);
+ }
+ total += message->msg_iov[index].iov_len;
+ }
+ if (message->msg_controllen != 0 && total == 0)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (af_unix_rights_create(message, &rights) < 0)
+ {
+ return -1;
+ }
+
+ if (total != 0)
+ {
+ buffer = (char *)rt_malloc(total);
+ if (buffer == RT_NULL)
+ {
+ af_unix_rights_release(rights);
+ return af_unix_error(ENOMEM);
+ }
+ for (index = 0; index < message->msg_iovlen; index++)
+ {
+ rt_memcpy(buffer + offset, message->msg_iov[index].iov_base,
+ message->msg_iov[index].iov_len);
+ offset += message->msg_iov[index].iov_len;
+ }
+ }
+
+ result = af_unix_sendto_with_rights(
+ handle, buffer, total, flags,
+ (const struct sockaddr *)message->msg_name,
+ message->msg_namelen, rights);
+ if (result >= 0)
+ {
+ rights = RT_NULL;
+ }
+ if (buffer != RT_NULL)
+ {
+ error = rt_get_errno();
+ rt_free(buffer);
+ rt_set_errno(error);
+ }
+ af_unix_rights_release(rights);
+ return result;
+}
+
+static int af_unix_recvmsg(int handle, struct msghdr *message, int flags)
+{
+ int error;
+ int result;
+ int index;
+ size_t total = 0;
+ size_t offset = 0;
+ char *buffer = RT_NULL;
+ rt_list_t rights;
+
+ if (message == RT_NULL || message->msg_iov == RT_NULL ||
+ message->msg_iovlen < 0)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ for (index = 0; index < message->msg_iovlen; index++)
+ {
+ if (SIZE_MAX - total < message->msg_iov[index].iov_len)
+ {
+ return af_unix_error(EMSGSIZE);
+ }
+ total += message->msg_iov[index].iov_len;
+ }
+ if (total != 0)
+ {
+ buffer = (char *)rt_malloc(total);
+ if (buffer == RT_NULL)
+ {
+ return af_unix_error(ENOMEM);
+ }
+ }
+
+ rt_list_init(&rights);
+ message->msg_flags = 0;
+ result = af_unix_recvfrom_with_rights(
+ handle, buffer, total, flags,
+ (struct sockaddr *)message->msg_name, &message->msg_namelen,
+ &rights);
+ if (result >= 0)
+ {
+ size_t remaining = (size_t)result;
+
+ for (index = 0; index < message->msg_iovlen && remaining != 0; index++)
+ {
+ size_t copy_length = message->msg_iov[index].iov_len;
+
+ if (copy_length > remaining)
+ {
+ copy_length = remaining;
+ }
+ rt_memcpy(message->msg_iov[index].iov_base,
+ buffer + offset, copy_length);
+ offset += copy_length;
+ remaining -= copy_length;
+ }
+ if ((flags & MSG_PEEK) != 0)
+ {
+ message->msg_controllen = 0;
+ }
+ else
+ {
+ (void)af_unix_rights_deliver(&rights, message);
+ }
+ }
+ af_unix_rights_list_release(&rights);
+ if (buffer != RT_NULL)
+ {
+ error = rt_get_errno();
+ rt_free(buffer);
+ rt_set_errno(error);
+ }
+ return result;
+}
+
+static int af_unix_getsockname(int handle, struct sockaddr *address,
+ socklen_t *length)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ af_unix_lock();
+ result = af_unix_address_copy(address, length, &sock->local_address,
+ sock->local_length);
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_getpeername(int handle, struct sockaddr *address,
+ socklen_t *length)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ af_unix_lock();
+ if (!sock->connected)
+ {
+ result = af_unix_error(ENOTCONN);
+ }
+ else
+ {
+ result = af_unix_address_copy(address, length, &sock->peer_address,
+ sock->peer_length);
+ }
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_timeout_from_timeval(const struct timeval *time_value)
+{
+ long long milliseconds;
+
+ if (time_value->tv_sec < 0 || time_value->tv_usec < 0 ||
+ time_value->tv_usec >= 1000000)
+ {
+ return -2;
+ }
+ if (time_value->tv_sec == 0 && time_value->tv_usec == 0)
+ {
+ return RT_WAITING_FOREVER;
+ }
+
+ milliseconds = (long long)time_value->tv_sec * 1000LL;
+ milliseconds += ((long long)time_value->tv_usec + 999LL) / 1000LL;
+ if (milliseconds > INT_MAX)
+ {
+ milliseconds = INT_MAX;
+ }
+ return (int)milliseconds;
+}
+
+static void af_unix_timeval_from_timeout(int timeout,
+ struct timeval *time_value)
+{
+ if (timeout == RT_WAITING_FOREVER)
+ {
+ time_value->tv_sec = 0;
+ time_value->tv_usec = 0;
+ }
+ else
+ {
+ time_value->tv_sec = timeout / 1000;
+ time_value->tv_usec = (timeout % 1000) * 1000;
+ }
+}
+
+static int af_unix_getsockopt(int handle, int level, int option,
+ void *value, socklen_t *length)
+{
+ int result = 0;
+ int int_value = 0;
+ struct timeval time_value;
+ struct af_unix_socket *sock;
+
+ if (level != SOL_SOCKET || value == RT_NULL || length == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ af_unix_lock();
+ switch (option)
+ {
+ case SO_RCVTIMEO:
+ case SO_SNDTIMEO:
+ if (*length < sizeof(time_value))
+ {
+ result = af_unix_error(EINVAL);
+ break;
+ }
+ af_unix_timeval_from_timeout(option == SO_RCVTIMEO ? sock->receive_timeout : sock->send_timeout,
+ &time_value);
+ rt_memcpy(value, &time_value, sizeof(time_value));
+ *length = sizeof(time_value);
+ break;
+ case SO_TYPE:
+ int_value = sock->type;
+ break;
+ case SO_DOMAIN:
+ int_value = AF_UNIX;
+ break;
+ case SO_PROTOCOL:
+ int_value = 0;
+ break;
+ case SO_ACCEPTCONN:
+ int_value = sock->listening;
+ break;
+ case SO_ERROR:
+ int_value = sock->socket_error;
+ sock->socket_error = 0;
+ break;
+ case SO_SNDBUF:
+ case SO_RCVBUF:
+ int_value = sock->type == SOCK_DGRAM ? AF_UNIX_DGRAM_MAX_SIZE : AF_UNIX_STREAM_BUFFER_SIZE;
+ break;
+ default:
+ result = af_unix_error(ENOPROTOOPT);
+ break;
+ }
+
+ if (result == 0 && option != SO_RCVTIMEO && option != SO_SNDTIMEO)
+ {
+ if (*length < sizeof(int_value))
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else
+ {
+ rt_memcpy(value, &int_value, sizeof(int_value));
+ *length = sizeof(int_value);
+ }
+ }
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_setsockopt(int handle, int level, int option,
+ const void *value, socklen_t length)
+{
+ int result = 0;
+ int timeout;
+ const struct timeval *time_value;
+ struct af_unix_socket *sock;
+
+ if (level != SOL_SOCKET || value == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (option != SO_RCVTIMEO && option != SO_SNDTIMEO)
+ {
+ return af_unix_error(ENOPROTOOPT);
+ }
+ if (length < sizeof(struct timeval))
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ time_value = (const struct timeval *)value;
+ timeout = af_unix_timeout_from_timeval(time_value);
+ if (timeout == -2)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ af_unix_lock();
+ if (option == SO_RCVTIMEO)
+ {
+ sock->receive_timeout = timeout;
+ }
+ else
+ {
+ sock->send_timeout = timeout;
+ }
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_shutdown(int handle, int how)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ if (sock->type == SOCK_STREAM)
+ {
+ result = af_unix_stream_shutdown(sock, how);
+ }
+ else
+ {
+ if (how < SHUT_RD || how > SHUT_RDWR)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else
+ {
+ af_unix_lock();
+ if (how == SHUT_RD || how == SHUT_RDWR)
+ {
+ sock->read_shutdown = 1;
+ }
+ if (how == SHUT_WR || how == SHUT_RDWR)
+ {
+ sock->write_shutdown = 1;
+ }
+ rt_wqueue_wakeup_all(&sock->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT));
+ af_unix_unlock();
+ result = 0;
+ }
+ }
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_ioctl(int handle, long command, void *argument)
+{
+ int result = 0;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+
+ af_unix_lock();
+ switch (command)
+ {
+ case F_GETFL:
+ result = sock->flags;
+ break;
+ case F_SETFL:
+ sock->flags &= ~O_NONBLOCK;
+ sock->flags |= ((int)(rt_base_t)argument & O_NONBLOCK);
+ break;
+ case FIONBIO:
+ if (argument == RT_NULL)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else if (*(int *)argument != 0)
+ {
+ sock->flags |= O_NONBLOCK;
+ }
+ else
+ {
+ sock->flags &= ~O_NONBLOCK;
+ }
+ break;
+ case FIONREAD:
+ if (argument == RT_NULL)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else if (sock->type == SOCK_STREAM)
+ {
+ *(int *)argument = (int)sock->stream_length;
+ }
+ else if (rt_list_isempty(&sock->message_queue))
+ {
+ *(int *)argument = 0;
+ }
+ else
+ {
+ struct af_unix_message *message;
+
+ message = rt_list_entry(sock->message_queue.next,
+ struct af_unix_message, node);
+ *(int *)argument = (int)message->length;
+ }
+ break;
+ default:
+ result = af_unix_error(EINVAL);
+ break;
+ }
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_socketpair(int domain, int type, int protocol, int *handles)
+{
+ int result = -1;
+ struct af_unix_socket *first;
+ struct af_unix_socket *second;
+
+ if (domain != AF_UNIX || protocol != 0 || handles == RT_NULL ||
+ (type != SOCK_DGRAM && type != SOCK_STREAM))
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ first = af_unix_socket_get(handles[0]);
+ if (first == RT_NULL)
+ {
+ return -1;
+ }
+ second = af_unix_socket_get(handles[1]);
+ if (second == RT_NULL)
+ {
+ af_unix_socket_put(first);
+ return -1;
+ }
+
+ af_unix_lock();
+ if (first->type != type || second->type != type ||
+ first->closed || second->closed)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else
+ {
+ result = af_unix_connect_peers_locked(first, second);
+ if (result == 0)
+ {
+ af_unix_address_set(&first->peer_address, &first->peer_length,
+ RT_NULL);
+ af_unix_address_set(&second->peer_address, &second->peer_length,
+ RT_NULL);
+ }
+ }
+ af_unix_unlock();
+ af_unix_socket_put(second);
+ af_unix_socket_put(first);
+ return result;
+}
+
+static int af_unix_poll(struct dfs_file *file, struct rt_pollreq *request)
+{
+ int mask = 0;
+ int sal_handle;
+ struct sal_socket *sal_sock;
+ struct af_unix_socket *sock;
+
+ sal_handle = (int)(size_t)file->vnode->data;
+ sal_sock = sal_get_socket(sal_handle);
+ if (sal_sock == RT_NULL)
+ {
+ return POLLNVAL;
+ }
+ sock = af_unix_socket_get((int)(size_t)sal_sock->user_data);
+ if (sock == RT_NULL)
+ {
+ return POLLNVAL;
+ }
+
+ /* Register only on this socket. The peer queue can be freed after
+ * peer close, while poll teardown still owns the wait-queue node. */
+ rt_poll_add(&sock->wait_queue, request);
+ af_unix_lock();
+ if (sock->closed)
+ {
+ mask = POLLHUP | POLLERR;
+ }
+ else if (sock->listening)
+ {
+ if (sock->pending_count > 0)
+ {
+ mask |= POLLIN;
+ }
+ }
+ else if (sock->type == SOCK_DGRAM)
+ {
+ if (sock->message_count > 0 || sock->read_shutdown)
+ {
+ mask |= POLLIN;
+ }
+ if (!sock->write_shutdown)
+ {
+ if (sock->peer != RT_NULL)
+ {
+ if (!sock->peer->closed &&
+ sock->peer->message_count < AF_UNIX_DGRAM_QUEUE_LEN)
+ {
+ mask |= POLLOUT;
+ }
+ }
+ else
+ {
+ mask |= POLLOUT;
+ }
+ }
+ }
+ else
+ {
+ if (sock->stream_length > 0 || sock->read_shutdown ||
+ sock->peer == RT_NULL || sock->peer->write_shutdown ||
+ sock->peer->closed)
+ {
+ mask |= POLLIN;
+ }
+ if (sock->peer != RT_NULL)
+ {
+ if (!sock->write_shutdown && !sock->peer->closed &&
+ !sock->peer->read_shutdown &&
+ sock->peer->stream_length < AF_UNIX_STREAM_BUFFER_SIZE)
+ {
+ mask |= POLLOUT;
+ }
+ if (sock->peer->closed)
+ {
+ mask |= POLLHUP;
+ }
+ }
+ else
+ {
+ mask |= POLLHUP;
+ }
+ }
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return mask;
+}
+
+static const struct sal_socket_ops af_unix_socket_ops = {
+ .socket = af_unix_socket_create,
+ .closesocket = af_unix_close,
+ .bind = af_unix_bind,
+ .listen = af_unix_listen,
+ .connect = af_unix_connect,
+ .accept = af_unix_accept,
+ .sendto = af_unix_sendto,
+ .sendmsg = af_unix_sendmsg,
+ .recvmsg = af_unix_recvmsg,
+ .recvfrom = af_unix_recvfrom,
+ .getsockopt = af_unix_getsockopt,
+ .setsockopt = af_unix_setsockopt,
+ .shutdown = af_unix_shutdown,
+ .getpeername = af_unix_getpeername,
+ .getsockname = af_unix_getsockname,
+ .ioctlsocket = af_unix_ioctl,
+ .socketpair = af_unix_socketpair,
+ .poll = af_unix_poll,
+};
+
+static const struct sal_proto_family af_unix_family = {
+ .family = AF_UNIX,
+ .sec_family = AF_UNIX,
+ .skt_ops = &af_unix_socket_ops,
+ .netdb_ops = RT_NULL,
+};
+
+int af_unix_init(void)
+{
+ int result;
+
+ rt_memset(af_unix_handles, 0, sizeof(af_unix_handles));
+ rt_mutex_init(&af_unix_core_lock, "afunix", RT_IPC_FLAG_PRIO);
+ result = af_unix_rights_init();
+ if (result != RT_EOK)
+ {
+ return result;
+ }
+ result = af_unix_namespace_init();
+ if (result != RT_EOK)
+ {
+ return result;
+ }
+ return sal_proto_family_register(&af_unix_family);
+}
+INIT_ENV_EXPORT(af_unix_init);
diff --git a/components/net/af_unix/src/af_unix_dgram.c b/components/net/af_unix/src/af_unix_dgram.c
new file mode 100644
index 0000000000..d28fcf8b52
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_dgram.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <string.h>
+
+#include "af_unix_internal.h"
+
+int af_unix_dgram_connect(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length)
+{
+ int result = 0;
+ struct sockaddr_un peer_address;
+ socklen_t peer_length;
+ struct af_unix_socket *peer;
+
+ peer = af_unix_namespace_lookup(address, length, SOCK_DGRAM,
+ &peer_address, &peer_length);
+ if (peer == RT_NULL)
+ {
+ return -1;
+ }
+
+ af_unix_lock();
+ if (sock->closed)
+ {
+ result = af_unix_error(EBADF);
+ }
+ else if (sock->write_shutdown)
+ {
+ result = af_unix_error(EPIPE);
+ }
+ else
+ {
+ af_unix_set_peer_locked(sock, peer);
+ sock->peer_address = peer_address;
+ sock->peer_length = peer_length;
+ sock->connected = 1;
+ }
+ af_unix_unlock();
+ af_unix_socket_put(peer);
+ return result;
+}
+
+static struct af_unix_socket *af_unix_dgram_target_get(
+ struct af_unix_socket *sock, const struct sockaddr *to,
+ socklen_t to_length)
+{
+ struct af_unix_socket *peer = RT_NULL;
+
+ if (to != RT_NULL)
+ {
+ return af_unix_namespace_lookup(to, to_length, SOCK_DGRAM,
+ RT_NULL, RT_NULL);
+ }
+
+ af_unix_lock();
+ if (sock->connected && sock->peer != RT_NULL)
+ {
+ peer = sock->peer;
+ af_unix_socket_ref_locked(peer);
+ }
+ af_unix_unlock();
+
+ if (peer == RT_NULL)
+ {
+ af_unix_error(EDESTADDRREQ);
+ }
+ return peer;
+}
+
+int af_unix_dgram_send(struct af_unix_socket *sock, const void *data,
+ size_t size, int flags, const struct sockaddr *to,
+ socklen_t to_length, struct af_unix_rights *rights)
+{
+ int error;
+ int result;
+ int nonblocking;
+ int timeout;
+ struct af_unix_socket *peer;
+ struct af_unix_message *message;
+
+ if (size > AF_UNIX_DGRAM_MAX_SIZE)
+ {
+ return af_unix_error(EMSGSIZE);
+ }
+ if (size != 0 && data == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ peer = af_unix_dgram_target_get(sock, to, to_length);
+ if (peer == RT_NULL)
+ {
+ return -1;
+ }
+
+ message = (struct af_unix_message *)rt_malloc(sizeof(*message) + size);
+ if (message == RT_NULL)
+ {
+ af_unix_socket_put(peer);
+ return af_unix_error(ENOMEM);
+ }
+ rt_list_init(&message->node);
+ message->length = size;
+ message->rights = rights;
+ if (size != 0)
+ {
+ rt_memcpy(message->data, data, size);
+ }
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ result = af_unix_error(EBADF);
+ af_unix_unlock();
+ break;
+ }
+ if (sock->write_shutdown)
+ {
+ result = af_unix_error(EPIPE);
+ af_unix_unlock();
+ break;
+ }
+ if (peer->closed)
+ {
+ result = af_unix_error(ECONNREFUSED);
+ af_unix_unlock();
+ break;
+ }
+ if (peer->read_shutdown)
+ {
+ result = af_unix_error(EPIPE);
+ af_unix_unlock();
+ break;
+ }
+ if (peer->message_count < AF_UNIX_DGRAM_QUEUE_LEN)
+ {
+ message->source = sock->local_address;
+ message->source_length = sock->local_length;
+ rt_list_insert_before(&peer->message_queue, &message->node);
+ peer->message_count++;
+ rt_wqueue_wakeup_all(&peer->wait_queue,
+ (void *)(rt_ubase_t)POLLIN);
+ af_unix_unlock();
+ af_unix_socket_put(peer);
+ return (int)size;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, flags);
+ timeout = sock->send_timeout;
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ result = af_unix_error(EAGAIN);
+ break;
+ }
+ if (af_unix_wait(&peer->wait_queue, timeout) < 0)
+ {
+ result = -1;
+ break;
+ }
+ }
+
+ error = rt_get_errno();
+ rt_free(message);
+ af_unix_socket_put(peer);
+ rt_set_errno(error);
+ return result;
+}
+
+int af_unix_dgram_receive(struct af_unix_socket *sock, void *data,
+ size_t size, int flags, struct sockaddr *from,
+ socklen_t *from_length, rt_list_t *rights)
+{
+ int result;
+ int nonblocking;
+ int timeout;
+ size_t copy_length;
+ struct af_unix_message *message;
+
+ if (size != 0 && data == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ af_unix_unlock();
+ return af_unix_error(EBADF);
+ }
+ if (!rt_list_isempty(&sock->message_queue))
+ {
+ message = rt_list_entry(sock->message_queue.next,
+ struct af_unix_message, node);
+ copy_length = message->length;
+ if (copy_length > size)
+ {
+ copy_length = size;
+ }
+ if (copy_length != 0)
+ {
+ rt_memcpy(data, message->data, copy_length);
+ }
+ result = af_unix_address_copy(from, from_length,
+ &message->source,
+ message->source_length);
+ if (result < 0)
+ {
+ af_unix_unlock();
+ return result;
+ }
+ if ((flags & MSG_PEEK) == 0)
+ {
+ rt_list_remove(&message->node);
+ sock->message_count--;
+ if (message->rights != RT_NULL)
+ {
+ rt_list_insert_before(rights, &message->rights->node);
+ message->rights = RT_NULL;
+ }
+ af_unix_wakeup_writable_locked(sock);
+ rt_free(message);
+ }
+ af_unix_unlock();
+ return (int)copy_length;
+ }
+ if (sock->read_shutdown)
+ {
+ af_unix_unlock();
+ return 0;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, flags);
+ timeout = sock->receive_timeout;
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ return af_unix_error(EAGAIN);
+ }
+ if (af_unix_wait(&sock->wait_queue, timeout) < 0)
+ {
+ return -1;
+ }
+ }
+}
diff --git a/components/net/af_unix/src/af_unix_internal.h b/components/net/af_unix/src/af_unix_internal.h
new file mode 100644
index 0000000000..e59ff52084
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_internal.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef AF_UNIX_INTERNAL_H__
+#define AF_UNIX_INTERNAL_H__
+
+#include <rtthread.h>
+#include <dfs.h>
+#include <dfs_file.h>
+#include <poll.h>
+#include <sal_low_lvl.h>
+#include <sal_socket.h>
+
+#ifndef AF_UNIX_DGRAM_MAX_SIZE
+#define AF_UNIX_DGRAM_MAX_SIZE 4096
+#endif
+
+#ifndef AF_UNIX_DGRAM_QUEUE_LEN
+#define AF_UNIX_DGRAM_QUEUE_LEN 16
+#endif
+
+#ifndef AF_UNIX_STREAM_BUFFER_SIZE
+#define AF_UNIX_STREAM_BUFFER_SIZE 4096
+#endif
+
+#ifndef AF_UNIX_LISTEN_BACKLOG_MAX
+#define AF_UNIX_LISTEN_BACKLOG_MAX 16
+#endif
+
+#ifndef AF_UNIX_RIGHTS_MAX
+#define AF_UNIX_RIGHTS_MAX 16
+#endif
+
+#define AF_UNIX_PATH_MAX ((int)sizeof(((struct sockaddr_un *)0)->sun_path))
+
+struct af_unix_rights
+{
+ rt_list_t node;
+ rt_uint64_t offset;
+ size_t count;
+ struct dfs_file *files[1];
+};
+
+struct af_unix_message
+{
+ rt_list_t node;
+ size_t length;
+ struct sockaddr_un source;
+ socklen_t source_length;
+ struct af_unix_rights *rights;
+ char data[1];
+};
+
+struct af_unix_socket
+{
+ int handle;
+ int type;
+ int flags;
+ int ref_count;
+ int closed;
+ int bound;
+ int connected;
+ int listening;
+ int read_shutdown;
+ int write_shutdown;
+ int socket_error;
+ int receive_timeout;
+ int send_timeout;
+
+ struct sockaddr_un local_address;
+ socklen_t local_length;
+ struct sockaddr_un peer_address;
+ socklen_t peer_length;
+ struct af_unix_socket *peer;
+ void *namespace_entry;
+
+ rt_wqueue_t wait_queue;
+ rt_list_t message_queue;
+ rt_size_t message_count;
+
+ char *stream_buffer;
+ rt_size_t stream_head;
+ rt_size_t stream_length;
+ rt_uint64_t stream_read_offset;
+ rt_uint64_t stream_write_offset;
+ rt_list_t rights_queue;
+
+ rt_list_t pending_queue;
+ rt_list_t pending_node;
+ int pending;
+ int backlog;
+ int pending_count;
+};
+
+void af_unix_lock(void);
+void af_unix_unlock(void);
+int af_unix_error(int error);
+int af_unix_is_nonblocking(const struct af_unix_socket *sock, int flags);
+int af_unix_wait(rt_wqueue_t *queue, int timeout);
+
+int af_unix_rights_create(const struct msghdr *message,
+ struct af_unix_rights **rights);
+void af_unix_rights_release(struct af_unix_rights *rights);
+void af_unix_rights_list_release(rt_list_t *list);
+void af_unix_rights_defer_locked(struct af_unix_rights *rights);
+void af_unix_rights_list_defer_locked(rt_list_t *list);
+void af_unix_rights_drain(void);
+int af_unix_rights_deliver(rt_list_t *list, struct msghdr *message);
+int af_unix_rights_init(void);
+
+struct af_unix_socket *af_unix_socket_create_locked(int type);
+struct af_unix_socket *af_unix_socket_get(int handle);
+void af_unix_socket_ref_locked(struct af_unix_socket *sock);
+void af_unix_socket_unref_locked(struct af_unix_socket *sock);
+void af_unix_socket_put(struct af_unix_socket *sock);
+int af_unix_handle_alloc_locked(struct af_unix_socket *sock);
+void af_unix_handle_remove_locked(struct af_unix_socket *sock);
+void af_unix_socket_close_locked(struct af_unix_socket *sock);
+void af_unix_wakeup_writable_locked(struct af_unix_socket *sock);
+int af_unix_connect_peers_locked(struct af_unix_socket *first,
+ struct af_unix_socket *second);
+void af_unix_set_peer_locked(struct af_unix_socket *sock,
+ struct af_unix_socket *peer);
+
+int af_unix_address_parse(const struct sockaddr *address, socklen_t length,
+ char path[AF_UNIX_PATH_MAX]);
+void af_unix_address_set(struct sockaddr_un *address, socklen_t *length,
+ const char *path);
+int af_unix_address_copy(struct sockaddr *address, socklen_t *length,
+ const struct sockaddr_un *source,
+ socklen_t source_length);
+int af_unix_namespace_bind(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length);
+struct af_unix_socket *af_unix_namespace_lookup(
+ const struct sockaddr *address, socklen_t length, int type,
+ struct sockaddr_un *normalized_address, socklen_t *normalized_length);
+void af_unix_namespace_detach_locked(struct af_unix_socket *sock);
+int af_unix_namespace_init(void);
+
+int af_unix_dgram_connect(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length);
+int af_unix_dgram_send(struct af_unix_socket *sock, const void *data,
+ size_t size, int flags, const struct sockaddr *to,
+ socklen_t to_length, struct af_unix_rights *rights);
+int af_unix_dgram_receive(struct af_unix_socket *sock, void *data,
+ size_t size, int flags, struct sockaddr *from,
+ socklen_t *from_length, rt_list_t *rights);
+
+int af_unix_stream_listen(struct af_unix_socket *sock, int backlog);
+int af_unix_stream_connect(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length);
+int af_unix_stream_accept(struct af_unix_socket *sock,
+ struct sockaddr *address, socklen_t *length);
+int af_unix_stream_send(struct af_unix_socket *sock, const void *data,
+ size_t size, int flags,
+ struct af_unix_rights *rights);
+int af_unix_stream_receive(struct af_unix_socket *sock, void *data,
+ size_t size, int flags, rt_list_t *rights);
+int af_unix_stream_shutdown(struct af_unix_socket *sock, int how);
+
+#endif /* AF_UNIX_INTERNAL_H__ */
diff --git a/components/net/af_unix/src/af_unix_namespace.c b/components/net/af_unix/src/af_unix_namespace.c
new file mode 100644
index 0000000000..386dfdc05f
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_namespace.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "af_unix_internal.h"
+
+struct af_unix_namespace_entry
+{
+ rt_list_t node;
+ char path[AF_UNIX_PATH_MAX];
+ struct af_unix_socket *sock;
+};
+
+static rt_list_t af_unix_namespace;
+
+static struct af_unix_namespace_entry *af_unix_namespace_find_locked(
+ const char *path)
+{
+ struct af_unix_namespace_entry *entry;
+
+ rt_list_for_each_entry(entry, &af_unix_namespace, node)
+ {
+ if (rt_strcmp(entry->path, path) == 0)
+ {
+ return entry;
+ }
+ }
+ return RT_NULL;
+}
+
+int af_unix_address_parse(const struct sockaddr *address, socklen_t length,
+ char path[AF_UNIX_PATH_MAX])
+{
+ int index;
+ int path_length;
+ char input_path[AF_UNIX_PATH_MAX];
+ char *normalized;
+ const struct sockaddr_un *unix_address;
+ const int path_offset = (int)offsetof(struct sockaddr_un, sun_path);
+
+ if (address == RT_NULL || length <= (socklen_t)path_offset ||
+ length > sizeof(struct sockaddr_un))
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ unix_address = (const struct sockaddr_un *)address;
+ if (unix_address->sa_family != AF_UNIX)
+ {
+ return af_unix_error(EAFNOSUPPORT);
+ }
+
+ path_length = (int)length - path_offset;
+ if (path_length > AF_UNIX_PATH_MAX)
+ {
+ path_length = AF_UNIX_PATH_MAX;
+ }
+ for (index = 0; index < path_length; index++)
+ {
+ if (unix_address->sun_path[index] == '\0')
+ {
+ break;
+ }
+ }
+ if (index == 0)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (index == path_length || index >= AF_UNIX_PATH_MAX)
+ {
+ return af_unix_error(ENAMETOOLONG);
+ }
+
+ rt_memcpy(input_path, unix_address->sun_path, index);
+ input_path[index] = '\0';
+ normalized = dfs_normalize_path(RT_NULL, input_path);
+ if (normalized == RT_NULL)
+ {
+ return af_unix_error(ENOMEM);
+ }
+ if (rt_strlen(normalized) >= AF_UNIX_PATH_MAX)
+ {
+ rt_free(normalized);
+ return af_unix_error(ENAMETOOLONG);
+ }
+ rt_strncpy(path, normalized, AF_UNIX_PATH_MAX);
+ path[AF_UNIX_PATH_MAX - 1] = '\0';
+ rt_free(normalized);
+ return 0;
+}
+
+void af_unix_address_set(struct sockaddr_un *address, socklen_t *length,
+ const char *path)
+{
+ size_t path_length = 0;
+
+ rt_memset(address, 0, sizeof(*address));
+ address->sa_family = AF_UNIX;
+ if (path != RT_NULL)
+ {
+ path_length = rt_strlen(path);
+ if (path_length >= sizeof(address->sun_path))
+ {
+ path_length = sizeof(address->sun_path) - 1;
+ }
+ rt_memcpy(address->sun_path, path, path_length);
+ }
+ *length = (socklen_t)(offsetof(struct sockaddr_un, sun_path) +
+ path_length + (path != RT_NULL ? 1 : 0));
+}
+
+int af_unix_address_copy(struct sockaddr *address, socklen_t *length,
+ const struct sockaddr_un *source,
+ socklen_t source_length)
+{
+ socklen_t copy_length;
+
+ if (address == RT_NULL)
+ {
+ return 0;
+ }
+ if (length == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ copy_length = *length;
+ if (copy_length > source_length)
+ {
+ copy_length = source_length;
+ }
+ if (copy_length > 0)
+ {
+ rt_memcpy(address, source, copy_length);
+ }
+ *length = source_length;
+ return 0;
+}
+
+int af_unix_namespace_bind(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length)
+{
+ int result;
+ char path[AF_UNIX_PATH_MAX];
+ struct af_unix_namespace_entry *entry;
+ struct af_unix_namespace_entry *new_entry;
+
+ result = af_unix_address_parse(address, length, path);
+ if (result < 0)
+ {
+ return result;
+ }
+
+ af_unix_lock();
+ if (sock->closed)
+ {
+ af_unix_unlock();
+ return af_unix_error(EBADF);
+ }
+ if (sock->bound)
+ {
+ af_unix_unlock();
+ return af_unix_error(EINVAL);
+ }
+ af_unix_unlock();
+
+ result = dfs_file_mknod(path, FT_SOCKET,
+ S_IRWXU | S_IRWXG | S_IRWXO);
+ if (result < 0)
+ {
+ return af_unix_error(result == -EEXIST ? EADDRINUSE : -result);
+ }
+
+ new_entry = (struct af_unix_namespace_entry *)rt_calloc(1,
+ sizeof(*new_entry));
+ if (new_entry == RT_NULL)
+ {
+ (void)dfs_file_unlink(path);
+ return af_unix_error(ENOMEM);
+ }
+ rt_strncpy(new_entry->path, path, sizeof(new_entry->path));
+ new_entry->path[sizeof(new_entry->path) - 1] = '\0';
+ rt_list_init(&new_entry->node);
+
+ af_unix_lock();
+ if (sock->closed || sock->bound)
+ {
+ af_unix_unlock();
+ rt_free(new_entry);
+ (void)dfs_file_unlink(path);
+ return af_unix_error(sock->closed ? EBADF : EINVAL);
+ }
+
+ entry = af_unix_namespace_find_locked(path);
+ if (entry == RT_NULL)
+ {
+ entry = new_entry;
+ new_entry = RT_NULL;
+ rt_list_insert_before(&af_unix_namespace, &entry->node);
+ }
+ entry->sock = sock;
+ sock->namespace_entry = entry;
+ sock->bound = 1;
+ af_unix_address_set(&sock->local_address, &sock->local_length, path);
+ af_unix_unlock();
+
+ if (new_entry != RT_NULL)
+ {
+ rt_free(new_entry);
+ }
+ return 0;
+}
+
+struct af_unix_socket *af_unix_namespace_lookup(
+ const struct sockaddr *address, socklen_t length, int type,
+ struct sockaddr_un *normalized_address, socklen_t *normalized_length)
+{
+ int result;
+ char path[AF_UNIX_PATH_MAX];
+ struct stat file_stat;
+ struct af_unix_socket *sock = RT_NULL;
+ struct af_unix_namespace_entry *entry;
+
+ result = af_unix_address_parse(address, length, path);
+ if (result < 0)
+ {
+ return RT_NULL;
+ }
+ result = dfs_file_stat(path, &file_stat);
+ if (result < 0)
+ {
+ af_unix_error(ENOENT);
+ return RT_NULL;
+ }
+ if (!S_ISSOCK(file_stat.st_mode))
+ {
+ af_unix_error(EPROTOTYPE);
+ return RT_NULL;
+ }
+
+ af_unix_lock();
+ entry = af_unix_namespace_find_locked(path);
+ if (entry != RT_NULL && entry->sock != RT_NULL && !entry->sock->closed)
+ {
+ if (entry->sock->type == type)
+ {
+ sock = entry->sock;
+ af_unix_socket_ref_locked(sock);
+ }
+ else
+ {
+ af_unix_error(EPROTOTYPE);
+ }
+ }
+ else
+ {
+ af_unix_error(ECONNREFUSED);
+ }
+ af_unix_unlock();
+
+ if (sock != RT_NULL && normalized_address != RT_NULL &&
+ normalized_length != RT_NULL)
+ {
+ af_unix_address_set(normalized_address, normalized_length, path);
+ }
+ return sock;
+}
+
+void af_unix_namespace_detach_locked(struct af_unix_socket *sock)
+{
+ struct af_unix_namespace_entry *entry;
+
+ entry = (struct af_unix_namespace_entry *)sock->namespace_entry;
+ if (entry != RT_NULL && entry->sock == sock)
+ {
+ entry->sock = RT_NULL;
+ rt_list_remove(&entry->node);
+ rt_free(entry);
+ }
+ sock->namespace_entry = RT_NULL;
+}
+
+int af_unix_namespace_init(void)
+{
+ rt_list_init(&af_unix_namespace);
+ return RT_EOK;
+}
diff --git a/components/net/af_unix/src/af_unix_rights.c b/components/net/af_unix/src/af_unix_rights.c
new file mode 100644
index 0000000000..18189dee1e
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_rights.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <dfs_net.h>
+
+#include "af_unix_internal.h"
+
+static struct rt_mutex af_unix_deferred_lock;
+static rt_list_t af_unix_deferred_rights;
+
+static size_t af_unix_cmsg_next(size_t offset, size_t length,
+ size_t control_length)
+{
+ size_t next;
+
+ next = offset + CMSG_ALIGN(length);
+ if (next > control_length)
+ {
+ next = offset + length;
+ }
+ return next;
+}
+
+static int af_unix_file_is_unix_socket(const struct dfs_file *file)
+{
+ int sal_handle;
+ struct sal_socket *sal_sock;
+
+ if (file == RT_NULL || file->vnode == RT_NULL ||
+ file->vnode->type != FT_SOCKET)
+ {
+ return 0;
+ }
+ if (file->vnode->fops != dfs_net_get_fops() &&
+ file->fops != dfs_net_get_fops())
+ {
+ return 0;
+ }
+
+ sal_handle = (int)(size_t)file->vnode->data;
+ sal_sock = sal_get_socket(sal_handle);
+ return (sal_sock != RT_NULL && sal_sock->domain == AF_UNIX);
+}
+
+int af_unix_rights_create(const struct msghdr *message,
+ struct af_unix_rights **out_rights)
+{
+ int fds[AF_UNIX_RIGHTS_MAX];
+ int result;
+ size_t count = 0;
+ size_t file_index;
+ size_t offset = 0;
+ const char *control;
+ struct af_unix_rights *rights;
+
+ *out_rights = RT_NULL;
+ if (message->msg_controllen == 0)
+ {
+ return 0;
+ }
+ if (message->msg_control == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ control = (const char *)message->msg_control;
+ while (offset < message->msg_controllen)
+ {
+ size_t data_length;
+ size_t fd_count;
+ size_t index;
+ size_t next;
+ const struct cmsghdr *cmsg;
+
+ if (message->msg_controllen - offset < sizeof(*cmsg))
+ {
+ return af_unix_error(EINVAL);
+ }
+ cmsg = (const struct cmsghdr *)(control + offset);
+ if (cmsg->cmsg_len < sizeof(*cmsg) ||
+ cmsg->cmsg_len > message->msg_controllen - offset)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
+ {
+ return af_unix_error(EOPNOTSUPP);
+ }
+
+ data_length = cmsg->cmsg_len - sizeof(*cmsg);
+ if (data_length == 0 || data_length % sizeof(int) != 0)
+ {
+ return af_unix_error(EINVAL);
+ }
+ fd_count = data_length / sizeof(int);
+ if (fd_count > AF_UNIX_RIGHTS_MAX - count)
+ {
+ return af_unix_error(EMSGSIZE);
+ }
+ for (index = 0; index < fd_count; index++)
+ {
+ rt_memcpy(&fds[count],
+ (const char *)CMSG_DATA(cmsg) + index * sizeof(int),
+ sizeof(int));
+ count++;
+ }
+
+ next = af_unix_cmsg_next(offset, cmsg->cmsg_len,
+ message->msg_controllen);
+ if (next <= offset || next > message->msg_controllen)
+ {
+ return af_unix_error(EINVAL);
+ }
+ offset = next;
+ }
+
+ rights = (struct af_unix_rights *)rt_calloc(
+ 1, sizeof(*rights) + (count - 1) * sizeof(rights->files[0]));
+ if (rights == RT_NULL)
+ {
+ return af_unix_error(ENOMEM);
+ }
+ rt_list_init(&rights->node);
+ rights->count = count;
+
+ result = dfs_file_get_refs(fds, count, rights->files);
+ if (result < 0)
+ {
+ rt_free(rights);
+ return af_unix_error(-result);
+ }
+
+ /* Passing AF_UNIX sockets can form an unreclaimable reference
+ * cycle between the in-flight right and the destination queue. */
+ for (file_index = 0; file_index < count; file_index++)
+ {
+ if (af_unix_file_is_unix_socket(rights->files[file_index]))
+ {
+ af_unix_rights_release(rights);
+ return af_unix_error(EOPNOTSUPP);
+ }
+ }
+
+ *out_rights = rights;
+ return 0;
+}
+
+void af_unix_rights_release(struct af_unix_rights *rights)
+{
+ size_t index;
+
+ if (rights == RT_NULL)
+ {
+ return;
+ }
+ for (index = 0; index < rights->count; index++)
+ {
+ if (rights->files[index] != RT_NULL)
+ {
+ dfs_file_put_ref(rights->files[index]);
+ }
+ }
+ rt_free(rights);
+}
+
+void af_unix_rights_list_release(rt_list_t *list)
+{
+ while (!rt_list_isempty(list))
+ {
+ struct af_unix_rights *rights;
+
+ rights = rt_list_entry(list->next, struct af_unix_rights, node);
+ rt_list_remove(&rights->node);
+ af_unix_rights_release(rights);
+ }
+}
+
+void af_unix_rights_defer_locked(struct af_unix_rights *rights)
+{
+ if (rights == RT_NULL)
+ {
+ return;
+ }
+
+ rt_mutex_take(&af_unix_deferred_lock, RT_WAITING_FOREVER);
+ rt_list_insert_before(&af_unix_deferred_rights, &rights->node);
+ rt_mutex_release(&af_unix_deferred_lock);
+}
+
+void af_unix_rights_list_defer_locked(rt_list_t *list)
+{
+ while (!rt_list_isempty(list))
+ {
+ struct af_unix_rights *rights;
+
+ rights = rt_list_entry(list->next, struct af_unix_rights, node);
+ rt_list_remove(&rights->node);
+ af_unix_rights_defer_locked(rights);
+ }
+}
+
+void af_unix_rights_drain(void)
+{
+ rt_list_t pending;
+
+ rt_list_init(&pending);
+ rt_mutex_take(&af_unix_deferred_lock, RT_WAITING_FOREVER);
+ while (!rt_list_isempty(&af_unix_deferred_rights))
+ {
+ rt_list_t *node = af_unix_deferred_rights.next;
+
+ rt_list_remove(node);
+ rt_list_insert_before(&pending, node);
+ }
+ rt_mutex_release(&af_unix_deferred_lock);
+
+ af_unix_rights_list_release(&pending);
+}
+
+static size_t af_unix_rights_count(const rt_list_t *list)
+{
+ size_t count = 0;
+ const rt_list_t *node;
+
+ for (node = list->next; node != list; node = node->next)
+ {
+ const struct af_unix_rights *rights;
+
+ rights = rt_list_entry(node, struct af_unix_rights, node);
+ count += rights->count;
+ }
+ return count;
+}
+
+static size_t af_unix_rights_capacity(const struct msghdr *message,
+ size_t total)
+{
+ size_t capacity;
+
+ if (message->msg_control == RT_NULL ||
+ message->msg_controllen < CMSG_SPACE(sizeof(int)))
+ {
+ return 0;
+ }
+
+ capacity = (message->msg_controllen - sizeof(struct cmsghdr)) /
+ sizeof(int);
+ if (capacity > total)
+ {
+ capacity = total;
+ }
+ while (capacity != 0 &&
+ CMSG_SPACE(capacity * sizeof(int)) > message->msg_controllen)
+ {
+ capacity--;
+ }
+ return capacity;
+}
+
+int af_unix_rights_deliver(rt_list_t *list, struct msghdr *message)
+{
+ int install_result = 0;
+ int *fds = RT_NULL;
+ size_t capacity;
+ size_t delivered = 0;
+ size_t index;
+ size_t total;
+ struct dfs_file **files = RT_NULL;
+ struct cmsghdr *cmsg;
+ rt_list_t *node;
+
+ total = af_unix_rights_count(list);
+ capacity = af_unix_rights_capacity(message, total);
+ if (capacity != 0)
+ {
+ files = (struct dfs_file **)rt_malloc(capacity * sizeof(*files));
+ fds = (int *)rt_malloc(capacity * sizeof(*fds));
+ if (files == RT_NULL || fds == RT_NULL)
+ {
+ capacity = 0;
+ }
+ }
+
+ for (node = list->next; node != list && delivered < capacity;
+ node = node->next)
+ {
+ struct af_unix_rights *rights;
+
+ rights = rt_list_entry(node, struct af_unix_rights, node);
+ for (index = 0; index < rights->count && delivered < capacity; index++)
+ {
+ files[delivered] = rights->files[index];
+ delivered++;
+ }
+ }
+
+ if (delivered != 0)
+ {
+ install_result = dfs_file_install_refs(files, delivered, fds);
+ if (install_result < 0)
+ {
+ delivered = 0;
+ }
+ }
+
+ if (delivered != 0)
+ {
+ size_t transferred = 0;
+
+ for (node = list->next; node != list && transferred < delivered;
+ node = node->next)
+ {
+ struct af_unix_rights *rights;
+
+ rights = rt_list_entry(node, struct af_unix_rights, node);
+ for (index = 0;
+ index < rights->count && transferred < delivered; index++)
+ {
+ rights->files[index] = RT_NULL;
+ transferred++;
+ }
+ }
+
+ cmsg = (struct cmsghdr *)message->msg_control;
+ cmsg->cmsg_len = CMSG_LEN(delivered * sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ rt_memcpy(CMSG_DATA(cmsg), fds, delivered * sizeof(int));
+ message->msg_controllen = CMSG_SPACE(delivered * sizeof(int));
+ }
+ else
+ {
+ message->msg_controllen = 0;
+ }
+
+ if (delivered < total)
+ {
+ message->msg_flags |= MSG_CTRUNC;
+ }
+
+ if (files != RT_NULL)
+ {
+ rt_free(files);
+ }
+ if (fds != RT_NULL)
+ {
+ rt_free(fds);
+ }
+ af_unix_rights_list_release(list);
+ return install_result;
+}
+
+int af_unix_rights_init(void)
+{
+ rt_list_init(&af_unix_deferred_rights);
+ return rt_mutex_init(&af_unix_deferred_lock, "afuright",
+ RT_IPC_FLAG_PRIO);
+}
diff --git a/components/net/af_unix/src/af_unix_stream.c b/components/net/af_unix/src/af_unix_stream.c
new file mode 100644
index 0000000000..cd78880cfb
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_stream.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <string.h>
+
+#include "af_unix_internal.h"
+
+static void af_unix_stream_buffer_write_locked(struct af_unix_socket *sock,
+ const char *data,
+ size_t length)
+{
+ size_t first_length;
+ size_t tail;
+
+ tail = (sock->stream_head + sock->stream_length) %
+ AF_UNIX_STREAM_BUFFER_SIZE;
+ first_length = AF_UNIX_STREAM_BUFFER_SIZE - tail;
+ if (first_length > length)
+ {
+ first_length = length;
+ }
+ rt_memcpy(sock->stream_buffer + tail, data, first_length);
+ if (length > first_length)
+ {
+ rt_memcpy(sock->stream_buffer, data + first_length,
+ length - first_length);
+ }
+ sock->stream_length += length;
+ sock->stream_write_offset += length;
+}
+
+static void af_unix_stream_buffer_read_locked(struct af_unix_socket *sock,
+ char *data, size_t length,
+ int peek)
+{
+ size_t first_length;
+
+ first_length = AF_UNIX_STREAM_BUFFER_SIZE - sock->stream_head;
+ if (first_length > length)
+ {
+ first_length = length;
+ }
+ rt_memcpy(data, sock->stream_buffer + sock->stream_head, first_length);
+ if (length > first_length)
+ {
+ rt_memcpy(data + first_length, sock->stream_buffer,
+ length - first_length);
+ }
+
+ if (!peek)
+ {
+ sock->stream_head = (sock->stream_head + length) %
+ AF_UNIX_STREAM_BUFFER_SIZE;
+ sock->stream_length -= length;
+ sock->stream_read_offset += length;
+ }
+}
+
+static void af_unix_stream_collect_rights_locked(struct af_unix_socket *sock,
+ rt_list_t *rights)
+{
+ while (!rt_list_isempty(&sock->rights_queue))
+ {
+ struct af_unix_rights *entry;
+
+ entry = rt_list_entry(sock->rights_queue.next,
+ struct af_unix_rights, node);
+ if (entry->offset >= sock->stream_read_offset)
+ {
+ break;
+ }
+ rt_list_remove(&entry->node);
+ rt_list_insert_before(rights, &entry->node);
+ }
+}
+
+int af_unix_stream_listen(struct af_unix_socket *sock, int backlog)
+{
+ int result = 0;
+
+ af_unix_lock();
+ if (sock->closed)
+ {
+ result = af_unix_error(EBADF);
+ }
+ else if (sock->type != SOCK_STREAM)
+ {
+ result = af_unix_error(EOPNOTSUPP);
+ }
+ else if (!sock->bound)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else if (sock->connected)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else
+ {
+ if (backlog < 1)
+ {
+ backlog = 1;
+ }
+ if (backlog > AF_UNIX_LISTEN_BACKLOG_MAX)
+ {
+ backlog = AF_UNIX_LISTEN_BACKLOG_MAX;
+ }
+ sock->backlog = backlog;
+ sock->listening = 1;
+ }
+ af_unix_unlock();
+ return result;
+}
+
+int af_unix_stream_connect(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length)
+{
+ int handle;
+ int nonblocking;
+ int timeout;
+ int result = -1;
+ struct sockaddr_un peer_address;
+ socklen_t peer_length;
+ struct af_unix_socket *listener;
+ struct af_unix_socket *accepted;
+
+ listener = af_unix_namespace_lookup(address, length, SOCK_STREAM,
+ &peer_address, &peer_length);
+ if (listener == RT_NULL)
+ {
+ return -1;
+ }
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ result = af_unix_error(EBADF);
+ af_unix_unlock();
+ break;
+ }
+ if (sock->connected)
+ {
+ result = af_unix_error(EISCONN);
+ af_unix_unlock();
+ break;
+ }
+ if (listener->closed || !listener->listening)
+ {
+ result = af_unix_error(ECONNREFUSED);
+ af_unix_unlock();
+ break;
+ }
+ if (listener->pending_count < listener->backlog)
+ {
+ accepted = af_unix_socket_create_locked(SOCK_STREAM);
+ if (accepted == RT_NULL)
+ {
+ result = af_unix_error(ENOMEM);
+ af_unix_unlock();
+ break;
+ }
+ handle = af_unix_handle_alloc_locked(accepted);
+ if (handle < 0)
+ {
+ af_unix_socket_unref_locked(accepted);
+ result = af_unix_error(EMFILE);
+ af_unix_unlock();
+ break;
+ }
+
+ result = af_unix_connect_peers_locked(sock, accepted);
+ if (result < 0)
+ {
+ af_unix_handle_remove_locked(accepted);
+ af_unix_unlock();
+ break;
+ }
+
+ sock->peer_address = peer_address;
+ sock->peer_length = peer_length;
+ accepted->local_address = listener->local_address;
+ accepted->local_length = listener->local_length;
+ accepted->peer_address = sock->local_address;
+ accepted->peer_length = sock->local_length;
+ accepted->pending = 1;
+ rt_list_insert_before(&listener->pending_queue,
+ &accepted->pending_node);
+ listener->pending_count++;
+ rt_wqueue_wakeup_all(&listener->wait_queue,
+ (void *)(rt_ubase_t)POLLIN);
+ af_unix_unlock();
+ result = 0;
+ break;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, 0);
+ timeout = sock->send_timeout;
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ result = af_unix_error(EAGAIN);
+ break;
+ }
+ if (af_unix_wait(&listener->wait_queue, timeout) < 0)
+ {
+ result = -1;
+ break;
+ }
+ }
+
+ af_unix_socket_put(listener);
+ return result;
+}
+
+int af_unix_stream_accept(struct af_unix_socket *sock,
+ struct sockaddr *address, socklen_t *length)
+{
+ int handle;
+ int nonblocking;
+ int timeout;
+ int result;
+ struct af_unix_socket *accepted;
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ af_unix_unlock();
+ return af_unix_error(EBADF);
+ }
+ if (!sock->listening)
+ {
+ af_unix_unlock();
+ return af_unix_error(EINVAL);
+ }
+ if (!rt_list_isempty(&sock->pending_queue))
+ {
+ accepted = rt_list_entry(sock->pending_queue.next,
+ struct af_unix_socket, pending_node);
+ result = af_unix_address_copy(address, length,
+ &accepted->peer_address,
+ accepted->peer_length);
+ if (result < 0)
+ {
+ af_unix_unlock();
+ return result;
+ }
+ rt_list_remove(&accepted->pending_node);
+ accepted->pending = 0;
+ sock->pending_count--;
+ handle = accepted->handle;
+ rt_wqueue_wakeup_all(&sock->wait_queue,
+ (void *)(rt_ubase_t)POLLOUT);
+ af_unix_unlock();
+ return handle;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, 0);
+ timeout = sock->receive_timeout;
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ return af_unix_error(EAGAIN);
+ }
+ if (af_unix_wait(&sock->wait_queue, timeout) < 0)
+ {
+ return -1;
+ }
+ }
+}
+
+int af_unix_stream_send(struct af_unix_socket *sock, const void *data,
+ size_t size, int flags,
+ struct af_unix_rights *rights)
+{
+ int nonblocking;
+ int timeout;
+ size_t copy_length;
+ size_t space;
+ struct af_unix_socket *peer;
+
+ if (size != 0 && data == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (size == 0)
+ {
+ return 0;
+ }
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ af_unix_unlock();
+ return af_unix_error(EBADF);
+ }
+ if (sock->write_shutdown)
+ {
+ af_unix_unlock();
+ return af_unix_error(EPIPE);
+ }
+ peer = sock->peer;
+ if (!sock->connected || peer == RT_NULL)
+ {
+ af_unix_unlock();
+ return af_unix_error(ENOTCONN);
+ }
+ if (peer->closed || peer->read_shutdown)
+ {
+ af_unix_unlock();
+ return af_unix_error(EPIPE);
+ }
+
+ space = AF_UNIX_STREAM_BUFFER_SIZE - peer->stream_length;
+ if (space != 0)
+ {
+ copy_length = size;
+ if (copy_length > space)
+ {
+ copy_length = space;
+ }
+ if (rights != RT_NULL)
+ {
+ rights->offset = peer->stream_write_offset;
+ rt_list_insert_before(&peer->rights_queue, &rights->node);
+ }
+ af_unix_stream_buffer_write_locked(peer, (const char *)data,
+ copy_length);
+ rt_wqueue_wakeup_all(&peer->wait_queue,
+ (void *)(rt_ubase_t)POLLIN);
+ af_unix_unlock();
+ return (int)copy_length;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, flags);
+ timeout = sock->send_timeout;
+ af_unix_socket_ref_locked(peer);
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ af_unix_socket_put(peer);
+ return af_unix_error(EAGAIN);
+ }
+ if (af_unix_wait(&peer->wait_queue, timeout) < 0)
+ {
+ af_unix_socket_put(peer);
+ return -1;
+ }
+ af_unix_socket_put(peer);
+ }
+}
+
+int af_unix_stream_receive(struct af_unix_socket *sock, void *data,
+ size_t size, int flags, rt_list_t *rights)
+{
+ int nonblocking;
+ int timeout;
+ int wait_all;
+ size_t copy_length;
+ size_t received = 0;
+ struct af_unix_socket *peer;
+
+ if (size != 0 && data == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (size == 0)
+ {
+ return 0;
+ }
+ wait_all = ((flags & MSG_WAITALL) != 0 && (flags & MSG_PEEK) == 0);
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ af_unix_unlock();
+ return received != 0 ? (int)received : af_unix_error(EBADF);
+ }
+ if (sock->read_shutdown)
+ {
+ af_unix_unlock();
+ return (int)received;
+ }
+ if (sock->stream_length != 0)
+ {
+ copy_length = size - received;
+ if (copy_length > sock->stream_length)
+ {
+ copy_length = sock->stream_length;
+ }
+ af_unix_stream_buffer_read_locked(sock,
+ (char *)data + received,
+ copy_length,
+ (flags & MSG_PEEK) != 0);
+ received += copy_length;
+ if ((flags & MSG_PEEK) == 0)
+ {
+ af_unix_stream_collect_rights_locked(sock, rights);
+ af_unix_wakeup_writable_locked(sock);
+ }
+ if (!wait_all || received == size)
+ {
+ af_unix_unlock();
+ return (int)received;
+ }
+ }
+
+ peer = sock->peer;
+ if (peer == RT_NULL || peer->closed || peer->write_shutdown)
+ {
+ af_unix_unlock();
+ return (int)received;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, flags);
+ timeout = sock->receive_timeout;
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ return received != 0 ? (int)received : af_unix_error(EAGAIN);
+ }
+ if (af_unix_wait(&sock->wait_queue, timeout) < 0)
+ {
+ return received != 0 ? (int)received : -1;
+ }
+ }
+}
+
+int af_unix_stream_shutdown(struct af_unix_socket *sock, int how)
+{
+ int result = 0;
+ struct af_unix_socket *peer;
+
+ if (how < SHUT_RD || how > SHUT_RDWR)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ af_unix_lock();
+ if (sock->closed)
+ {
+ result = af_unix_error(EBADF);
+ }
+ else if (!sock->connected)
+ {
+ result = af_unix_error(ENOTCONN);
+ }
+ else
+ {
+ if (how == SHUT_RD || how == SHUT_RDWR)
+ {
+ sock->read_shutdown = 1;
+ sock->stream_head = 0;
+ sock->stream_read_offset += sock->stream_length;
+ sock->stream_length = 0;
+ af_unix_rights_list_defer_locked(&sock->rights_queue);
+ }
+ if (how == SHUT_WR || how == SHUT_RDWR)
+ {
+ sock->write_shutdown = 1;
+ }
+ rt_wqueue_wakeup_all(&sock->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT));
+ peer = sock->peer;
+ if (peer != RT_NULL)
+ {
+ rt_wqueue_wakeup_all(&peer->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT));
+ }
+ }
+ af_unix_unlock();
+ return result;
+}
diff --git a/components/net/af_unix/testcases/af_unix_test.c b/components/net/af_unix/testcases/af_unix_test.c
new file mode 100644
index 0000000000..874546fed5
--- /dev/null
+++ b/components/net/af_unix/testcases/af_unix_test.c
@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <utest.h>
+
+#define AF_UNIX_DGRAM_SERVER_PATH "/dev/afudgs"
+#define AF_UNIX_DGRAM_CLIENT_PATH "/dev/afudgc"
+#define AF_UNIX_STREAM_PATH "/dev/afustr"
+#define AF_UNIX_PENDING_PATH "/dev/afupend"
+#define AF_UNIX_PERSIST_PATH "/var/afupath"
+
+static void af_unix_make_address(struct sockaddr_un *address,
+ const char *path)
+{
+ rt_memset(address, 0, sizeof(*address));
+ address->sa_family = AF_UNIX;
+ rt_strncpy(address->sun_path, path, sizeof(address->sun_path) - 1);
+}
+
+static int af_unix_send_rights(int socket_fd, const int *fds, size_t fd_count,
+ char payload)
+{
+ char control[CMSG_SPACE(2 * sizeof(int))];
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ struct msghdr message;
+
+ rt_memset(&message, 0, sizeof(message));
+ rt_memset(control, 0, sizeof(control));
+ iov.iov_base = &payload;
+ iov.iov_len = sizeof(payload);
+ message.msg_iov = &iov;
+ message.msg_iovlen = 1;
+ message.msg_control = control;
+ message.msg_controllen = CMSG_SPACE(fd_count * sizeof(int));
+ cmsg = CMSG_FIRSTHDR(&message);
+ cmsg->cmsg_len = CMSG_LEN(fd_count * sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ rt_memcpy(CMSG_DATA(cmsg), fds, fd_count * sizeof(int));
+ return sendmsg(socket_fd, &message, 0);
+}
+
+static int af_unix_receive_rights(int socket_fd, int flags,
+ size_t control_length, int *fds,
+ size_t *fd_count, int *message_flags,
+ char *payload)
+{
+ char control[CMSG_SPACE(2 * sizeof(int))];
+ int result;
+ size_t count = 0;
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ struct msghdr message;
+
+ rt_memset(&message, 0, sizeof(message));
+ rt_memset(control, 0, sizeof(control));
+ iov.iov_base = payload;
+ iov.iov_len = sizeof(*payload);
+ message.msg_iov = &iov;
+ message.msg_iovlen = 1;
+ message.msg_control = control_length != 0 ? control : RT_NULL;
+ message.msg_controllen = control_length;
+
+ result = recvmsg(socket_fd, &message, flags);
+ if (result >= 0)
+ {
+ cmsg = CMSG_FIRSTHDR(&message);
+ if (cmsg != RT_NULL && cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS &&
+ cmsg->cmsg_len >= CMSG_LEN(sizeof(int)))
+ {
+ count = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(int);
+ rt_memcpy(fds, CMSG_DATA(cmsg), count * sizeof(int));
+ }
+ *fd_count = count;
+ *message_flags = message.msg_flags;
+ }
+ return result;
+}
+
+static void af_unix_test_dgram(void)
+{
+ int server = -1;
+ int client = -1;
+ int error;
+ int result;
+ char buffer[16];
+ const char payload[] = "datagram";
+ struct sockaddr_un server_address;
+ struct sockaddr_un client_address;
+ struct sockaddr_un source_address;
+ socklen_t source_length;
+
+ (void)unlink(AF_UNIX_DGRAM_SERVER_PATH);
+ (void)unlink(AF_UNIX_DGRAM_CLIENT_PATH);
+ af_unix_make_address(&server_address, AF_UNIX_DGRAM_SERVER_PATH);
+ af_unix_make_address(&client_address, AF_UNIX_DGRAM_CLIENT_PATH);
+
+ server = socket(AF_UNIX, SOCK_DGRAM, 0);
+ client = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(server >= 0);
+ uassert_true(client >= 0);
+ if (server < 0 || client < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(bind(server, (struct sockaddr *)&server_address,
+ sizeof(server_address)),
+ 0);
+ uassert_int_equal(bind(client, (struct sockaddr *)&client_address,
+ sizeof(client_address)),
+ 0);
+ uassert_int_equal(connect(client, (struct sockaddr *)&server_address,
+ sizeof(server_address)),
+ 0);
+ uassert_int_equal(send(client, payload, sizeof(payload), 0),
+ sizeof(payload));
+
+ source_length = sizeof(source_address);
+ result = recvfrom(server, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&source_address, &source_length);
+ uassert_int_equal(result, sizeof(payload));
+ uassert_buf_equal(buffer, payload, sizeof(payload));
+ uassert_str_equal(source_address.sun_path, AF_UNIX_DGRAM_CLIENT_PATH);
+
+ result = fcntl(server, F_SETFL, O_NONBLOCK);
+ uassert_int_equal(result, 0);
+ result = recv(server, buffer, sizeof(buffer), 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_true(error == EAGAIN || error == EWOULDBLOCK);
+
+__exit:
+ if (client >= 0)
+ {
+ closesocket(client);
+ }
+ if (server >= 0)
+ {
+ closesocket(server);
+ }
+ (void)unlink(AF_UNIX_DGRAM_CLIENT_PATH);
+ (void)unlink(AF_UNIX_DGRAM_SERVER_PATH);
+}
+
+static void af_unix_test_pathname_lifetime(void)
+{
+ int first = -1;
+ int second = -1;
+ int error;
+ int result;
+ struct stat file_stat;
+ struct sockaddr_un address;
+
+ (void)unlink(AF_UNIX_PERSIST_PATH);
+ af_unix_make_address(&address, AF_UNIX_PERSIST_PATH);
+
+ first = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(first >= 0);
+ if (first < 0)
+ {
+ goto __exit;
+ }
+ uassert_int_equal(bind(first, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ uassert_int_equal(stat(AF_UNIX_PERSIST_PATH, &file_stat), 0);
+ uassert_true(S_ISSOCK(file_stat.st_mode));
+
+ closesocket(first);
+ first = -1;
+ uassert_int_equal(stat(AF_UNIX_PERSIST_PATH, &file_stat), 0);
+ uassert_true(S_ISSOCK(file_stat.st_mode));
+
+ second = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(second >= 0);
+ if (second < 0)
+ {
+ goto __exit;
+ }
+ result = bind(second, (struct sockaddr *)&address, sizeof(address));
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EADDRINUSE);
+
+ uassert_int_equal(unlink(AF_UNIX_PERSIST_PATH), 0);
+ uassert_int_equal(bind(second, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+
+__exit:
+ if (second >= 0)
+ {
+ closesocket(second);
+ }
+ if (first >= 0)
+ {
+ closesocket(first);
+ }
+ (void)unlink(AF_UNIX_PERSIST_PATH);
+}
+
+static void af_unix_test_stream(void)
+{
+ int listener = -1;
+ int client = -1;
+ int accepted = -1;
+ char buffer[16];
+ const char request[] = "request";
+ const char response[] = "response";
+ struct sockaddr_un address;
+
+ (void)unlink(AF_UNIX_STREAM_PATH);
+ af_unix_make_address(&address, AF_UNIX_STREAM_PATH);
+
+ listener = socket(AF_UNIX, SOCK_STREAM, 0);
+ client = socket(AF_UNIX, SOCK_STREAM, 0);
+ uassert_true(listener >= 0);
+ uassert_true(client >= 0);
+ if (listener < 0 || client < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(bind(listener, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ uassert_int_equal(listen(listener, 2), 0);
+ uassert_int_equal(connect(client, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ accepted = accept(listener, RT_NULL, RT_NULL);
+ uassert_true(accepted >= 0);
+ if (accepted < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(send(client, request, sizeof(request), 0),
+ sizeof(request));
+ uassert_int_equal(recv(accepted, buffer, sizeof(buffer), 0),
+ sizeof(request));
+ uassert_buf_equal(buffer, request, sizeof(request));
+ uassert_int_equal(send(accepted, response, sizeof(response), 0),
+ sizeof(response));
+ uassert_int_equal(recv(client, buffer, sizeof(buffer), 0),
+ sizeof(response));
+ uassert_buf_equal(buffer, response, sizeof(response));
+
+ uassert_int_equal(shutdown(client, SHUT_WR), 0);
+ uassert_int_equal(recv(accepted, buffer, sizeof(buffer), 0), 0);
+
+__exit:
+ if (accepted >= 0)
+ {
+ closesocket(accepted);
+ }
+ if (client >= 0)
+ {
+ closesocket(client);
+ }
+ if (listener >= 0)
+ {
+ closesocket(listener);
+ }
+ (void)unlink(AF_UNIX_STREAM_PATH);
+}
+
+static void af_unix_test_dgram_readiness(void)
+{
+ int error;
+ int index;
+ int result;
+ int sockets[2] = { -1, -1 };
+ char buffer[2];
+ const char payload[] = "x";
+ struct pollfd poll_fd;
+
+ result = socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, sockets);
+ uassert_int_equal(result, 0);
+ if (result < 0)
+ {
+ return;
+ }
+
+ poll_fd.fd = sockets[1];
+ poll_fd.events = POLLIN;
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 0);
+
+ for (index = 0; index < AF_UNIX_DGRAM_QUEUE_LEN; index++)
+ {
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+ }
+ result = send(sockets[0], payload, sizeof(payload), 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EAGAIN);
+
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLIN) != 0);
+ uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
+ sizeof(payload));
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+
+ result = send(sockets[0], payload, AF_UNIX_DGRAM_MAX_SIZE + 1, 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EMSGSIZE);
+
+ closesocket(sockets[0]);
+ closesocket(sockets[1]);
+}
+
+static void af_unix_test_stream_readiness(void)
+{
+ int result;
+ int sockets[2] = { -1, -1 };
+ char buffer[8];
+ const char payload[] = "poll";
+ struct pollfd poll_fd;
+
+ result = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ uassert_int_equal(result, 0);
+ if (result < 0)
+ {
+ return;
+ }
+
+ poll_fd.fd = sockets[1];
+ poll_fd.events = POLLIN | POLLOUT;
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLOUT) != 0);
+ uassert_true((poll_fd.revents & POLLIN) == 0);
+
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLIN) != 0);
+ uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
+ sizeof(payload));
+
+ uassert_int_equal(shutdown(sockets[0], SHUT_WR), 0);
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLIN) != 0);
+ uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0), 0);
+
+ closesocket(sockets[0]);
+ closesocket(sockets[1]);
+}
+
+static void af_unix_test_pending_close(void)
+{
+ int client = -1;
+ int error;
+ int listener = -1;
+ int result;
+ const char payload[] = "pending";
+ struct sockaddr_un address;
+
+ (void)unlink(AF_UNIX_PENDING_PATH);
+ af_unix_make_address(&address, AF_UNIX_PENDING_PATH);
+
+ listener = socket(AF_UNIX, SOCK_STREAM, 0);
+ client = socket(AF_UNIX, SOCK_STREAM, 0);
+ uassert_true(listener >= 0);
+ uassert_true(client >= 0);
+ if (listener < 0 || client < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(bind(listener, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ uassert_int_equal(listen(listener, 1), 0);
+ uassert_int_equal(connect(client, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ closesocket(listener);
+ listener = -1;
+
+ result = send(client, payload, sizeof(payload), 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_true(error == ENOTCONN || error == EPIPE);
+
+__exit:
+ if (client >= 0)
+ {
+ closesocket(client);
+ }
+ if (listener >= 0)
+ {
+ closesocket(listener);
+ }
+ (void)unlink(AF_UNIX_PENDING_PATH);
+}
+
+static void af_unix_test_socketpair(void)
+{
+ int type_index;
+ int types[2] = { SOCK_DGRAM, SOCK_STREAM };
+ int sockets[2];
+ char buffer[8];
+ char first_part[3];
+ char second_part[3];
+ const char payload[] = "pair";
+ struct iovec receive_iov[2];
+ struct iovec send_iov[2];
+ struct msghdr receive_message;
+ struct msghdr send_message;
+
+ for (type_index = 0; type_index < 2; type_index++)
+ {
+ uassert_int_equal(socketpair(AF_UNIX, types[type_index], 0,
+ sockets),
+ 0);
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+ uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
+ sizeof(payload));
+ uassert_buf_equal(buffer, payload, sizeof(payload));
+
+ rt_memset(&send_message, 0, sizeof(send_message));
+ send_iov[0].iov_base = (void *)payload;
+ send_iov[0].iov_len = 2;
+ send_iov[1].iov_base = (void *)(payload + 2);
+ send_iov[1].iov_len = sizeof(payload) - 2;
+ send_message.msg_iov = send_iov;
+ send_message.msg_iovlen = 2;
+ uassert_int_equal(sendmsg(sockets[0], &send_message, 0),
+ sizeof(payload));
+
+ rt_memset(&receive_message, 0, sizeof(receive_message));
+ receive_iov[0].iov_base = first_part;
+ receive_iov[0].iov_len = sizeof(first_part);
+ receive_iov[1].iov_base = second_part;
+ receive_iov[1].iov_len = sizeof(second_part);
+ receive_message.msg_iov = receive_iov;
+ receive_message.msg_iovlen = 2;
+ uassert_int_equal(recvmsg(sockets[1], &receive_message, 0),
+ sizeof(payload));
+ uassert_buf_equal(first_part, payload, sizeof(first_part));
+ uassert_buf_equal(second_part, payload + sizeof(first_part),
+ sizeof(payload) - sizeof(first_part));
+ closesocket(sockets[0]);
+ closesocket(sockets[1]);
+ }
+}
+
+static void af_unix_test_rights_transfer(void)
+{
+ char data;
+ char payload;
+ int received_fds[2] = { -1, -1 };
+ int pipe_fds[2][2] = { { -1, -1 }, { -1, -1 } };
+ int send_fds[2];
+ int sockets[2] = { -1, -1 };
+ int type_index;
+ int types[2] = { SOCK_DGRAM, SOCK_STREAM };
+ size_t fd_count;
+ int message_flags;
+
+ for (type_index = 0; type_index < 2; type_index++)
+ {
+ received_fds[0] = -1;
+ received_fds[1] = -1;
+ pipe_fds[0][0] = -1;
+ pipe_fds[0][1] = -1;
+ pipe_fds[1][0] = -1;
+ pipe_fds[1][1] = -1;
+ sockets[0] = -1;
+ sockets[1] = -1;
+
+ uassert_int_equal(socketpair(AF_UNIX, types[type_index], 0,
+ sockets),
+ 0);
+ uassert_int_equal(pipe(pipe_fds[0]), 0);
+ uassert_int_equal(pipe(pipe_fds[1]), 0);
+ if (sockets[0] < 0 || pipe_fds[0][0] < 0 || pipe_fds[1][0] < 0)
+ {
+ goto __iteration_exit;
+ }
+
+ uassert_int_equal(write(pipe_fds[0][1], "a", 1), 1);
+ uassert_int_equal(write(pipe_fds[1][1], "b", 1), 1);
+ send_fds[0] = pipe_fds[0][0];
+ send_fds[1] = pipe_fds[1][0];
+ uassert_int_equal(af_unix_send_rights(sockets[0], send_fds, 2,
+ 'r'),
+ 1);
+ close(pipe_fds[0][0]);
+ pipe_fds[0][0] = -1;
+ close(pipe_fds[1][0]);
+ pipe_fds[1][0] = -1;
+
+ fd_count = 0;
+ message_flags = 0;
+ uassert_int_equal(af_unix_receive_rights(
+ sockets[1], 0,
+ CMSG_SPACE(2 * sizeof(int)), received_fds,
+ &fd_count, &message_flags, &payload),
+ 1);
+ uassert_int_equal(payload, 'r');
+ uassert_int_equal(fd_count, 2);
+ uassert_int_equal(message_flags & MSG_CTRUNC, 0);
+ uassert_int_equal(read(received_fds[0], &data, 1), 1);
+ uassert_int_equal(data, 'a');
+ uassert_int_equal(read(received_fds[1], &data, 1), 1);
+ uassert_int_equal(data, 'b');
+
+ __iteration_exit:
+ if (received_fds[0] >= 0)
+ {
+ close(received_fds[0]);
+ }
+ if (received_fds[1] >= 0)
+ {
+ close(received_fds[1]);
+ }
+ if (pipe_fds[0][0] >= 0)
+ {
+ close(pipe_fds[0][0]);
+ }
+ if (pipe_fds[0][1] >= 0)
+ {
+ close(pipe_fds[0][1]);
+ }
+ if (pipe_fds[1][0] >= 0)
+ {
+ close(pipe_fds[1][0]);
+ }
+ if (pipe_fds[1][1] >= 0)
+ {
+ close(pipe_fds[1][1]);
+ }
+ if (sockets[0] >= 0)
+ {
+ closesocket(sockets[0]);
+ }
+ if (sockets[1] >= 0)
+ {
+ closesocket(sockets[1]);
+ }
+ }
+}
+
+static void af_unix_test_rights_truncation(void)
+{
+ char payload;
+ int message_flags = 0;
+ int pipe_fds[2][2] = { { -1, -1 }, { -1, -1 } };
+ int received_fds[2] = { -1, -1 };
+ int send_fds[2];
+ int sockets[2] = { -1, -1 };
+ size_t fd_count = 0;
+
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets), 0);
+ uassert_int_equal(pipe(pipe_fds[0]), 0);
+ uassert_int_equal(pipe(pipe_fds[1]), 0);
+ if (sockets[0] < 0 || pipe_fds[0][0] < 0 || pipe_fds[1][0] < 0)
+ {
+ goto __exit;
+ }
+
+ send_fds[0] = pipe_fds[0][0];
+ send_fds[1] = pipe_fds[1][0];
+ uassert_int_equal(af_unix_send_rights(sockets[0], send_fds, 2, 't'), 1);
+ uassert_int_equal(af_unix_receive_rights(
+ sockets[1], 0, 0,
+ received_fds, &fd_count, &message_flags, &payload),
+ 1);
+ uassert_int_equal(fd_count, 0);
+ uassert_true((message_flags & MSG_CTRUNC) != 0);
+
+__exit:
+ if (received_fds[0] >= 0)
+ {
+ close(received_fds[0]);
+ }
+ if (pipe_fds[0][0] >= 0)
+ {
+ close(pipe_fds[0][0]);
+ }
+ if (pipe_fds[0][1] >= 0)
+ {
+ close(pipe_fds[0][1]);
+ }
+ if (pipe_fds[1][0] >= 0)
+ {
+ close(pipe_fds[1][0]);
+ }
+ if (pipe_fds[1][1] >= 0)
+ {
+ close(pipe_fds[1][1]);
+ }
+ if (sockets[0] >= 0)
+ {
+ closesocket(sockets[0]);
+ }
+ if (sockets[1] >= 0)
+ {
+ closesocket(sockets[1]);
+ }
+}
+
+static void af_unix_test_rights_peek_and_invalid(void)
+{
+ char payload;
+ int error;
+ int invalid_fd = -1;
+ int message_flags = 0;
+ int pipe_fds[2] = { -1, -1 };
+ int received_fds[2] = { -1, -1 };
+ int sockets[2] = { -1, -1 };
+ int result;
+ size_t fd_count = 0;
+
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
+ sockets),
+ 0);
+ uassert_int_equal(pipe(pipe_fds), 0);
+ if (sockets[0] < 0 || pipe_fds[0] < 0)
+ {
+ goto __exit;
+ }
+
+ result = af_unix_send_rights(sockets[0], &invalid_fd, 1, 'x');
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EBADF);
+
+ uassert_int_equal(af_unix_send_rights(sockets[0], pipe_fds, 1, 'p'), 1);
+ fd_count = 0;
+ uassert_int_equal(af_unix_receive_rights(
+ sockets[1], MSG_PEEK, CMSG_SPACE(sizeof(int)),
+ received_fds, &fd_count, &message_flags, &payload),
+ 1);
+ uassert_int_equal(payload, 'p');
+ uassert_int_equal(fd_count, 0);
+
+ fd_count = 0;
+ message_flags = 0;
+ uassert_int_equal(af_unix_receive_rights(
+ sockets[1], 0, CMSG_SPACE(sizeof(int)),
+ received_fds, &fd_count, &message_flags, &payload),
+ 1);
+ uassert_int_equal(fd_count, 1);
+
+__exit:
+ if (received_fds[0] >= 0)
+ {
+ close(received_fds[0]);
+ }
+ if (pipe_fds[0] >= 0)
+ {
+ close(pipe_fds[0]);
+ }
+ if (pipe_fds[1] >= 0)
+ {
+ close(pipe_fds[1]);
+ }
+ if (sockets[0] >= 0)
+ {
+ closesocket(sockets[0]);
+ }
+ if (sockets[1] >= 0)
+ {
+ closesocket(sockets[1]);
+ }
+}
+
+static void af_unix_test_rights_socket(void)
+{
+ char payload = 0;
+ int carrier[2] = { -1, -1 };
+ int error;
+ int message_flags = 0;
+ int passed[2] = { -1, -1 };
+ int received_fds[2] = { -1, -1 };
+ int result;
+ size_t fd_count = 0;
+
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM, 0, carrier), 0);
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_DGRAM, 0, passed), 0);
+ if (carrier[0] < 0 || passed[0] < 0)
+ {
+ goto __exit;
+ }
+
+ result = af_unix_send_rights(carrier[0], &passed[1], 1, 's');
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EOPNOTSUPP);
+
+ result = af_unix_receive_rights(carrier[1], MSG_DONTWAIT,
+ CMSG_SPACE(sizeof(int)), received_fds,
+ &fd_count, &message_flags, &payload);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_true(error == EAGAIN || error == EWOULDBLOCK);
+ uassert_int_equal(fd_count, 0);
+
+ uassert_int_equal(send(passed[0], "q", 1, 0), 1);
+ uassert_int_equal(recv(passed[1], &payload, 1, 0), 1);
+ uassert_int_equal(payload, 'q');
+
+__exit:
+ if (received_fds[0] >= 0)
+ {
+ closesocket(received_fds[0]);
+ }
+ if (passed[0] >= 0)
+ {
+ closesocket(passed[0]);
+ }
+ if (passed[1] >= 0)
+ {
+ closesocket(passed[1]);
+ }
+ if (carrier[0] >= 0)
+ {
+ closesocket(carrier[0]);
+ }
+ if (carrier[1] >= 0)
+ {
+ closesocket(carrier[1]);
+ }
+}
+
+static void af_unix_testcase(void)
+{
+ UTEST_UNIT_RUN(af_unix_test_dgram);
+ UTEST_UNIT_RUN(af_unix_test_stream);
+ UTEST_UNIT_RUN(af_unix_test_dgram_readiness);
+ UTEST_UNIT_RUN(af_unix_test_stream_readiness);
+ UTEST_UNIT_RUN(af_unix_test_pending_close);
+ UTEST_UNIT_RUN(af_unix_test_socketpair);
+ UTEST_UNIT_RUN(af_unix_test_pathname_lifetime);
+ UTEST_UNIT_RUN(af_unix_test_rights_transfer);
+ UTEST_UNIT_RUN(af_unix_test_rights_truncation);
+ UTEST_UNIT_RUN(af_unix_test_rights_peek_and_invalid);
+ UTEST_UNIT_RUN(af_unix_test_rights_socket);
+}
+
+UTEST_TC_EXPORT(af_unix_testcase, "components.net.af_unix", RT_NULL,
+ RT_NULL, 20);
diff --git a/components/net/sal/include/sal_low_lvl.h b/components/net/sal/include/sal_low_lvl.h
index c19c10ffc1..759bad730f 100644
--- a/components/net/sal/include/sal_low_lvl.h
+++ b/components/net/sal/include/sal_low_lvl.h
@@ -48,6 +48,7 @@ typedef uint32_t socklen_t;
struct sockaddr;
struct msghdr;
struct addrinfo;
+struct sal_proto_family;
struct sal_socket
{
uint32_t magic; /* SAL socket magic word */
@@ -58,6 +59,7 @@ struct sal_socket
int protocol;
struct netdev *netdev; /* SAL network interface device */
+ const struct sal_proto_family *protocol_family; /* selected protocol provider */
void *user_data; /* user-specific data */
#ifdef SAL_USING_TLS
@@ -109,6 +111,9 @@ struct sal_proto_family
/* SAL(Socket Abstraction Layer) initialize */
int sal_init(void);
+/* Register and find protocol providers which do not require a netdev. */
+int sal_proto_family_register(const struct sal_proto_family *pf);
+const struct sal_proto_family *sal_proto_family_find(int family);
/* Get SAL socket object by socket descriptor */
struct sal_socket *sal_get_socket(int sock);
diff --git a/components/net/sal/include/sal_socket.h b/components/net/sal/include/sal_socket.h
index 527f3fe440..7d74310094 100644
--- a/components/net/sal/include/sal_socket.h
+++ b/components/net/sal/include/sal_socket.h
@@ -119,6 +119,9 @@ typedef uint16_t in_port_t;
#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */
#define MSG_MORE 0x10 /* Sender will send more */
+/* Output-only flags returned through struct msghdr. */
+#define MSG_CTRUNC 0x08 /* Control data was discarded due to truncation */
+
#define MSG_ERRQUEUE 0x2000 /* Fetch message from error queue */
#define MSG_CONFIRM 0x0800 /* Confirm path validity */
diff --git a/components/net/sal/socket/net_sockets.c b/components/net/sal/socket/net_sockets.c
index 615bb0ed03..50c20b908a 100644
--- a/components/net/sal/socket/net_sockets.c
+++ b/components/net/sal/socket/net_sockets.c
@@ -725,7 +725,11 @@ int closesocket(int s)
return -1;
}
+#ifdef RT_USING_DFS_V2
+ if (dfs_file_close(d) == 0)
+#else
if (sal_closesocket(socket) == 0)
+#endif
{
error = 0;
}
@@ -771,8 +775,19 @@ RTM_EXPORT(closesocket);
int socketpair(int domain, int type, int protocol, int *fds)
{
rt_err_t ret = 0;
+ int nonblocking = 0;
int sock_fds[2];
+ if ((type & SOCK_CLOEXEC) != 0)
+ {
+ type &= ~SOCK_CLOEXEC;
+ }
+ if ((type & SOCK_NONBLOCK) != 0)
+ {
+ nonblocking = 1;
+ type &= ~SOCK_NONBLOCK;
+ }
+
fds[0] = socket(domain, type, protocol);
if (fds[0] < 0)
{
@@ -799,6 +814,11 @@ int socketpair(int domain, int type, int protocol, int *fds)
closesocket(fds[0]);
closesocket(fds[1]);
}
+ else if (nonblocking)
+ {
+ (void)fcntl(fds[0], F_SETFL, O_NONBLOCK);
+ (void)fcntl(fds[1], F_SETFL, O_NONBLOCK);
+ }
return ret;
}
diff --git a/components/net/sal/src/sal_socket.c b/components/net/sal/src/sal_socket.c
index fa9eb9cc74..b420eb2957 100644
--- a/components/net/sal/src/sal_socket.c
+++ b/components/net/sal/src/sal_socket.c
@@ -44,16 +44,14 @@
#include <rtdbg.h>
#define VALID_PROTOCOL(protocol) ((protocol) >= 0 && (protocol) <= IPPROTO_RAW)
-#define VALID_COMBO(domain, type, protocol) \
- ( \
- (((domain) == AF_INET || (domain) == AF_INET6) && \
- (((type) == SOCK_STREAM && ((protocol) == 0 || (protocol) == IPPROTO_TCP)) || \
- ((type) == SOCK_DGRAM && ((protocol) == 0 || (protocol) == IPPROTO_UDP)) || \
- ((type) == SOCK_RAW && ((protocol) == IPPROTO_RAW)) \
- )) || \
- ((domain) == AF_UNIX && (type) == SOCK_STREAM && (protocol) == 0) || \
- ((domain) == AF_NETLINK && (type) == SOCK_RAW && (protocol) == 0) \
- )
+#define VALID_COMBO(domain, type, protocol) \
+ ( \
+ (((domain) == AF_INET || (domain) == AF_INET6) && \
+ (((type) == SOCK_STREAM && ((protocol) == 0 || (protocol) == IPPROTO_TCP)) || \
+ ((type) == SOCK_DGRAM && ((protocol) == 0 || (protocol) == IPPROTO_UDP)) || \
+ ((type) == SOCK_RAW && ((protocol) == IPPROTO_RAW)))) || \
+ ((domain) == AF_UNIX && ((type) == SOCK_STREAM || (type) == SOCK_DGRAM) && (protocol) == 0) || \
+ ((domain) == AF_NETLINK && (type) == SOCK_RAW && (protocol) == 0))
/* the socket table used to dynamic allocate sockets */
struct sal_socket_table
@@ -89,9 +87,10 @@ static struct sal_socket_table socket_table;
static struct rt_mutex sal_core_lock;
static rt_bool_t init_ok = RT_FALSE;
static struct sal_netdev_res_table sal_dev_res_tbl[SAL_SOCKETS_NUM];
+static const struct sal_proto_family *local_proto_families[SAL_PROTO_FAMILIES_NUM];
-#define IS_SOCKET_PROTO_TLS(sock) (((sock)->protocol == PROTOCOL_TLS) || \
- ((sock)->protocol == PROTOCOL_DTLS))
+#define IS_SOCKET_PROTO_TLS(sock) (((sock)->protocol == PROTOCOL_TLS) || \
+ ((sock)->protocol == PROTOCOL_DTLS))
#define SAL_SOCKOPS_PROTO_TLS_VALID(sock, name) (proto_tls && (proto_tls->ops->name) && IS_SOCKET_PROTO_TLS(sock))
#define SAL_SOCKOPT_PROTO_TLS_EXEC(sock, name, optval, optlen) \
@@ -113,23 +112,42 @@ static struct sal_netdev_res_table sal_dev_res_tbl[SAL_SOCKETS_NUM];
} \
} while (0)
-#define SAL_NETDEV_IS_UP(netdev) \
- do \
- { \
- if (!netdev_is_up(netdev)) \
- { \
- return -1; \
- } \
+#define SAL_NETDEV_IS_UP(netdev) \
+ do \
+ { \
+ if ((netdev) && !netdev_is_up(netdev)) \
+ { \
+ return -1; \
+ } \
} while (0)
-#define SAL_NETDEV_SOCKETOPS_VALID(netdev, pf, ops) \
- do \
- { \
- (pf) = (struct sal_proto_family *)netdev->sal_user_data; \
- if ((pf)->skt_ops->ops == RT_NULL) \
- { \
- return -1; \
- } \
+#define SAL_SOCKETOPS_VALID(sock, pf, ops) \
+ do \
+ { \
+ (pf) = (sock)->protocol_family; \
+ if ((pf) == RT_NULL || (pf)->skt_ops == RT_NULL || \
+ (pf)->skt_ops->ops == RT_NULL) \
+ { \
+ rt_set_errno(EOPNOTSUPP); \
+ return -1; \
+ } \
+ } while (0)
+
+#define SAL_NETDEV_SOCKETOPS_VALID(netdev, pf, ops) \
+ do \
+ { \
+ if ((netdev) == RT_NULL) \
+ { \
+ rt_set_errno(EOPNOTSUPP); \
+ return -1; \
+ } \
+ (pf) = (struct sal_proto_family *)(netdev)->sal_user_data; \
+ if ((pf) == RT_NULL || (pf)->skt_ops == RT_NULL || \
+ (pf)->skt_ops->ops == RT_NULL) \
+ { \
+ rt_set_errno(EOPNOTSUPP); \
+ return -1; \
+ } \
} while (0)
#define SAL_NETDEV_NETDBOPS_VALID(netdev, pf, ops) \
@@ -181,6 +199,58 @@ int sal_init(void)
}
INIT_COMPONENT_EXPORT(sal_init);
+int sal_proto_family_register(const struct sal_proto_family *pf)
+{
+ int index;
+
+ if (pf == RT_NULL || pf->skt_ops == RT_NULL)
+ {
+ return -EINVAL;
+ }
+
+ for (index = 0; index < SAL_PROTO_FAMILIES_NUM; index++)
+ {
+ if (local_proto_families[index] == pf)
+ {
+ return RT_EOK;
+ }
+ if (local_proto_families[index] != RT_NULL &&
+ (local_proto_families[index]->family == pf->family ||
+ local_proto_families[index]->sec_family == pf->family))
+ {
+ return -EEXIST;
+ }
+ }
+
+ for (index = 0; index < SAL_PROTO_FAMILIES_NUM; index++)
+ {
+ if (local_proto_families[index] == RT_NULL)
+ {
+ local_proto_families[index] = pf;
+ return RT_EOK;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+const struct sal_proto_family *sal_proto_family_find(int family)
+{
+ int index;
+
+ for (index = 0; index < SAL_PROTO_FAMILIES_NUM; index++)
+ {
+ const struct sal_proto_family *pf = local_proto_families[index];
+
+ if (pf != RT_NULL && (pf->family == family || pf->sec_family == family))
+ {
+ return pf;
+ }
+ }
+
+ return RT_NULL;
+}
+
#ifdef SAL_INTERNET_CHECK
/* check SAL network interface device internet status */
static void check_netdev_internet_up_work(struct rt_work *work, void *work_data)
@@ -456,7 +526,7 @@ int sal_netdev_cleanup(struct netdev *netdev)
static int socket_init(int family, int type, int protocol, struct sal_socket **res)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
struct netdev *netdv_def = netdev_default;
struct netdev *netdev = RT_NULL;
rt_bool_t flag = RT_FALSE;
@@ -495,6 +565,14 @@ static int socket_init(int family, int type, int protocol, struct sal_socket **r
return -4;
}
+ pf = sal_proto_family_find(family);
+ if (pf != RT_NULL)
+ {
+ sock->protocol_family = pf;
+ sock->netdev = RT_NULL;
+ return 0;
+ }
+
/* Existing netdev selection logic */
if (netdv_def && netdev_is_up(netdv_def))
{
@@ -503,6 +581,7 @@ static int socket_init(int family, int type, int protocol, struct sal_socket **r
if (pf != RT_NULL && pf->skt_ops && (pf->family == family || pf->sec_family == family))
{
sock->netdev = netdv_def;
+ sock->protocol_family = pf;
flag = RT_TRUE;
}
}
@@ -518,6 +597,11 @@ static int socket_init(int family, int type, int protocol, struct sal_socket **r
}
sock->netdev = netdev;
+ sock->protocol_family = (const struct sal_proto_family *)netdev->sal_user_data;
+ if (sock->protocol_family == RT_NULL || sock->protocol_family->skt_ops == RT_NULL)
+ {
+ return -3;
+ }
}
LOG_D("Socket init success: domain=%d, type=%d, protocol=%d, netdev=%s",
@@ -607,6 +691,7 @@ static int socket_new(void)
sock->socket = idx + SAL_SOCKET_OFFSET;
sock->magic = SAL_SOCKET_MAGIC;
sock->netdev = RT_NULL;
+ sock->protocol_family = RT_NULL;
sock->user_data = RT_NULL;
#ifdef SAL_USING_TLS
sock->user_data_tls = RT_NULL;
@@ -633,6 +718,7 @@ static void socket_delete(int socket)
RT_ASSERT(sock != RT_NULL);
sock->magic = 0;
sock->netdev = RT_NULL;
+ sock->protocol_family = RT_NULL;
socket_free(st, idx);
sal_unlock();
}
@@ -641,7 +727,7 @@ int sal_accept(int socket, struct sockaddr *addr, socklen_t *addrlen)
{
int new_socket;
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
@@ -650,12 +736,11 @@ int sal_accept(int socket, struct sockaddr *addr, socklen_t *addrlen)
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket operations */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, accept);
+ SAL_SOCKETOPS_VALID(sock, pf, accept);
new_socket = pf->skt_ops->accept((int)(size_t)sock->user_data, addr, addrlen);
if (new_socket != -1)
{
- int retval;
int new_sal_socket;
struct sal_socket *new_sock;
@@ -668,19 +753,11 @@ int sal_accept(int socket, struct sockaddr *addr, socklen_t *addrlen)
return -1;
}
- retval = socket_init(sock->domain, sock->type, sock->protocol, &new_sock);
- if (retval < 0)
- {
- pf->skt_ops->closesocket(new_socket);
- rt_memset(new_sock, 0x00, sizeof(struct sal_socket));
- /* socket init failed, delete socket */
- socket_delete(new_sal_socket);
- LOG_E("New socket registered failed, return error %d.", retval);
- return -1;
- }
-
- /* new socket create by accept should have the same netdev with server*/
+ new_sock->domain = sock->domain;
+ new_sock->type = sock->type;
+ new_sock->protocol = sock->protocol;
new_sock->netdev = sock->netdev;
+ new_sock->protocol_family = sock->protocol_family;
/* socket structure user_data used to store the acquired new socket */
new_sock->user_data = (void *)(size_t)new_socket;
@@ -707,7 +784,7 @@ static void sal_sockaddr_to_ipaddr(const struct sockaddr *name, ip_addr_t *local
int sal_bind(int socket, const struct sockaddr *name, socklen_t namelen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
ip_addr_t input_ipaddr;
RT_ASSERT(name);
@@ -751,19 +828,20 @@ int sal_bind(int socket, const struct sockaddr *name, socklen_t namelen)
return -1;
}
sock->netdev = new_netdev;
+ sock->protocol_family = input_pf;
sock->user_data = (void *)(size_t)new_socket;
}
}
}
/* check and get protocol families by the network interface device */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, bind);
+ SAL_SOCKETOPS_VALID(sock, pf, bind);
return pf->skt_ops->bind((int)(size_t)sock->user_data, name, namelen);
}
int sal_shutdown(int socket, int how)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
int error = 0;
/* get the socket object by socket descriptor */
@@ -771,7 +849,7 @@ int sal_shutdown(int socket, int how)
/* shutdown operation not need to check network interface status */
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, shutdown);
+ SAL_SOCKETOPS_VALID(sock, pf, shutdown);
if (pf->skt_ops->shutdown((int)(size_t)sock->user_data, how) == 0)
{
@@ -798,13 +876,13 @@ int sal_shutdown(int socket, int how)
int sal_getpeername(int socket, struct sockaddr *name, socklen_t *namelen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, getpeername);
+ SAL_SOCKETOPS_VALID(sock, pf, getpeername);
return pf->skt_ops->getpeername((int)(size_t)sock->user_data, name, namelen);
}
@@ -812,13 +890,13 @@ int sal_getpeername(int socket, struct sockaddr *name, socklen_t *namelen)
int sal_getsockname(int socket, struct sockaddr *name, socklen_t *namelen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, getsockname);
+ SAL_SOCKETOPS_VALID(sock, pf, getsockname);
return pf->skt_ops->getsockname((int)(size_t)sock->user_data, name, namelen);
}
@@ -826,13 +904,13 @@ int sal_getsockname(int socket, struct sockaddr *name, socklen_t *namelen)
int sal_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, getsockopt);
+ SAL_SOCKETOPS_VALID(sock, pf, getsockopt);
return pf->skt_ops->getsockopt((int)(size_t)sock->user_data, level, optname, optval, optlen);
}
@@ -840,13 +918,13 @@ int sal_getsockopt(int socket, int level, int optname, void *optval, socklen_t *
int sal_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, setsockopt);
+ SAL_SOCKETOPS_VALID(sock, pf, setsockopt);
#ifdef SAL_USING_TLS
if (level == SOL_TLS)
@@ -887,7 +965,7 @@ int sal_setsockopt(int socket, int level, int optname, const void *optval, sockl
int sal_connect(int socket, const struct sockaddr *name, socklen_t namelen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
int ret;
/* get the socket object by socket descriptor */
@@ -896,7 +974,7 @@ int sal_connect(int socket, const struct sockaddr *name, socklen_t namelen)
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, connect);
+ SAL_SOCKETOPS_VALID(sock, pf, connect);
ret = pf->skt_ops->connect((int)(size_t)sock->user_data, name, namelen);
#ifdef SAL_USING_TLS
@@ -917,13 +995,13 @@ int sal_connect(int socket, const struct sockaddr *name, socklen_t namelen)
int sal_listen(int socket, int backlog)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, listen);
+ SAL_SOCKETOPS_VALID(sock, pf, listen);
return pf->skt_ops->listen((int)(size_t)sock->user_data, backlog);
}
@@ -931,7 +1009,7 @@ int sal_listen(int socket, int backlog)
int sal_sendmsg(int socket, const struct msghdr *message, int flags)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
@@ -939,7 +1017,7 @@ int sal_sendmsg(int socket, const struct msghdr *message, int flags)
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, sendmsg);
+ SAL_SOCKETOPS_VALID(sock, pf, sendmsg);
#ifdef SAL_USING_TLS
if (SAL_SOCKOPS_PROTO_TLS_VALID(sock, send))
@@ -964,7 +1042,7 @@ int sal_sendmsg(int socket, const struct msghdr *message, int flags)
int sal_recvmsg(int socket, struct msghdr *message, int flags)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
@@ -972,7 +1050,7 @@ int sal_recvmsg(int socket, struct msghdr *message, int flags)
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, recvmsg);
+ SAL_SOCKETOPS_VALID(sock, pf, recvmsg);
#ifdef SAL_USING_TLS
if (SAL_SOCKOPS_PROTO_TLS_VALID(sock, recv))
@@ -998,7 +1076,7 @@ int sal_recvfrom(int socket, void *mem, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
@@ -1006,7 +1084,7 @@ int sal_recvfrom(int socket, void *mem, size_t len, int flags,
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, recvfrom);
+ SAL_SOCKETOPS_VALID(sock, pf, recvfrom);
#ifdef SAL_USING_TLS
if (SAL_SOCKOPS_PROTO_TLS_VALID(sock, recv))
@@ -1032,7 +1110,7 @@ int sal_sendto(int socket, const void *dataptr, size_t size, int flags,
const struct sockaddr *to, socklen_t tolen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
@@ -1040,7 +1118,7 @@ int sal_sendto(int socket, const void *dataptr, size_t size, int flags,
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, sendto);
+ SAL_SOCKETOPS_VALID(sock, pf, sendto);
#ifdef SAL_USING_TLS
if (SAL_SOCKOPS_PROTO_TLS_VALID(sock, send))
@@ -1067,7 +1145,7 @@ int sal_socket(int domain, int type, int protocol)
int retval;
int socket, proto_socket;
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* allocate a new socket and registered socket options */
socket = socket_new();
@@ -1094,7 +1172,7 @@ int sal_socket(int domain, int type, int protocol)
}
/* valid the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, socket);
+ SAL_SOCKETOPS_VALID(sock, pf, socket);
proto_socket = pf->skt_ops->socket(domain, type, protocol);
if (proto_socket >= 0)
@@ -1122,7 +1200,7 @@ int sal_socketpair(int domain, int type, int protocol, int *fds)
int unix_fd[2];
struct sal_socket *socka;
struct sal_socket *sockb;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
if (domain == AF_UNIX)
{
@@ -1131,15 +1209,17 @@ int sal_socketpair(int domain, int type, int protocol, int *fds)
SAL_SOCKET_OBJ_GET(sockb, fds[1]);
/* valid the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(socka->netdev, pf, socket);
+ if (socka->protocol_family != sockb->protocol_family)
+ {
+ rt_set_errno(EINVAL);
+ return -1;
+ }
+ SAL_SOCKETOPS_VALID(socka, pf, socketpair);
unix_fd[0] = (int)(size_t)socka->user_data;
unix_fd[1] = (int)(size_t)sockb->user_data;
- if (pf->skt_ops->socketpair)
- {
- return pf->skt_ops->socketpair(domain, type, protocol, unix_fd);
- }
+ return pf->skt_ops->socketpair(domain, type, protocol, unix_fd);
}
rt_set_errno(EINVAL);
@@ -1150,7 +1230,7 @@ int sal_socketpair(int domain, int type, int protocol, int *fds)
int sal_closesocket(int socket)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
int error = 0;
/* get the socket object by socket descriptor */
@@ -1158,7 +1238,7 @@ int sal_closesocket(int socket)
/* clsoesocket operation not need to vaild network interface status */
/* valid the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, closesocket);
+ SAL_SOCKETOPS_VALID(sock, pf, closesocket);
if (pf->skt_ops->closesocket((int)(size_t)sock->user_data) == 0)
{
@@ -1196,13 +1276,19 @@ int sal_ioctlsocket(int socket, long cmd, void *arg)
struct netdev *netdev = RT_NULL;
struct netdev *cur_netdev_list = netdev_list;
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
struct sockaddr_in *addr_in = RT_NULL;
struct sockaddr *addr = RT_NULL;
ip_addr_t input_ipaddr;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
+ if (sock->netdev == RT_NULL)
+ {
+ SAL_SOCKETOPS_VALID(sock, pf, ioctlsocket);
+ return pf->skt_ops->ioctlsocket((int)(size_t)sock->user_data, cmd, arg);
+ }
+
struct sal_ifreq *ifr = (struct sal_ifreq *)arg;
if (ifr != RT_NULL)
@@ -1528,7 +1614,7 @@ int sal_ioctlsocket(int socket, long cmd, void *arg)
int sal_poll(struct dfs_file *file, struct rt_pollreq *req)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
int socket = (int)(size_t)file->vnode->data;
/* get the socket object by socket descriptor */
@@ -1537,7 +1623,7 @@ int sal_poll(struct dfs_file *file, struct rt_pollreq *req)
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, poll);
+ SAL_SOCKETOPS_VALID(sock, pf, poll);
return pf->skt_ops->poll(file, req);
}