Commit d3397e8f authored by Pavel Emelyanov's avatar Pavel Emelyanov

sk: Support socket filters

One thing to note. The socket filter proggie is a set of struct-s
wuth 8 and 16 bits values in it. Protobuf doesn't support such thing
and it's too annoying to mess with yet another message for that.
Instead, I encode all this stuff into array of fixed64 fields to
handle endianity (yes, protobuf handles it, but each field is not
just 64-bit value, but a structure).
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent a6a476ff
...@@ -17,6 +17,8 @@ message sk_opts_entry { ...@@ -17,6 +17,8 @@ message sk_opts_entry {
optional bool so_no_check = 14; optional bool so_no_check = 14;
optional uint32 so_bound_dev = 15; optional uint32 so_bound_dev = 15;
repeated fixed64 so_filter = 16;
} }
enum sk_shutdown { enum sk_shutdown {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <errno.h> #include <errno.h>
#include <linux/if.h> #include <linux/if.h>
#include <linux/filter.h>
#include "libnetlink.h" #include "libnetlink.h"
#include "sockets.h" #include "sockets.h"
...@@ -32,6 +33,10 @@ ...@@ -32,6 +33,10 @@
#define SK_HASH_SIZE 32 #define SK_HASH_SIZE 32
#ifndef SO_GET_FILTER
#define SO_GET_FILTER SO_ATTACH_FILTER
#endif
static int dump_bound_dev(int sk, SkOptsEntry *soe) static int dump_bound_dev(int sk, SkOptsEntry *soe)
{ {
int dev = 0, ret; int dev = 0, ret;
...@@ -69,6 +74,95 @@ static int restore_bound_dev(int sk, SkOptsEntry *soe) ...@@ -69,6 +74,95 @@ static int restore_bound_dev(int sk, SkOptsEntry *soe)
return do_restore_opt(sk, SOL_SOCKET, SO_BINDTODEVICE, n, IFNAMSIZ); return do_restore_opt(sk, SOL_SOCKET, SO_BINDTODEVICE, n, IFNAMSIZ);
} }
/*
* 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;
}
static struct socket_desc *sockets[SK_HASH_SIZE]; static struct socket_desc *sockets[SK_HASH_SIZE];
struct socket_desc *lookup_socket(int ino, int family) struct socket_desc *lookup_socket(int ino, int family)
...@@ -160,6 +254,7 @@ int restore_socket_opts(int sk, SkOptsEntry *soe) ...@@ -160,6 +254,7 @@ int restore_socket_opts(int sk, SkOptsEntry *soe)
ret |= restore_opt(sk, SOL_SOCKET, SO_RCVTIMEO, &tv); ret |= restore_opt(sk, SOL_SOCKET, SO_RCVTIMEO, &tv);
ret |= restore_bound_dev(sk, soe); ret |= restore_bound_dev(sk, soe);
ret |= restore_socket_filter(sk, soe);
/* The restore of SO_REUSEADDR depends on type of socket */ /* The restore of SO_REUSEADDR depends on type of socket */
...@@ -227,12 +322,14 @@ int dump_socket_opts(int sk, SkOptsEntry *soe) ...@@ -227,12 +322,14 @@ int dump_socket_opts(int sk, SkOptsEntry *soe)
soe->so_no_check = val ? true : false; soe->so_no_check = val ? true : false;
ret |= dump_bound_dev(sk, soe); ret |= dump_bound_dev(sk, soe);
ret |= dump_socket_filter(sk, soe);
return ret; return ret;
} }
void release_skopts(SkOptsEntry *soe) void release_skopts(SkOptsEntry *soe)
{ {
xfree(soe->so_filter);
} }
int dump_socket(struct fd_parms *p, int lfd, const struct cr_fdset *cr_fdset) int dump_socket(struct fd_parms *p, int lfd, const struct cr_fdset *cr_fdset)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment