ss: allow to retrieve AF_PACKET info via netlink
This patch add support of netlink messages for AF_PACKET and thus it allows to get filter information of this kind of sockets. To dump these filters info the option --bfp must be specified and the user must have admin rights. Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
This commit is contained in:
parent
f7431e2913
commit
372c30d2aa
190
misc/ss.c
190
misc/ss.c
|
|
@ -36,6 +36,9 @@
|
|||
#include <linux/sock_diag.h>
|
||||
#include <linux/inet_diag.h>
|
||||
#include <linux/unix_diag.h>
|
||||
#include <linux/netdevice.h> /* for MAX_ADDR_LEN */
|
||||
#include <linux/filter.h>
|
||||
#include <linux/packet_diag.h>
|
||||
|
||||
int resolve_hosts = 0;
|
||||
int resolve_services = 1;
|
||||
|
|
@ -45,6 +48,7 @@ int show_details = 0;
|
|||
int show_users = 0;
|
||||
int show_mem = 0;
|
||||
int show_tcpinfo = 0;
|
||||
int show_bpf = 0;
|
||||
|
||||
int netid_width;
|
||||
int state_width;
|
||||
|
|
@ -2389,6 +2393,181 @@ static int unix_show(struct filter *f)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int packet_show_sock(struct nlmsghdr *nlh, struct filter *f)
|
||||
{
|
||||
struct packet_diag_msg *r = NLMSG_DATA(nlh);
|
||||
struct rtattr *tb[PACKET_DIAG_MAX+1];
|
||||
__u32 rq;
|
||||
|
||||
parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr*)(r+1),
|
||||
nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
|
||||
|
||||
/* use /proc/net/packet if all info are not available */
|
||||
if (!tb[PACKET_DIAG_MEMINFO])
|
||||
return -1;
|
||||
|
||||
if (netid_width)
|
||||
printf("%-*s ", netid_width,
|
||||
r->pdiag_type == SOCK_RAW ? "p_raw" : "p_dgr");
|
||||
if (state_width)
|
||||
printf("%-*s ", state_width, "UNCONN");
|
||||
|
||||
if (tb[PACKET_DIAG_MEMINFO]) {
|
||||
__u32 *skmeminfo = RTA_DATA(tb[PACKET_DIAG_MEMINFO]);
|
||||
|
||||
rq = skmeminfo[SK_MEMINFO_RMEM_ALLOC];
|
||||
} else
|
||||
rq = 0;
|
||||
printf("%-6d %-6d ", rq, 0);
|
||||
|
||||
if (r->pdiag_num == 3) {
|
||||
printf("%*s:", addr_width, "*");
|
||||
} else {
|
||||
char tb2[16];
|
||||
printf("%*s:", addr_width,
|
||||
ll_proto_n2a(htons(r->pdiag_num), tb2, sizeof(tb2)));
|
||||
}
|
||||
if (tb[PACKET_DIAG_INFO]) {
|
||||
struct packet_diag_info *pinfo = RTA_DATA(tb[PACKET_DIAG_INFO]);
|
||||
|
||||
if (pinfo->pdi_index == 0)
|
||||
printf("%-*s ", serv_width, "*");
|
||||
else
|
||||
printf("%-*s ", serv_width, xll_index_to_name(pinfo->pdi_index));
|
||||
} else
|
||||
printf("%-*s ", serv_width, "*");
|
||||
|
||||
printf("%*s*%-*s",
|
||||
addr_width, "", serv_width, "");
|
||||
|
||||
if (show_users) {
|
||||
char ubuf[4096];
|
||||
if (find_users(r->pdiag_ino, ubuf, sizeof(ubuf)) > 0)
|
||||
printf(" users:(%s)", ubuf);
|
||||
}
|
||||
if (show_details) {
|
||||
__u32 uid = 0;
|
||||
|
||||
if (tb[PACKET_DIAG_UID])
|
||||
uid = *(__u32 *)RTA_DATA(tb[PACKET_DIAG_UID]);
|
||||
|
||||
printf(" ino=%u uid=%u sk=", r->pdiag_ino, uid);
|
||||
if (r->pdiag_cookie[1] != 0)
|
||||
printf("%08x", r->pdiag_cookie[1]);
|
||||
printf("%08x", r->pdiag_cookie[0]);
|
||||
}
|
||||
|
||||
if (show_bpf && tb[PACKET_DIAG_FILTER]) {
|
||||
struct sock_filter *fil =
|
||||
RTA_DATA(tb[PACKET_DIAG_FILTER]);
|
||||
int num = RTA_PAYLOAD(tb[PACKET_DIAG_FILTER]) /
|
||||
sizeof(struct sock_filter);
|
||||
|
||||
printf("\n\tbpf filter (%d): ", num);
|
||||
while (num) {
|
||||
printf(" 0x%02x %u %u %u,",
|
||||
fil->code, fil->jt, fil->jf, fil->k);
|
||||
num--;
|
||||
fil++;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int packet_show_netlink(struct filter *f, FILE *dump_fp)
|
||||
{
|
||||
int fd;
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
struct packet_diag_req r;
|
||||
} req;
|
||||
char buf[8192];
|
||||
|
||||
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0)
|
||||
return -1;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.nlh.nlmsg_len = sizeof(req);
|
||||
req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
|
||||
req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
|
||||
req.nlh.nlmsg_seq = 123456;
|
||||
|
||||
req.r.sdiag_family = AF_PACKET;
|
||||
req.r.pdiag_show = PACKET_SHOW_INFO | PACKET_SHOW_MEMINFO | PACKET_SHOW_FILTER;
|
||||
|
||||
if (send(fd, &req, sizeof(req), 0) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
ssize_t status;
|
||||
struct nlmsghdr *h;
|
||||
struct sockaddr_nl nladdr;
|
||||
socklen_t slen = sizeof(nladdr);
|
||||
|
||||
status = recvfrom(fd, buf, sizeof(buf), 0,
|
||||
(struct sockaddr *) &nladdr, &slen);
|
||||
if (status < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
perror("OVERRUN");
|
||||
continue;
|
||||
}
|
||||
if (status == 0) {
|
||||
fprintf(stderr, "EOF on netlink\n");
|
||||
goto close_it;
|
||||
}
|
||||
|
||||
if (dump_fp)
|
||||
fwrite(buf, 1, NLMSG_ALIGN(status), dump_fp);
|
||||
|
||||
h = (struct nlmsghdr*)buf;
|
||||
while (NLMSG_OK(h, status)) {
|
||||
int err;
|
||||
|
||||
if (h->nlmsg_seq != 123456)
|
||||
goto skip_it;
|
||||
|
||||
if (h->nlmsg_type == NLMSG_DONE)
|
||||
goto close_it;
|
||||
|
||||
if (h->nlmsg_type == NLMSG_ERROR) {
|
||||
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
|
||||
if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
|
||||
fprintf(stderr, "ERROR truncated\n");
|
||||
} else {
|
||||
errno = -err->error;
|
||||
if (errno != ENOENT)
|
||||
fprintf(stderr, "UDIAG answers %d\n", errno);
|
||||
}
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (!dump_fp) {
|
||||
err = packet_show_sock(h, f);
|
||||
if (err < 0) {
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
skip_it:
|
||||
h = NLMSG_NEXT(h, status);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
fprintf(stderr, "!!!Remnant of size %zd\n", status);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
close_it:
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int packet_show(struct filter *f)
|
||||
{
|
||||
|
|
@ -2406,6 +2585,9 @@ static int packet_show(struct filter *f)
|
|||
if (!(f->states & (1<<SS_CLOSE)))
|
||||
return 0;
|
||||
|
||||
if (packet_show_netlink(f, NULL) == 0)
|
||||
return 0;
|
||||
|
||||
if ((fp = net_packet_open()) == NULL)
|
||||
return -1;
|
||||
fgets(buf, sizeof(buf)-1, fp);
|
||||
|
|
@ -2730,6 +2912,7 @@ static void _usage(FILE *dest)
|
|||
" -p, --processes show process using socket\n"
|
||||
" -i, --info show internal TCP information\n"
|
||||
" -s, --summary show socket usage summary\n"
|
||||
" -b, --bfp show bpf filter socket information\n"
|
||||
"\n"
|
||||
" -4, --ipv4 display only IP version 4 sockets\n"
|
||||
" -6, --ipv6 display only IP version 6 sockets\n"
|
||||
|
|
@ -2800,6 +2983,7 @@ static const struct option long_opts[] = {
|
|||
{ "memory", 0, 0, 'm' },
|
||||
{ "info", 0, 0, 'i' },
|
||||
{ "processes", 0, 0, 'p' },
|
||||
{ "bpf", 0, 0, 'b' },
|
||||
{ "dccp", 0, 0, 'd' },
|
||||
{ "tcp", 0, 0, 't' },
|
||||
{ "udp", 0, 0, 'u' },
|
||||
|
|
@ -2836,7 +3020,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
current_filter.states = default_filter.states;
|
||||
|
||||
while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spf:miA:D:F:vV",
|
||||
while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbf:miA:D:F:vV",
|
||||
long_opts, NULL)) != EOF) {
|
||||
switch(ch) {
|
||||
case 'n':
|
||||
|
|
@ -2862,6 +3046,10 @@ int main(int argc, char *argv[])
|
|||
show_users++;
|
||||
user_ent_hash_build();
|
||||
break;
|
||||
case 'b':
|
||||
show_options = 1;
|
||||
show_bpf++;
|
||||
break;
|
||||
case 'd':
|
||||
current_filter.dbs |= (1<<DCCP_DB);
|
||||
do_default = 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue