738 lines
18 KiB
C
738 lines
18 KiB
C
/*
|
|
* lsfd-sock-xinfo.c - read various information from files under /proc/net/
|
|
*
|
|
* Copyright (C) 2022 Red Hat, Inc. All rights reserved.
|
|
* Written by Masatake YAMATO <yamato@redhat.com>
|
|
*
|
|
* This program 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 program is distributed in the hope that it would 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
#include <arpa/inet.h> /* inet_ntop */
|
|
#include <fcntl.h> /* open(2) */
|
|
#include <linux/net.h> /* SS_* */
|
|
#include <linux/un.h> /* UNIX_PATH_MAX */
|
|
#include <sched.h> /* for setns(2) */
|
|
#include <search.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h> /* SOCK_* */
|
|
|
|
#include "xalloc.h"
|
|
#include "nls.h"
|
|
#include "libsmartcols.h"
|
|
#include "sysfs.h"
|
|
#include "bitops.h"
|
|
|
|
#include "lsfd.h"
|
|
#include "lsfd-sock.h"
|
|
|
|
static void load_xinfo_from_proc_unix(ino_t netns_inode);
|
|
static void load_xinfo_from_proc_tcp(ino_t netns_inode);
|
|
static void load_xinfo_from_proc_udp(ino_t netns_inode);
|
|
|
|
static int self_netns_fd = -1;
|
|
struct stat self_netns_sb;
|
|
|
|
static void *xinfo_tree; /* for tsearch/tfind */
|
|
static void *netns_tree;
|
|
|
|
static int netns_compare(const void *a, const void *b)
|
|
{
|
|
if (*(ino_t *)a < *(ino_t *)b)
|
|
return -1;
|
|
else if (*(ino_t *)a > *(ino_t *)b)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static bool is_sock_xinfo_loaded(ino_t netns)
|
|
{
|
|
return tfind(&netns, &netns_tree, netns_compare)? true: false;
|
|
}
|
|
|
|
static void mark_sock_xinfo_loaded(ino_t ino)
|
|
{
|
|
ino_t *netns = xmalloc(sizeof(ino));
|
|
ino_t **tmp;
|
|
|
|
*netns = ino;
|
|
tmp = tsearch(netns, &netns_tree, netns_compare);
|
|
if (tmp == NULL)
|
|
errx(EXIT_FAILURE, _("failed to allocate memory"));
|
|
}
|
|
|
|
static void load_sock_xinfo_no_nsswitch(ino_t netns)
|
|
{
|
|
load_xinfo_from_proc_unix(netns);
|
|
load_xinfo_from_proc_tcp(netns);
|
|
load_xinfo_from_proc_udp(netns);
|
|
}
|
|
|
|
static void load_sock_xinfo_with_fd(int fd, ino_t netns)
|
|
{
|
|
if (setns(fd, CLONE_NEWNET) == 0) {
|
|
load_sock_xinfo_no_nsswitch(netns);
|
|
setns(self_netns_fd, CLONE_NEWNET);
|
|
}
|
|
}
|
|
|
|
void load_sock_xinfo(struct path_cxt *pc, const char *name, ino_t netns)
|
|
{
|
|
if (self_netns_fd == -1)
|
|
return;
|
|
|
|
if (!is_sock_xinfo_loaded(netns)) {
|
|
int fd;
|
|
|
|
mark_sock_xinfo_loaded(netns);
|
|
fd = ul_path_open(pc, O_RDONLY, name);
|
|
if (fd < 0)
|
|
return;
|
|
|
|
load_sock_xinfo_with_fd(fd, netns);
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
void initialize_sock_xinfos(void)
|
|
{
|
|
struct path_cxt *pc;
|
|
DIR *dir;
|
|
struct dirent *d;
|
|
|
|
self_netns_fd = open("/proc/self/ns/net", O_RDONLY);
|
|
|
|
if (self_netns_fd < 0)
|
|
load_sock_xinfo_no_nsswitch(0);
|
|
else {
|
|
if (fstat(self_netns_fd, &self_netns_sb) == 0) {
|
|
mark_sock_xinfo_loaded(self_netns_sb.st_ino);
|
|
load_sock_xinfo_no_nsswitch(self_netns_sb.st_ino);
|
|
}
|
|
}
|
|
|
|
/* Load /proc/net/{unix,...} of the network namespace
|
|
* specified with netns files under /var/run/netns/.
|
|
*
|
|
* `ip netns' command pins a network namespace on
|
|
* /var/run/netns.
|
|
*/
|
|
pc = ul_new_path("/var/run/netns");
|
|
if (!pc)
|
|
err(EXIT_FAILURE, _("failed to alloc path context for /var/run/netns"));
|
|
dir = ul_path_opendir(pc, NULL);
|
|
if (dir == NULL) {
|
|
ul_unref_path(pc);
|
|
return;
|
|
}
|
|
while ((d = readdir(dir))) {
|
|
struct stat sb;
|
|
int fd;
|
|
if (ul_path_stat(pc, &sb, 0, d->d_name) < 0)
|
|
continue;
|
|
if (is_sock_xinfo_loaded(sb.st_ino))
|
|
continue;
|
|
mark_sock_xinfo_loaded(sb.st_ino);
|
|
fd = ul_path_open(pc, O_RDONLY, d->d_name);
|
|
if (fd < 0)
|
|
continue;
|
|
load_sock_xinfo_with_fd(fd, sb.st_ino);
|
|
close(fd);
|
|
}
|
|
closedir(dir);
|
|
ul_unref_path(pc);
|
|
}
|
|
|
|
static void free_sock_xinfo(void *node)
|
|
{
|
|
struct sock_xinfo *xinfo = node;
|
|
if (xinfo->class->free)
|
|
xinfo->class->free(xinfo);
|
|
free(node);
|
|
}
|
|
|
|
void finalize_sock_xinfos(void)
|
|
{
|
|
if (self_netns_fd != -1)
|
|
close(self_netns_fd);
|
|
tdestroy(netns_tree, free);
|
|
tdestroy(xinfo_tree, free_sock_xinfo);
|
|
}
|
|
|
|
static int xinfo_compare(const void *a, const void *b)
|
|
{
|
|
if (((struct sock_xinfo *)a)->inode < ((struct sock_xinfo *)b)->inode)
|
|
return -1;
|
|
if (((struct sock_xinfo *)a)->inode > ((struct sock_xinfo *)b)->inode)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static void add_sock_info(struct sock_xinfo *xinfo)
|
|
{
|
|
struct sock_xinfo **tmp = tsearch(xinfo, &xinfo_tree, xinfo_compare);
|
|
|
|
if (tmp == NULL)
|
|
errx(EXIT_FAILURE, _("failed to allocate memory"));
|
|
}
|
|
|
|
struct sock_xinfo *get_sock_xinfo(ino_t netns_inode)
|
|
{
|
|
struct sock_xinfo **xinfo = tfind(&netns_inode, &xinfo_tree, xinfo_compare);
|
|
|
|
if (xinfo)
|
|
return *xinfo;
|
|
return NULL;
|
|
}
|
|
|
|
bool is_nsfs_dev(dev_t dev)
|
|
{
|
|
return (dev == self_netns_sb.st_dev);
|
|
}
|
|
|
|
static const char *sock_decode_type(uint16_t type)
|
|
{
|
|
switch (type) {
|
|
case SOCK_STREAM:
|
|
return "stream";
|
|
case SOCK_DGRAM:
|
|
return "dgram";
|
|
case SOCK_RAW:
|
|
return "raw";
|
|
case SOCK_RDM:
|
|
return "rdm";
|
|
case SOCK_SEQPACKET:
|
|
return "seqpacket";
|
|
case SOCK_DCCP:
|
|
return "dccp";
|
|
case SOCK_PACKET:
|
|
return "packet";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Protocol specific code
|
|
*/
|
|
|
|
/*
|
|
* UNIX
|
|
*/
|
|
struct unix_xinfo {
|
|
struct sock_xinfo sock;
|
|
int acceptcon; /* flags */
|
|
uint16_t type;
|
|
uint8_t st;
|
|
char path[
|
|
UNIX_PATH_MAX
|
|
+ 1 /* for @ */
|
|
+ 1 /* \0? */
|
|
];
|
|
};
|
|
|
|
static const char *unix_decode_state(uint8_t st)
|
|
{
|
|
switch (st) {
|
|
case SS_FREE:
|
|
return "free";
|
|
case SS_UNCONNECTED:
|
|
return "unconnected";
|
|
case SS_CONNECTING:
|
|
return "connecting";
|
|
case SS_CONNECTED:
|
|
return "connected";
|
|
case SS_DISCONNECTING:
|
|
return "disconnecting";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
static char *unix_get_name(struct sock_xinfo *sock_xinfo,
|
|
struct sock *sock)
|
|
{
|
|
struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
|
|
const char *state = unix_decode_state(ux->st);
|
|
char *str = NULL;
|
|
|
|
if (sock->protoname && (strcmp(sock->protoname, "UNIX-STREAM") == 0))
|
|
xasprintf(&str, "state=%s%s%s",
|
|
(ux->acceptcon)? "listen": state,
|
|
*(ux->path)? " path=": "",
|
|
*(ux->path)? ux->path: "");
|
|
else
|
|
xasprintf(&str, "state=%s%s%s type=%s",
|
|
(ux->acceptcon)? "listen": state,
|
|
*(ux->path)? " path=": "",
|
|
*(ux->path)? ux->path: "",
|
|
sock_decode_type(ux->type));
|
|
return str;
|
|
}
|
|
|
|
static char *unix_get_type(struct sock_xinfo *sock_xinfo,
|
|
struct sock *sock __attribute__((__unused__)))
|
|
{
|
|
const char *str;
|
|
struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
|
|
|
|
str = sock_decode_type(ux->type);
|
|
return strdup(str);
|
|
}
|
|
|
|
static char *unix_get_state(struct sock_xinfo *sock_xinfo,
|
|
struct sock *sock __attribute__((__unused__)))
|
|
{
|
|
const char *str;
|
|
struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
|
|
|
|
if (ux->acceptcon)
|
|
return strdup("listen");
|
|
|
|
str = unix_decode_state(ux->st);
|
|
return strdup(str);
|
|
}
|
|
|
|
static bool unix_get_listening(struct sock_xinfo *sock_xinfo,
|
|
struct sock *sock __attribute__((__unused__)))
|
|
{
|
|
struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
|
|
|
|
return ux->acceptcon;
|
|
}
|
|
|
|
static bool unix_fill_column(struct proc *proc __attribute__((__unused__)),
|
|
struct sock_xinfo *sock_xinfo,
|
|
struct sock *sock __attribute__((__unused__)),
|
|
struct libscols_line *ln __attribute__((__unused__)),
|
|
int column_id,
|
|
size_t column_index __attribute__((__unused__)),
|
|
char **str)
|
|
{
|
|
struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
|
|
|
|
switch(column_id) {
|
|
case COL_UNIX_PATH:
|
|
if (*ux->path) {
|
|
*str = strdup(ux->path);
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static const struct sock_xinfo_class unix_xinfo_class = {
|
|
.class = "unix",
|
|
.get_name = unix_get_name,
|
|
.get_type = unix_get_type,
|
|
.get_state = unix_get_state,
|
|
.get_listening = unix_get_listening,
|
|
.fill_column = unix_fill_column,
|
|
.free = NULL,
|
|
};
|
|
|
|
/* #define UNIX_LINE_LEN 54 + 21 + UNIX_LINE_LEN + 1 */
|
|
#define UNIX_LINE_LEN 256
|
|
static void load_xinfo_from_proc_unix(ino_t netns_inode)
|
|
{
|
|
char line[UNIX_LINE_LEN];
|
|
FILE *unix_fp;
|
|
|
|
unix_fp = fopen("/proc/net/unix", "r");
|
|
if (!unix_fp)
|
|
return;
|
|
|
|
if (fgets(line, sizeof(line), unix_fp) == NULL)
|
|
goto out;
|
|
if (!(line[0] == 'N' && line[1] == 'u' && line[2] == 'm'))
|
|
/* Unexpected line */
|
|
goto out;
|
|
|
|
while (fgets(line, sizeof(line), unix_fp)) {
|
|
uint64_t flags;
|
|
uint32_t type;
|
|
unsigned int st;
|
|
unsigned long inode;
|
|
struct unix_xinfo *ux;
|
|
char path[ sizeof_member(struct unix_xinfo, path) ] = { 0 };
|
|
|
|
|
|
if (sscanf(line, "%*x: %*x %*x %" SCNx64 " %x %x %lu %s",
|
|
&flags, &type, &st, &inode, path) < 4)
|
|
continue;
|
|
|
|
if (inode == 0)
|
|
continue;
|
|
|
|
ux = xcalloc(1, sizeof(struct unix_xinfo));
|
|
ux->sock.class = &unix_xinfo_class;
|
|
ux->sock.inode = (ino_t)inode;
|
|
ux->sock.netns_inode = netns_inode;
|
|
|
|
ux->acceptcon = !!flags;
|
|
ux->type = type;
|
|
ux->st = st;
|
|
xstrncpy(ux->path, path, sizeof(ux->path));
|
|
|
|
add_sock_info((struct sock_xinfo *)ux);
|
|
}
|
|
|
|
out:
|
|
fclose(unix_fp);
|
|
}
|
|
|
|
/*
|
|
* AF_INET
|
|
*/
|
|
struct inet_xinfo {
|
|
struct sock_xinfo sock;
|
|
uint32_t local_addr;
|
|
uint32_t remote_addr;
|
|
};
|
|
|
|
static bool inet_fill_column(struct proc *proc __attribute__((__unused__)),
|
|
struct inet_xinfo *inet,
|
|
struct sock *sock __attribute__((__unused__)),
|
|
struct libscols_line *ln __attribute__((__unused__)),
|
|
int column_id,
|
|
size_t column_index __attribute__((__unused__)),
|
|
char **str)
|
|
{
|
|
struct in_addr n;
|
|
struct in_addr *nptr = NULL;
|
|
char s[INET_ADDRSTRLEN];
|
|
|
|
switch(column_id) {
|
|
case COL_INET_LADDR:
|
|
n.s_addr = inet->local_addr;
|
|
nptr = &n;
|
|
break;
|
|
case COL_INET_RADDR:
|
|
n.s_addr = inet->remote_addr;
|
|
nptr = &n;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (nptr && inet_ntop(AF_INET, &n, s, sizeof(s))) {
|
|
*str = strdup(s);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* TCP
|
|
*/
|
|
struct tcp_xinfo {
|
|
struct inet_xinfo inet;
|
|
uint16_t local_port;
|
|
uint16_t remote_port;
|
|
unsigned int st;
|
|
};
|
|
|
|
enum tcp_state {
|
|
/*
|
|
* Taken from linux/include/net/tcp_states.h.
|
|
* (GPL-2.0-or-later)
|
|
*/
|
|
TCP_ESTABLISHED = 1,
|
|
TCP_SYN_SENT,
|
|
TCP_SYN_RECV,
|
|
TCP_FIN_WAIT1,
|
|
TCP_FIN_WAIT2,
|
|
TCP_TIME_WAIT,
|
|
TCP_CLOSE,
|
|
TCP_CLOSE_WAIT,
|
|
TCP_LAST_ACK,
|
|
TCP_LISTEN,
|
|
TCP_CLOSING,
|
|
TCP_NEW_SYN_RECV,
|
|
|
|
TCP_MAX_STATES /* Leave at the end! */
|
|
};
|
|
|
|
static const char *tcp_decode_state(unsigned int st)
|
|
{
|
|
const char * table [] = {
|
|
[TCP_ESTABLISHED] = "established",
|
|
[TCP_SYN_SENT] = "syn-sent",
|
|
[TCP_SYN_RECV] = "syn-recv",
|
|
[TCP_FIN_WAIT1] = "fin-wait1",
|
|
[TCP_FIN_WAIT2] = "fin-wait2",
|
|
[TCP_TIME_WAIT] = "time-wait",
|
|
[TCP_CLOSE] = "close",
|
|
[TCP_CLOSE_WAIT] = "close-wait",
|
|
[TCP_LAST_ACK] = "last-ack",
|
|
[TCP_LISTEN] = "listen",
|
|
[TCP_CLOSING] = "closing",
|
|
[TCP_NEW_SYN_RECV] = "new-syn-recv",
|
|
};
|
|
|
|
if (st < TCP_MAX_STATES)
|
|
return table[st];
|
|
return "unknown";
|
|
}
|
|
|
|
static char *tcp_get_name(struct sock_xinfo *sock_xinfo,
|
|
struct sock *sock __attribute__((__unused__)))
|
|
{
|
|
char *str = NULL;
|
|
struct inet_xinfo *inet = ((struct inet_xinfo *)sock_xinfo);
|
|
struct tcp_xinfo *tcp = ((struct tcp_xinfo *)sock_xinfo);
|
|
struct in_addr local_n, remote_n;
|
|
char local_s[INET_ADDRSTRLEN], remote_s[INET_ADDRSTRLEN];
|
|
|
|
local_n.s_addr = inet->local_addr;
|
|
remote_n.s_addr = inet->remote_addr;
|
|
if (!inet_ntop(AF_INET, &local_n, local_s, sizeof(local_s)))
|
|
xasprintf(&str, "state=%s", tcp_decode_state(tcp->st));
|
|
else if (tcp->st == TCP_LISTEN
|
|
|| !inet_ntop(AF_INET, &remote_n, remote_s, sizeof(remote_s)))
|
|
xasprintf(&str, "state=%s laddr=%s:%u",
|
|
tcp_decode_state(tcp->st),
|
|
local_s, tcp->local_port);
|
|
else
|
|
xasprintf(&str, "state=%s laddr=%s:%u raddr=%s:%u",
|
|
tcp_decode_state(tcp->st),
|
|
local_s, tcp->local_port,
|
|
remote_s, tcp->remote_port);
|
|
return str;
|
|
}
|
|
|
|
static char *tcp_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
|
|
struct sock *sock __attribute__((__unused__)))
|
|
{
|
|
return strdup("stream");
|
|
}
|
|
|
|
static char *tcp_get_state(struct sock_xinfo *sock_xinfo,
|
|
struct sock *sock __attribute__((__unused__)))
|
|
{
|
|
struct tcp_xinfo *tcp = (struct tcp_xinfo *)sock_xinfo;
|
|
|
|
return strdup(tcp_decode_state(tcp->st));
|
|
}
|
|
|
|
static bool tcp_get_listening(struct sock_xinfo *sock_xinfo,
|
|
struct sock *sock __attribute__((__unused__)))
|
|
{
|
|
struct tcp_xinfo *tcp = (struct tcp_xinfo *)sock_xinfo;
|
|
return tcp->st == TCP_LISTEN;
|
|
}
|
|
|
|
#define define_fill_column_func(l3,L3) \
|
|
static bool l3##_fill_column(struct proc *proc, \
|
|
struct sock_xinfo *sock_xinfo, \
|
|
struct sock *sock, \
|
|
struct libscols_line *ln, \
|
|
int column_id, \
|
|
size_t column_index, \
|
|
char **str) \
|
|
{ \
|
|
struct tcp_xinfo *tcp = (struct tcp_xinfo *)sock_xinfo; \
|
|
struct inet_xinfo *inet = (struct inet_xinfo *)sock_xinfo; \
|
|
struct in_addr n; \
|
|
bool has_laddr = false; \
|
|
char s[INET_ADDRSTRLEN]; \
|
|
unsigned int p; \
|
|
bool has_lport = false; \
|
|
\
|
|
if (inet_fill_column(proc, (struct inet_xinfo *)sock_xinfo, sock, ln, \
|
|
column_id, column_index, str)) \
|
|
return true; \
|
|
\
|
|
switch(column_id) { \
|
|
case COL_##L3##_LADDR: \
|
|
n.s_addr = inet->local_addr; \
|
|
has_laddr = true; \
|
|
p = (unsigned int)tcp->local_port; \
|
|
/* FALL THROUGH */ \
|
|
case COL_##L3##_RADDR: \
|
|
if (!has_laddr) { \
|
|
n.s_addr = inet->remote_addr; \
|
|
p = (unsigned int)tcp->remote_port; \
|
|
} \
|
|
if (inet_ntop(AF_INET, &n, s, sizeof(s))) \
|
|
xasprintf(str, "%s:%u", s, p); \
|
|
break; \
|
|
case COL_##L3##_LPORT: \
|
|
p = (unsigned int)tcp->local_port; \
|
|
has_lport = true; \
|
|
/* FALL THROUGH */ \
|
|
case COL_##L3##_RPORT: \
|
|
if (!has_lport) \
|
|
p = (unsigned int)tcp->remote_port; \
|
|
xasprintf(str, "%u", p); \
|
|
break; \
|
|
default: \
|
|
return false; \
|
|
} \
|
|
\
|
|
return true; \
|
|
}
|
|
|
|
define_fill_column_func(tcp, TCP)
|
|
static const struct sock_xinfo_class tcp_xinfo_class = {
|
|
.class = "tcp",
|
|
.get_name = tcp_get_name,
|
|
.get_type = tcp_get_type,
|
|
.get_state = tcp_get_state,
|
|
.get_listening = tcp_get_listening,
|
|
.fill_column = tcp_fill_column,
|
|
.free = NULL,
|
|
};
|
|
|
|
static bool L3_verify_initial_line(const char *line)
|
|
{
|
|
/* At least we expect two white spaces. */
|
|
if (strncmp (line, " ", 2) != 0)
|
|
return false;
|
|
line += 2;
|
|
|
|
/* Skip white spaces. */
|
|
while (*line == ' ')
|
|
line++;
|
|
|
|
return (strncmp(line, "sl", 2) == 0);
|
|
}
|
|
|
|
static uint32_t kernel32_to_cpu(enum sysfs_byteorder byteorder, uint32_t v)
|
|
{
|
|
if (byteorder == SYSFS_BYTEORDER_LITTLE)
|
|
return le32_to_cpu(v);
|
|
else
|
|
return be32_to_cpu(v);
|
|
}
|
|
|
|
#define TCP_LINE_LEN 256
|
|
static void load_xinfo_from_proc_inet_L3(ino_t netns_inode, const char *proc_file,
|
|
const struct sock_xinfo_class *class)
|
|
{
|
|
char line[TCP_LINE_LEN];
|
|
FILE *tcp_fp;
|
|
|
|
tcp_fp = fopen(proc_file, "r");
|
|
if (!tcp_fp)
|
|
return;
|
|
|
|
if (fgets(line, sizeof(line), tcp_fp) == NULL)
|
|
goto out;
|
|
if (!L3_verify_initial_line(line))
|
|
/* Unexpected line */
|
|
goto out;
|
|
|
|
enum sysfs_byteorder byteorder = sysfs_get_byteorder(NULL);
|
|
|
|
while (fgets(line, sizeof(line), tcp_fp)) {
|
|
unsigned long local_addr;
|
|
unsigned long local_port;
|
|
unsigned long remote_addr;
|
|
unsigned long remote_port;
|
|
unsigned long st;
|
|
unsigned long long inode;
|
|
struct tcp_xinfo *tcp;
|
|
struct inet_xinfo *inet;
|
|
struct sock_xinfo *sock;
|
|
|
|
if (sscanf(line, "%*d: %lx:%lx %lx:%lx %lx %*x:%*x %*x:%*x %*x %*u %*u %lld",
|
|
&local_addr, &local_port, &remote_addr, &remote_port,
|
|
&st, &inode) != 6)
|
|
continue;
|
|
|
|
if (inode == 0)
|
|
continue;
|
|
|
|
tcp = xcalloc(1, sizeof(struct tcp_xinfo));
|
|
inet = (struct inet_xinfo *)tcp;
|
|
sock = (struct sock_xinfo *)inet;
|
|
sock->class = class;
|
|
sock->inode = (ino_t)inode;
|
|
sock->netns_inode = netns_inode;
|
|
inet->local_addr = kernel32_to_cpu(byteorder, local_addr);
|
|
tcp->local_port = local_port;
|
|
inet->remote_addr = kernel32_to_cpu(byteorder, remote_addr);
|
|
tcp->remote_port = remote_port;
|
|
tcp->st = st;
|
|
|
|
add_sock_info(sock);
|
|
}
|
|
|
|
out:
|
|
fclose(tcp_fp);
|
|
}
|
|
|
|
static void load_xinfo_from_proc_tcp(ino_t netns_inode)
|
|
{
|
|
load_xinfo_from_proc_inet_L3(netns_inode,
|
|
"/proc/net/tcp",
|
|
&tcp_xinfo_class);
|
|
}
|
|
|
|
/*
|
|
* UDP
|
|
*/
|
|
static char *udp_get_name(struct sock_xinfo *sock_xinfo,
|
|
struct sock *sock __attribute__((__unused__)))
|
|
{
|
|
char *str = NULL;
|
|
struct inet_xinfo *inet = ((struct inet_xinfo *)sock_xinfo);
|
|
struct tcp_xinfo *tcp = ((struct tcp_xinfo *)sock_xinfo);
|
|
struct in_addr local_n, remote_n;
|
|
char local_s[INET_ADDRSTRLEN], remote_s[INET_ADDRSTRLEN];
|
|
|
|
local_n.s_addr = inet->local_addr;
|
|
remote_n.s_addr = inet->remote_addr;
|
|
if (!inet_ntop(AF_INET, &local_n, local_s, sizeof(local_s)))
|
|
xasprintf(&str, "state=%s", tcp_decode_state(tcp->st));
|
|
else if ((remote_n.s_addr == 0 && tcp->remote_port == 0)
|
|
|| !inet_ntop(AF_INET, &remote_n, remote_s, sizeof(remote_s)))
|
|
xasprintf(&str, "state=%s laddr=%s:%u",
|
|
tcp_decode_state(tcp->st),
|
|
local_s, tcp->local_port);
|
|
else
|
|
xasprintf(&str, "state=%s laddr=%s:%u raddr=%s:%u",
|
|
tcp_decode_state(tcp->st),
|
|
local_s, tcp->local_port,
|
|
remote_s, tcp->remote_port);
|
|
return str;
|
|
}
|
|
|
|
static char *udp_get_type(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
|
|
struct sock *sock __attribute__((__unused__)))
|
|
{
|
|
return strdup("dgram");
|
|
}
|
|
|
|
define_fill_column_func(udp, UDP)
|
|
static const struct sock_xinfo_class udp_xinfo_class = {
|
|
.class = "udp",
|
|
.get_name = udp_get_name,
|
|
.get_type = udp_get_type,
|
|
.get_state = tcp_get_state,
|
|
.get_listening = NULL,
|
|
.fill_column = udp_fill_column,
|
|
.free = NULL,
|
|
};
|
|
|
|
static void load_xinfo_from_proc_udp(ino_t netns_inode)
|
|
{
|
|
load_xinfo_from_proc_inet_L3(netns_inode,
|
|
"/proc/net/udp",
|
|
&udp_xinfo_class);
|
|
}
|