2012-08-11 13:12:43 +04:00
|
|
|
#include <unistd.h>
|
2012-03-31 02:46:00 +04:00
|
|
|
#include <sys/socket.h>
|
2011-12-26 22:12:03 +04:00
|
|
|
#include <linux/netlink.h>
|
2012-08-02 07:24:29 +04:00
|
|
|
#include <linux/rtnetlink.h>
|
2011-12-26 22:12:03 +04:00
|
|
|
#include <netinet/tcp.h>
|
2012-10-19 17:36:44 +04:00
|
|
|
#include <errno.h>
|
|
|
|
#include <linux/if.h>
|
2012-11-01 19:20:33 +03:00
|
|
|
#include <linux/filter.h>
|
2012-11-28 20:24:42 +03:00
|
|
|
#include <string.h>
|
2011-12-27 14:24:47 +04:00
|
|
|
|
2011-12-26 22:12:03 +04:00
|
|
|
#include "libnetlink.h"
|
|
|
|
#include "sockets.h"
|
|
|
|
#include "unix_diag.h"
|
2012-01-17 21:30:00 +04:00
|
|
|
#include "inet_diag.h"
|
2012-08-15 17:39:21 +04:00
|
|
|
#include "packet_diag.h"
|
2012-03-29 16:40:10 +04:00
|
|
|
#include "files.h"
|
2012-03-29 16:44:15 +04:00
|
|
|
#include "util-net.h"
|
2012-08-09 16:17:41 +04:00
|
|
|
#include "sk-packet.h"
|
2012-08-11 13:12:43 +04:00
|
|
|
#include "namespaces.h"
|
|
|
|
#include "crtools.h"
|
2012-10-19 17:36:44 +04:00
|
|
|
#include "net.h"
|
2011-12-26 22:12:03 +04:00
|
|
|
|
|
|
|
#ifndef NETLINK_SOCK_DIAG
|
|
|
|
#define NETLINK_SOCK_DIAG NETLINK_INET_DIAG
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef SOCK_DIAG_BY_FAMILY
|
|
|
|
#define SOCK_DIAG_BY_FAMILY 20
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef SOCKFS_MAGIC
|
|
|
|
#define SOCKFS_MAGIC 0x534F434B
|
|
|
|
#endif
|
|
|
|
|
2012-01-27 12:53:19 +04:00
|
|
|
#define SK_HASH_SIZE 32
|
2012-01-27 22:52:13 +04:00
|
|
|
|
2012-11-01 19:20:33 +03:00
|
|
|
#ifndef SO_GET_FILTER
|
|
|
|
#define SO_GET_FILTER SO_ATTACH_FILTER
|
|
|
|
#endif
|
|
|
|
|
2012-10-19 17:36:44 +04:00
|
|
|
static int dump_bound_dev(int sk, SkOptsEntry *soe)
|
|
|
|
{
|
2012-11-28 20:24:42 +03:00
|
|
|
int ret;
|
|
|
|
char dev[IFNAMSIZ];
|
2012-10-19 17:36:44 +04:00
|
|
|
socklen_t len = sizeof(dev);
|
|
|
|
|
|
|
|
ret = getsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, &dev, &len);
|
2012-11-28 20:24:42 +03:00
|
|
|
if (ret) {
|
|
|
|
if (errno == ENOPROTOOPT) {
|
|
|
|
pr_warn("Bound device may be missing for socket\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-19 17:36:44 +04:00
|
|
|
pr_perror("Can't get bound dev");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-11-28 20:24:42 +03:00
|
|
|
if (len == 0)
|
2012-10-19 17:36:44 +04:00
|
|
|
return 0;
|
|
|
|
|
2012-11-28 20:24:42 +03:00
|
|
|
pr_debug("\tDumping %s bound dev for sk\n", dev);
|
|
|
|
soe->so_bound_dev = xmalloc(len);
|
|
|
|
strcpy(soe->so_bound_dev, dev);
|
2012-10-19 17:36:44 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int restore_bound_dev(int sk, SkOptsEntry *soe)
|
|
|
|
{
|
2012-11-28 20:24:42 +03:00
|
|
|
char *n = soe->so_bound_dev;
|
2012-10-19 17:36:44 +04:00
|
|
|
|
2012-11-28 20:24:42 +03:00
|
|
|
if (!n)
|
2012-10-19 17:36:44 +04:00
|
|
|
return 0;
|
|
|
|
|
2012-11-28 20:24:42 +03:00
|
|
|
pr_debug("\tBinding socket to %s dev\n", n);
|
|
|
|
return do_restore_opt(sk, SOL_SOCKET, SO_BINDTODEVICE, n, strlen(n));
|
2012-10-19 17:36:44 +04:00
|
|
|
}
|
|
|
|
|
2012-11-01 19:20:33 +03:00
|
|
|
/*
|
|
|
|
* Protobuf handles le/be himself, but the sock_filter is not just u64,
|
|
|
|
* it's a structure and we have to preserve the fields order to be able
|
|
|
|
* to move socket image across architectures.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void encode_filter(struct sock_filter *f, uint64_t *img, int n)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
BUILD_BUG_ON(sizeof(*f) != sizeof(*img));
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
img[i] = ((uint64_t)f[i].code << 48) |
|
|
|
|
((uint64_t)f[i].jt << 40) |
|
|
|
|
((uint64_t)f[i].jf << 32) |
|
|
|
|
((uint64_t)f[i].k << 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void decode_filter(uint64_t *img, struct sock_filter *f, int n)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
f[i].code = img[i] >> 48;
|
|
|
|
f[i].jt = img[i] >> 40;
|
|
|
|
f[i].jf = img[i] >> 32;
|
|
|
|
f[i].k = img[i] >> 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dump_socket_filter(int sk, SkOptsEntry *soe)
|
|
|
|
{
|
|
|
|
socklen_t len = 0;
|
|
|
|
int ret;
|
|
|
|
struct sock_filter *flt;
|
|
|
|
|
|
|
|
ret = getsockopt(sk, SOL_SOCKET, SO_GET_FILTER, NULL, &len);
|
|
|
|
if (ret && errno != ENOPROTOOPT) {
|
|
|
|
pr_perror("Can't get socket filter len");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!len) {
|
|
|
|
pr_info("No filter for socket\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
flt = xmalloc(len * sizeof(*flt));
|
|
|
|
if (!flt)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = getsockopt(sk, SOL_SOCKET, SO_GET_FILTER, flt, &len);
|
|
|
|
if (ret) {
|
|
|
|
pr_perror("Can't get socket filter\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
soe->so_filter = xmalloc(len * sizeof(*soe->so_filter));
|
|
|
|
if (!soe->so_filter)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
encode_filter(flt, soe->so_filter, len);
|
|
|
|
soe->n_so_filter = len;
|
|
|
|
xfree(flt);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int restore_socket_filter(int sk, SkOptsEntry *soe)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct sock_fprog sfp;
|
|
|
|
|
|
|
|
if (!soe->n_so_filter)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pr_info("Restoring socket filter\n");
|
|
|
|
sfp.len = soe->n_so_filter;
|
|
|
|
sfp.filter = xmalloc(soe->n_so_filter * sfp.len);
|
|
|
|
if (!sfp.filter)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
decode_filter(soe->so_filter, sfp.filter, sfp.len);
|
|
|
|
ret = restore_opt(sk, SOL_SOCKET, SO_ATTACH_FILTER, &sfp);
|
|
|
|
xfree(sfp.filter);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-01-27 12:53:19 +04:00
|
|
|
static struct socket_desc *sockets[SK_HASH_SIZE];
|
2012-04-26 14:51:21 +04:00
|
|
|
|
2012-08-15 19:16:12 +04:00
|
|
|
struct socket_desc *lookup_socket(int ino, int family)
|
2012-04-26 14:51:21 +04:00
|
|
|
{
|
|
|
|
struct socket_desc *sd;
|
|
|
|
|
2012-08-15 19:16:12 +04:00
|
|
|
pr_debug("\tSearching for socket %x (family %d)\n", ino, family);
|
2012-04-26 14:51:21 +04:00
|
|
|
for (sd = sockets[ino % SK_HASH_SIZE]; sd; sd = sd->next)
|
2012-08-15 19:16:12 +04:00
|
|
|
if (sd->ino == ino) {
|
|
|
|
BUG_ON(sd->family != family);
|
2012-04-26 14:51:21 +04:00
|
|
|
return sd;
|
2012-08-15 19:16:12 +04:00
|
|
|
}
|
|
|
|
|
2012-04-26 14:51:21 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
2011-12-26 22:12:03 +04:00
|
|
|
|
2012-04-26 14:30:42 +04:00
|
|
|
int sk_collect_one(int ino, int family, struct socket_desc *d)
|
2011-12-26 22:12:03 +04:00
|
|
|
{
|
2012-04-26 14:48:58 +04:00
|
|
|
struct socket_desc **chain;
|
|
|
|
|
2011-12-28 02:02:46 +04:00
|
|
|
d->ino = ino;
|
|
|
|
d->family = family;
|
2012-08-15 17:39:21 +04:00
|
|
|
d->already_dumped = 0;
|
2011-12-28 02:02:46 +04:00
|
|
|
|
2012-04-26 14:48:58 +04:00
|
|
|
chain = &sockets[ino % SK_HASH_SIZE];
|
|
|
|
d->next = *chain;
|
|
|
|
*chain = d;
|
2011-12-28 02:02:46 +04:00
|
|
|
|
2011-12-26 22:12:03 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-09 17:25:11 +04:00
|
|
|
int do_restore_opt(int sk, int level, int name, void *val, int len)
|
2012-05-05 03:11:56 +04:00
|
|
|
{
|
2012-08-09 17:25:11 +04:00
|
|
|
if (setsockopt(sk, level, name, val, len) < 0) {
|
|
|
|
pr_perror("Can't set %d:%d (len %d)", level, name, len);
|
2012-05-05 03:11:56 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-02 13:40:54 +04:00
|
|
|
/*
|
|
|
|
* Set sizes of buffers to maximum and prevent blocking
|
|
|
|
* Caller of this fn should call other socket restoring
|
|
|
|
* routines to drop the non-blocking and set proper send
|
|
|
|
* and receive buffers.
|
|
|
|
*/
|
|
|
|
int restore_prepare_socket(int sk)
|
|
|
|
{
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
/* In kernel a bufsize has type int and a value is doubled. */
|
|
|
|
u32 maxbuf = INT_MAX / 2;
|
|
|
|
|
|
|
|
if (restore_opt(sk, SOL_SOCKET, SO_SNDBUFFORCE, &maxbuf))
|
|
|
|
return -1;
|
|
|
|
if (restore_opt(sk, SOL_SOCKET, SO_RCVBUFFORCE, &maxbuf))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Prevent blocking on restore */
|
|
|
|
flags = fcntl(sk, F_GETFL, 0);
|
|
|
|
if (flags == -1) {
|
|
|
|
pr_perror("Unable to get flags for %d", sk);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (fcntl(sk, F_SETFL, flags | O_NONBLOCK) ) {
|
|
|
|
pr_perror("Unable to set O_NONBLOCK for %d", sk);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-19 09:44:20 +04:00
|
|
|
int restore_socket_opts(int sk, SkOptsEntry *soe)
|
2012-07-19 09:23:50 +04:00
|
|
|
{
|
2012-10-12 14:01:18 +04:00
|
|
|
int ret = 0, val;
|
2012-07-19 09:23:50 +04:00
|
|
|
struct timeval tv;
|
|
|
|
|
2012-09-28 16:42:15 +04:00
|
|
|
pr_info("%d restore sndbuf %d rcv buf %d\n", sk, soe->so_sndbuf, soe->so_rcvbuf);
|
2012-08-09 17:25:11 +04:00
|
|
|
ret |= restore_opt(sk, SOL_SOCKET, SO_SNDBUFFORCE, &soe->so_sndbuf);
|
|
|
|
ret |= restore_opt(sk, SOL_SOCKET, SO_RCVBUFFORCE, &soe->so_rcvbuf);
|
2012-10-11 22:02:25 +04:00
|
|
|
if (soe->has_so_priority) {
|
|
|
|
pr_debug("\trestore priority %d for socket\n", soe->so_priority);
|
|
|
|
ret |= restore_opt(sk, SOL_SOCKET, SO_PRIORITY, &soe->so_priority);
|
|
|
|
}
|
|
|
|
if (soe->has_so_rcvlowat) {
|
|
|
|
pr_debug("\trestore rcvlowat %d for socket\n", soe->so_rcvlowat);
|
|
|
|
ret |= restore_opt(sk, SOL_SOCKET, SO_RCVLOWAT, &soe->so_rcvlowat);
|
|
|
|
}
|
2012-10-12 13:48:18 +04:00
|
|
|
if (soe->has_so_mark) {
|
|
|
|
pr_debug("\trestore mark %d for socket\n", soe->so_mark);
|
|
|
|
ret |= restore_opt(sk, SOL_SOCKET, SO_MARK, &soe->so_mark);
|
|
|
|
}
|
2012-10-12 14:01:18 +04:00
|
|
|
if (soe->has_so_passcred && soe->so_passcred) {
|
|
|
|
val = 1;
|
|
|
|
pr_debug("\tset passcred for socket\n");
|
|
|
|
ret |= restore_opt(sk, SOL_SOCKET, SO_PASSCRED, &val);
|
|
|
|
}
|
|
|
|
if (soe->has_so_passsec && soe->so_passsec) {
|
|
|
|
val = 1;
|
|
|
|
pr_debug("\tset passsec for socket\n");
|
|
|
|
ret |= restore_opt(sk, SOL_SOCKET, SO_PASSSEC, &val);
|
|
|
|
}
|
2012-10-12 20:32:06 +04:00
|
|
|
if (soe->has_so_dontroute && soe->so_dontroute) {
|
|
|
|
val = 1;
|
|
|
|
pr_debug("\tset dontroute for socket\n");
|
|
|
|
ret |= restore_opt(sk, SOL_SOCKET, SO_DONTROUTE, &val);
|
|
|
|
}
|
2012-10-15 15:45:57 +04:00
|
|
|
if (soe->has_so_no_check && soe->so_no_check) {
|
|
|
|
val = 1;
|
|
|
|
pr_debug("\tset no_check for socket\n");
|
|
|
|
ret |= restore_opt(sk, SOL_SOCKET, SO_NO_CHECK, &val);
|
|
|
|
}
|
2012-07-19 09:23:50 +04:00
|
|
|
|
|
|
|
tv.tv_sec = soe->so_snd_tmo_sec;
|
|
|
|
tv.tv_usec = soe->so_snd_tmo_usec;
|
2012-08-09 17:25:11 +04:00
|
|
|
ret |= restore_opt(sk, SOL_SOCKET, SO_SNDTIMEO, &tv);
|
2012-07-19 09:23:50 +04:00
|
|
|
|
|
|
|
tv.tv_sec = soe->so_rcv_tmo_sec;
|
|
|
|
tv.tv_usec = soe->so_rcv_tmo_usec;
|
2012-08-09 17:25:11 +04:00
|
|
|
ret |= restore_opt(sk, SOL_SOCKET, SO_RCVTIMEO, &tv);
|
2012-07-19 09:23:50 +04:00
|
|
|
|
2012-11-01 17:37:53 +03:00
|
|
|
ret |= restore_bound_dev(sk, soe);
|
2012-11-01 19:20:33 +03:00
|
|
|
ret |= restore_socket_filter(sk, soe);
|
2012-10-19 17:36:44 +04:00
|
|
|
|
2012-08-20 17:50:08 +04:00
|
|
|
/* The restore of SO_REUSEADDR depends on type of socket */
|
|
|
|
|
2012-07-19 09:23:50 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-08-09 17:25:11 +04:00
|
|
|
int do_dump_opt(int sk, int level, int name, void *val, int len)
|
2012-05-05 03:11:56 +04:00
|
|
|
{
|
|
|
|
socklen_t aux = len;
|
|
|
|
|
2012-08-09 17:25:11 +04:00
|
|
|
if (getsockopt(sk, level, name, val, &aux) < 0) {
|
|
|
|
pr_perror("Can't get %d:%d opt", level, name);
|
2012-05-05 03:11:56 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aux != len) {
|
2012-08-09 17:25:11 +04:00
|
|
|
pr_err("Len mismatch on %d:%d : %d, want %d\n",
|
|
|
|
level, name, aux, len);
|
2012-05-05 03:11:56 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-05-05 02:11:14 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-19 09:44:20 +04:00
|
|
|
int dump_socket_opts(int sk, SkOptsEntry *soe)
|
2012-07-19 09:23:50 +04:00
|
|
|
{
|
2012-08-20 17:50:08 +04:00
|
|
|
int ret = 0, val;
|
2012-07-19 09:23:50 +04:00
|
|
|
struct timeval tv;
|
|
|
|
|
2012-08-09 17:25:11 +04:00
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_SNDBUF, &soe->so_sndbuf);
|
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_RCVBUF, &soe->so_rcvbuf);
|
2012-10-11 22:02:25 +04:00
|
|
|
soe->has_so_priority = true;
|
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_PRIORITY, &soe->so_priority);
|
|
|
|
soe->has_so_rcvlowat = true;
|
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_RCVLOWAT, &soe->so_rcvlowat);
|
2012-10-12 13:48:18 +04:00
|
|
|
soe->has_so_mark = true;
|
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_MARK, &soe->so_mark);
|
2012-07-19 09:23:50 +04:00
|
|
|
|
2012-08-09 17:25:11 +04:00
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_SNDTIMEO, &tv);
|
2012-07-19 09:23:50 +04:00
|
|
|
soe->so_snd_tmo_sec = tv.tv_sec;
|
|
|
|
soe->so_snd_tmo_usec = tv.tv_usec;
|
|
|
|
|
2012-08-09 17:25:11 +04:00
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_RCVTIMEO, &tv);
|
2012-07-19 09:23:50 +04:00
|
|
|
soe->so_rcv_tmo_sec = tv.tv_sec;
|
|
|
|
soe->so_rcv_tmo_usec = tv.tv_usec;
|
|
|
|
|
2012-08-20 17:50:08 +04:00
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_REUSEADDR, &val);
|
|
|
|
soe->reuseaddr = val ? true : false;
|
|
|
|
soe->has_reuseaddr = true;
|
|
|
|
|
2012-10-12 14:01:18 +04:00
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_PASSCRED, &val);
|
|
|
|
soe->has_so_passcred = true;
|
|
|
|
soe->so_passcred = val ? true : false;
|
|
|
|
|
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_PASSSEC, &val);
|
|
|
|
soe->has_so_passsec = true;
|
|
|
|
soe->so_passsec = val ? true : false;
|
|
|
|
|
2012-10-12 20:32:06 +04:00
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_DONTROUTE, &val);
|
|
|
|
soe->has_so_dontroute = true;
|
|
|
|
soe->so_dontroute = val ? true : false;
|
|
|
|
|
2012-10-15 15:45:57 +04:00
|
|
|
ret |= dump_opt(sk, SOL_SOCKET, SO_NO_CHECK, &val);
|
|
|
|
soe->has_so_no_check = true;
|
|
|
|
soe->so_no_check = val ? true : false;
|
|
|
|
|
2012-11-01 17:37:53 +03:00
|
|
|
ret |= dump_bound_dev(sk, soe);
|
2012-11-01 19:20:33 +03:00
|
|
|
ret |= dump_socket_filter(sk, soe);
|
2012-10-19 17:36:44 +04:00
|
|
|
|
2012-07-19 09:23:50 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-11-01 17:53:57 +03:00
|
|
|
void release_skopts(SkOptsEntry *soe)
|
|
|
|
{
|
2012-11-01 19:20:33 +03:00
|
|
|
xfree(soe->so_filter);
|
2012-11-28 20:24:42 +03:00
|
|
|
xfree(soe->so_bound_dev);
|
2012-11-01 17:53:57 +03:00
|
|
|
}
|
|
|
|
|
2013-01-11 13:22:41 +04:00
|
|
|
int dump_socket(struct fd_parms *p, int lfd, const int fdinfo)
|
2011-12-26 22:12:03 +04:00
|
|
|
{
|
2012-06-03 20:03:34 +04:00
|
|
|
int family;
|
2011-12-26 22:12:03 +04:00
|
|
|
|
2012-08-09 17:25:11 +04:00
|
|
|
if (dump_opt(lfd, SOL_SOCKET, SO_DOMAIN, &family))
|
2011-12-26 22:12:03 +04:00
|
|
|
return -1;
|
|
|
|
|
2012-06-03 20:03:34 +04:00
|
|
|
switch (family) {
|
2011-12-26 22:12:03 +04:00
|
|
|
case AF_UNIX:
|
2013-01-11 13:22:41 +04:00
|
|
|
return dump_one_unix(p, lfd, fdinfo);
|
2012-01-17 21:30:00 +04:00
|
|
|
case AF_INET:
|
2013-01-11 13:22:41 +04:00
|
|
|
return dump_one_inet(p, lfd, fdinfo);
|
2012-08-15 19:16:12 +04:00
|
|
|
case AF_INET6:
|
2013-01-11 13:22:41 +04:00
|
|
|
return dump_one_inet6(p, lfd, fdinfo);
|
2012-08-09 16:17:41 +04:00
|
|
|
case AF_PACKET:
|
2013-01-11 13:22:41 +04:00
|
|
|
return dump_one_packet_sk(p, lfd, fdinfo);
|
2011-12-26 22:12:03 +04:00
|
|
|
default:
|
2013-02-07 00:25:03 +04:00
|
|
|
pr_err("BUG! Unknown socket collected (family %d)\n", family);
|
2012-01-12 23:50:45 +04:00
|
|
|
break;
|
2011-12-26 22:12:03 +04:00
|
|
|
}
|
2012-01-12 23:50:45 +04:00
|
|
|
|
|
|
|
return -1;
|
2011-12-26 22:12:03 +04:00
|
|
|
}
|
|
|
|
|
2012-08-11 22:32:38 +04:00
|
|
|
static int inet_receive_one(struct nlmsghdr *h, void *arg)
|
2012-01-17 21:30:00 +04:00
|
|
|
{
|
2012-08-11 22:32:38 +04:00
|
|
|
struct inet_diag_req_v2 *i = arg;
|
|
|
|
int type;
|
2012-04-21 02:56:00 +04:00
|
|
|
|
2012-08-11 22:32:38 +04:00
|
|
|
switch (i->sdiag_protocol) {
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
type = SOCK_STREAM;
|
|
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
case IPPROTO_UDPLITE:
|
|
|
|
type = SOCK_DGRAM;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG_ON(1);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-04-21 02:56:00 +04:00
|
|
|
|
2012-08-11 22:32:38 +04:00
|
|
|
return inet_collect_one(h, i->sdiag_family, type, i->sdiag_protocol);
|
2012-04-13 10:42:30 +04:00
|
|
|
}
|
|
|
|
|
2012-08-11 13:12:43 +04:00
|
|
|
int collect_sockets(int pid)
|
2011-12-26 22:12:03 +04:00
|
|
|
{
|
2012-01-19 19:16:55 +04:00
|
|
|
int err = 0, tmp;
|
2012-08-11 13:12:43 +04:00
|
|
|
int rst = -1;
|
2011-12-26 22:12:03 +04:00
|
|
|
int nl;
|
2012-01-17 16:28:21 +03:00
|
|
|
struct {
|
|
|
|
struct nlmsghdr hdr;
|
|
|
|
union {
|
2012-01-20 16:07:52 +04:00
|
|
|
struct unix_diag_req u;
|
|
|
|
struct inet_diag_req_v2 i;
|
2012-08-15 17:39:21 +04:00
|
|
|
struct packet_diag_req p;
|
2012-01-17 16:28:21 +03:00
|
|
|
} r;
|
|
|
|
} req;
|
2011-12-26 22:12:03 +04:00
|
|
|
|
2013-01-17 18:14:55 +04:00
|
|
|
if (current_ns_mask & CLONE_NEWNET) {
|
2012-08-11 13:12:43 +04:00
|
|
|
pr_info("Switching to %d's net for collecting sockets\n", pid);
|
|
|
|
|
2013-01-15 23:24:01 +04:00
|
|
|
if (switch_ns(pid, &net_ns_desc, &rst))
|
2012-08-11 13:12:43 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-12-26 22:12:03 +04:00
|
|
|
nl = socket(PF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
|
|
|
|
if (nl < 0) {
|
2012-02-11 03:30:10 +04:00
|
|
|
pr_perror("Can't create sock diag socket");
|
2012-08-11 13:12:43 +04:00
|
|
|
err = -1;
|
|
|
|
goto out;
|
2011-12-26 22:12:03 +04:00
|
|
|
}
|
|
|
|
|
2012-01-17 16:28:21 +03:00
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
req.hdr.nlmsg_len = sizeof(req);
|
|
|
|
req.hdr.nlmsg_type = SOCK_DIAG_BY_FAMILY;
|
|
|
|
req.hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
|
|
|
|
req.hdr.nlmsg_seq = CR_NLMSG_SEQ;
|
2011-12-28 02:02:46 +04:00
|
|
|
|
2012-01-17 16:28:21 +03:00
|
|
|
/* Collect UNIX sockets */
|
|
|
|
req.r.u.sdiag_family = AF_UNIX;
|
|
|
|
req.r.u.udiag_states = -1; /* All */
|
|
|
|
req.r.u.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_VFS |
|
|
|
|
UDIAG_SHOW_PEER | UDIAG_SHOW_ICONS |
|
|
|
|
UDIAG_SHOW_RQLEN;
|
2012-08-02 07:28:45 +04:00
|
|
|
tmp = do_rtnl_req(nl, &req, sizeof(req), unix_receive_one, NULL);
|
2012-01-19 19:16:55 +04:00
|
|
|
if (tmp)
|
|
|
|
err = tmp;
|
2012-01-17 16:28:21 +03:00
|
|
|
|
2012-01-17 21:30:00 +04:00
|
|
|
/* Collect IPv4 TCP sockets */
|
|
|
|
req.r.i.sdiag_family = AF_INET;
|
|
|
|
req.r.i.sdiag_protocol = IPPROTO_TCP;
|
2012-01-19 19:17:54 +04:00
|
|
|
req.r.i.idiag_ext = 0;
|
2012-04-28 17:15:12 +04:00
|
|
|
/* Only listening and established sockets supported yet */
|
|
|
|
req.r.i.idiag_states = (1 << TCP_LISTEN) | (1 << TCP_ESTABLISHED);
|
2012-08-11 22:32:38 +04:00
|
|
|
tmp = do_rtnl_req(nl, &req, sizeof(req), inet_receive_one, &req.r.i);
|
2012-01-19 19:16:55 +04:00
|
|
|
if (tmp)
|
|
|
|
err = tmp;
|
2012-01-17 21:30:00 +04:00
|
|
|
|
2012-03-02 15:47:21 +04:00
|
|
|
/* Collect IPv4 UDP sockets */
|
|
|
|
req.r.i.sdiag_family = AF_INET;
|
|
|
|
req.r.i.sdiag_protocol = IPPROTO_UDP;
|
|
|
|
req.r.i.idiag_ext = 0;
|
|
|
|
req.r.i.idiag_states = -1; /* All */
|
2012-08-11 22:32:38 +04:00
|
|
|
tmp = do_rtnl_req(nl, &req, sizeof(req), inet_receive_one, &req.r.i);
|
2012-03-02 15:47:21 +04:00
|
|
|
if (tmp)
|
|
|
|
err = tmp;
|
|
|
|
|
2012-04-13 10:42:30 +04:00
|
|
|
/* Collect IPv4 UDP-lite sockets */
|
|
|
|
req.r.i.sdiag_family = AF_INET;
|
|
|
|
req.r.i.sdiag_protocol = IPPROTO_UDPLITE;
|
|
|
|
req.r.i.idiag_ext = 0;
|
|
|
|
req.r.i.idiag_states = -1; /* All */
|
2012-08-11 22:32:38 +04:00
|
|
|
tmp = do_rtnl_req(nl, &req, sizeof(req), inet_receive_one, &req.r.i);
|
2012-04-13 10:42:30 +04:00
|
|
|
if (tmp)
|
|
|
|
err = tmp;
|
|
|
|
|
2012-04-21 02:56:00 +04:00
|
|
|
/* Collect IPv6 TCP sockets */
|
|
|
|
req.r.i.sdiag_family = AF_INET6;
|
|
|
|
req.r.i.sdiag_protocol = IPPROTO_TCP;
|
|
|
|
req.r.i.idiag_ext = 0;
|
|
|
|
/* Only listening sockets supported yet */
|
2012-11-23 16:36:37 +04:00
|
|
|
req.r.i.idiag_states = (1 << TCP_LISTEN) | (1 << TCP_ESTABLISHED);
|
2012-08-11 22:32:38 +04:00
|
|
|
tmp = do_rtnl_req(nl, &req, sizeof(req), inet_receive_one, &req.r.i);
|
2012-04-21 02:56:00 +04:00
|
|
|
if (tmp)
|
|
|
|
err = tmp;
|
|
|
|
|
|
|
|
/* Collect IPv6 UDP sockets */
|
|
|
|
req.r.i.sdiag_family = AF_INET6;
|
|
|
|
req.r.i.sdiag_protocol = IPPROTO_UDP;
|
|
|
|
req.r.i.idiag_ext = 0;
|
|
|
|
req.r.i.idiag_states = -1; /* All */
|
2012-08-11 22:32:38 +04:00
|
|
|
tmp = do_rtnl_req(nl, &req, sizeof(req), inet_receive_one, &req.r.i);
|
2012-04-21 02:56:00 +04:00
|
|
|
if (tmp)
|
|
|
|
err = tmp;
|
|
|
|
|
|
|
|
/* Collect IPv6 UDP-lite sockets */
|
|
|
|
req.r.i.sdiag_family = AF_INET6;
|
|
|
|
req.r.i.sdiag_protocol = IPPROTO_UDPLITE;
|
|
|
|
req.r.i.idiag_ext = 0;
|
|
|
|
req.r.i.idiag_states = -1; /* All */
|
2012-08-11 22:32:38 +04:00
|
|
|
tmp = do_rtnl_req(nl, &req, sizeof(req), inet_receive_one, &req.r.i);
|
2012-04-21 02:56:00 +04:00
|
|
|
if (tmp)
|
|
|
|
err = tmp;
|
2012-05-18 15:39:00 +04:00
|
|
|
|
2012-08-15 17:39:21 +04:00
|
|
|
req.r.p.sdiag_family = AF_PACKET;
|
|
|
|
req.r.p.sdiag_protocol = 0;
|
2012-08-16 17:31:08 +04:00
|
|
|
req.r.p.pdiag_show = PACKET_SHOW_INFO | PACKET_SHOW_MCLIST |
|
2012-08-16 18:54:44 +04:00
|
|
|
PACKET_SHOW_FANOUT | PACKET_SHOW_RING_CFG;
|
2012-08-15 17:39:21 +04:00
|
|
|
tmp = do_rtnl_req(nl, &req, sizeof(req), packet_receive_one, NULL);
|
|
|
|
if (tmp)
|
|
|
|
err = tmp;
|
|
|
|
|
2012-01-17 16:28:21 +03:00
|
|
|
close(nl);
|
2012-08-11 13:12:43 +04:00
|
|
|
out:
|
2013-01-25 15:20:38 +04:00
|
|
|
if (rst >= 0) {
|
|
|
|
if (restore_ns(rst, &net_ns_desc) < 0)
|
|
|
|
err = -1;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If netns isn't dumped, crtools will fail only
|
|
|
|
* if an unsupported socket will be really dumped.
|
|
|
|
*/
|
|
|
|
pr_info("Uncollected sockets! Will probably fail later.\n");
|
|
|
|
err = 0;
|
|
|
|
}
|
|
|
|
|
2011-12-26 22:12:03 +04:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2012-03-27 13:11:23 +04:00
|
|
|
static inline char *unknown(u32 val)
|
|
|
|
{
|
|
|
|
static char unk[12];
|
|
|
|
snprintf(unk, sizeof(unk), "x%d", val);
|
|
|
|
return unk;
|
|
|
|
}
|
|
|
|
|
2012-04-26 14:30:42 +04:00
|
|
|
char *skfamily2s(u32 f)
|
2012-03-27 13:11:23 +04:00
|
|
|
{
|
|
|
|
if (f == AF_INET)
|
|
|
|
return " inet";
|
2012-04-21 02:56:00 +04:00
|
|
|
else if (f == AF_INET6)
|
|
|
|
return "inet6";
|
2012-03-27 13:11:23 +04:00
|
|
|
else
|
|
|
|
return unknown(f);
|
|
|
|
}
|
|
|
|
|
2012-04-26 14:30:42 +04:00
|
|
|
char *sktype2s(u32 t)
|
2012-03-27 13:11:23 +04:00
|
|
|
{
|
|
|
|
if (t == SOCK_STREAM)
|
|
|
|
return "stream";
|
|
|
|
else if (t == SOCK_DGRAM)
|
|
|
|
return " dgram";
|
|
|
|
else
|
|
|
|
return unknown(t);
|
|
|
|
}
|
|
|
|
|
2012-04-26 14:30:42 +04:00
|
|
|
char *skproto2s(u32 p)
|
2012-03-27 13:11:23 +04:00
|
|
|
{
|
|
|
|
if (p == IPPROTO_UDP)
|
|
|
|
return "udp";
|
2012-04-13 10:42:30 +04:00
|
|
|
else if (p == IPPROTO_UDPLITE)
|
|
|
|
return "udpl";
|
2012-03-27 13:11:23 +04:00
|
|
|
else if (p == IPPROTO_TCP)
|
|
|
|
return "tcp";
|
|
|
|
else
|
|
|
|
return unknown(p);
|
|
|
|
}
|
|
|
|
|
2012-04-26 14:30:42 +04:00
|
|
|
char *skstate2s(u32 state)
|
2012-03-27 13:11:23 +04:00
|
|
|
{
|
|
|
|
if (state == TCP_ESTABLISHED)
|
|
|
|
return " estab";
|
|
|
|
else if (state == TCP_CLOSE)
|
|
|
|
return "closed";
|
|
|
|
else if (state == TCP_LISTEN)
|
|
|
|
return "listen";
|
|
|
|
else
|
|
|
|
return unknown(state);
|
|
|
|
}
|