else if (assoc >= 0) {
/* file-descriptor based association */
- bool is_socket = (sb.st_mode & S_IFMT) == S_IFSOCK;
FILE *fdinfo;
if (ul_path_stat(pc, &sb, AT_SYMLINK_NOFOLLOW, name) == 0)
if (is_nsfs_dev(f->stat.st_dev))
load_sock_xinfo(pc, name, f->stat.st_ino);
- if (is_socket)
- load_fdsk_xinfo(proc, assoc);
fdinfo = ul_path_fopenf(pc, "r", "fdinfo/%d", assoc);
if (fdinfo) {
*/
void load_sock_xinfo(struct path_cxt *pc, const char *name, ino_t netns);
bool is_nsfs_dev(dev_t dev);
-void load_fdsk_xinfo(struct proc *proc, int fd);
/*
* POSIX Mqueue
#include <linux/netlink.h> /* NETLINK_*, NLMSG_* */
#include <linux/rtnetlink.h> /* RTA_*, struct rtattr, */
#include <linux/sock_diag.h> /* SOCK_DIAG_BY_FAMILY */
-#include <linux/sockios.h> /* SIOCGSKNS */
#include <linux/un.h> /* UNIX_PATH_MAX */
#include <linux/unix_diag.h> /* UNIX_DIAG_*, UDIAG_SHOW_*,
struct unix_diag_req */
#include <search.h> /* tfind, tsearch */
#include <stdint.h>
#include <string.h>
-#include <sys/ioctl.h>
#include <sys/socket.h> /* SOCK_* */
#include "sysfs.h"
}
}
-static int load_fdsk_xinfo_cb(int sk, void *data __attribute__((__unused__)))
+void load_fdsk_xinfo(ino_t netns_ino, int netns_fd)
{
- int nsfd;
- struct netns *nsobj;
- struct stat sb;
- int r = -1;
-
- nsfd = ioctl(sk, SIOCGSKNS);
- if (nsfd < 0)
- return nsfd;
-
- if (fstat(nsfd, &sb) < 0)
- goto out_nsfd;
-
- if (is_sock_xinfo_loaded(sb.st_ino))
- goto out_nsfd;
-
- r = 0;
- nsobj = mark_sock_xinfo_loaded(sb.st_ino);
- load_sock_xinfo_with_fd(nsfd, nsobj);
-
-out_nsfd:
- close(nsfd);
- return r;
-}
-
-void load_fdsk_xinfo(struct proc *proc, int fd)
-{
- call_with_foreign_fd(proc->pid, fd,
- load_fdsk_xinfo_cb, NULL);
-
+ if (!is_sock_xinfo_loaded(netns_ino)) {
+ struct netns *nsobj = mark_sock_xinfo_loaded(netns_ino);
+ load_sock_xinfo_with_fd(netns_fd, nsobj);
+ }
}
void initialize_sock_xinfos(void)
#include <sys/types.h>
#include <sys/xattr.h>
+#include <linux/sockios.h> /* SIOCGSKNS */
+#include <sys/ioctl.h>
#include "lsfd.h"
#include "sock.h"
{
struct sock *sock = (struct sock *)file;
- sock->xinfo = get_sock_xinfo(file->stat.st_ino);
+ if (!sock->xinfo)
+ sock->xinfo = get_sock_xinfo(file->stat.st_ino);
+
if (sock->xinfo) {
struct ipc *ipc = get_ipc(file);
if (ipc)
}
return false;
case COL_SOCK_NETNS:
- if (sock->xinfo) {
+ if (sock->xinfo && sock->xinfo->netns_inode != 0) {
xasprintf(&str, "%llu",
(unsigned long long)sock->xinfo->netns_inode);
break;
}
+ if (sock->netns_inode) {
+ xasprintf(&str, "%llu",
+ (unsigned long long)sock->netns_inode);
+ break;
+ }
return false;
case COL_SOCK_TYPE:
if (sock->xinfo
return true;
}
+
+static ino_t get_netns_from_socket(int sk)
+{
+ int nsfd;
+ struct stat sb;
+
+ nsfd = ioctl(sk, SIOCGSKNS);
+ if (nsfd < 0)
+ return 0;
+
+ if (fstat(nsfd, &sb) < 0) {
+ close(nsfd);
+ return 0;
+ }
+
+ load_fdsk_xinfo(sb.st_ino, nsfd);
+
+ close(nsfd);
+ return sb.st_ino;
+}
+
static void init_sock_content(struct file *file)
{
int fd;
init_endpoint(&sock->endpoint);
}
+static bool sock_needs_target_fd(struct file *file)
+{
+ struct sock *sock = (struct sock *)file;
+
+ /* DB behind get_sock_xinfo() is not fulfilled enough yet.
+ * However, if we are lucky enough to find an xinfo in the DB,
+ * we can avoid calling sock_inspect_target_fd().
+ *
+ * Even if get_sock_xinfo() returns NULL in this timing,
+ * eventually we call it again attach_sock_xinfo.
+ * When calling attach_sock_xinfo, the DB is completely
+ * fulfilled.
+ */
+ sock->xinfo = get_sock_xinfo(file->stat.st_ino);
+ return !(sock->xinfo && sock->xinfo->netns_inode != 0);
+}
+
+static void sock_inspect_target_fd(struct file *file, int fd)
+{
+ struct sock *sock = (struct sock *)file;
+
+ sock->netns_inode = get_netns_from_socket(fd);
+}
+
static void free_sock_content(struct file *file)
{
struct sock *sock = (struct sock *)file;
.fill_column = sock_fill_column,
.attach_xinfo = attach_sock_xinfo,
.initialize_content = init_sock_content,
+ .needs_target_fd = sock_needs_target_fd,
+ .inspect_target_fd = sock_inspect_target_fd,
.free_content = free_sock_content,
.initialize_class = initialize_sock_class,
.finalize_class = finalize_sock_class,
char *protoname;
struct sock_xinfo *xinfo;
struct ipc_endpoint endpoint;
+
+ /*
+ * There are two netns_inode fields reachable from struct sock:
+ *
+ * - sock->xinfo->netns_inode: filled via sock_diag netlink (when available)
+ * - sock->netns_inode: filled via ioctl(SIOCGSKNS)
+ *
+ * Use sock->netns_inode as a fallback when xinfo is unavailable or
+ * does not provide the netns inode.
+ */
+ ino_t netns_inode;
+
};
struct sock_xinfo_class {
void finalize_sock_xinfos(void);
struct sock_xinfo *get_sock_xinfo(ino_t inode);
+void load_fdsk_xinfo(ino_t netns_ino, int netns_fd);
#endif /* UTIL_LINUX_LSFD_SOCK_H */
--- /dev/null
+ioctl(fd, SIOCGSKNS): 0
+INODE: 0
+SOCK_NETNS: 0
+SOCK_NETNS: OK
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2026 Masatake YAMATO <yamato@redhat.com>
+#
+# This file is part of util-linux.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This file is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="SOCK.NETNS of an unbound tcp socket"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_skip_nonroot
+ts_skip_docker
+
+. "$TS_SELF/lsfd-functions.bash"
+ts_check_test_command "$TS_CMD_LSFD"
+ts_check_test_command "$TS_HELPER_MKFDS"
+
+ts_cd "$TS_OUTDIR"
+
+PID=
+INODE=
+SOCK_NETNS=
+FD=5
+
+"$TS_HELPER_MKFDS" --quiet --dont-pause netns "$FD"
+SIOCGSKNS=$?
+if [[ "$SIOCGSKNS" == "$TS_EXIT_NOTSUPP" ]]; then
+ ts_skip "ioctl(fd, SIOCGSKNS) is not available"
+elif [[ "$SIOCGSKNS" == "$EPERM" ]]; then
+ ts_skip "ioctl(fd, SIOCGSKNS) is not usable"
+else
+ echo "ioctl(fd, SIOCGSKNS): $SIOCGSKNS" > "$TS_OUTPUT"
+fi
+
+{
+ coproc MKFDS { "$TS_HELPER_MKFDS" tcp-bare $FD; }
+ if read -r -u "${MKFDS[0]}" PID; then
+ INODE=$(${TS_CMD_LSFD} -n --raw -o INODE -p "${PID}" -Q "ASSOC == 'net'")
+ echo INODE: $?
+ SOCK_NETNS=$(${TS_CMD_LSFD} -n --raw -o SOCK.NETNS -p "${PID}" -Q "FD == $FD")
+ echo SOCK_NETNS: $?
+ if [[ -z "$SOCK_NETNS" ]]; then
+ echo "SOCK_NETNS: failed (empty, inode: $INODE)"
+ elif [[ "$SOCK_NETNS" == "$INODE" ]]; then
+ echo "SOCK_NETNS: OK"
+ else
+ echo "SOCK_NETNS: failed (inode: $INODE, sock_netns: $SOCK_NETNS)"
+ fi
+ echo DONE >&"${MKFDS[1]}"
+ fi
+ wait "${MKFDS_PID}"
+} >> "$TS_OUTPUT" 2>&1
+
+ts_finalize