Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sk-inet: Add support for checkpoint/restore of ICMP sockets #2558

Open
wants to merge 8 commits into
base: criu-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions criu/sk-inet.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "rst-malloc.h"
#include "sockets.h"
#include "sk-inet.h"
#include "sysctl.h"
#include "protobuf.h"
#include "util.h"
#include "namespaces.h"
Expand Down Expand Up @@ -125,6 +126,8 @@ static int can_dump_ipproto(unsigned int ino, int proto, int type)
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
break;
default:
pr_err("Unsupported proto %d for socket %x\n", proto, ino);
Expand Down Expand Up @@ -575,6 +578,26 @@ static int do_dump_one_inet_fd(int lfd, u32 id, const struct fd_parms *p, int fa
if (dump_socket_opts(lfd, &skopts))
goto err;

if (type == SOCK_DGRAM && (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6)) {
char buffer[16];

struct sysctl_req req[] = {
{ "net/ipv4/ping_group_range", &buffer, CTL_STR(16) },
};

ret = sysctl_op(req, ARRAY_SIZE(req), CTL_READ, CLONE_NEWNET);
if (ret < 0) {
pr_perror("Failed to read ping group range");
goto err;
}

buffer[strlen(buffer)] = '\0';

ie.ping_grp_range = xstrdup(buffer);
if (!ie.ping_grp_range)
goto err;
}

pr_info("Dumping inet socket at %d\n", p->fd);
show_one_inet("Dumping", sk);
show_one_inet_img("Dumped", &ie);
Expand Down Expand Up @@ -625,6 +648,7 @@ static int do_dump_one_inet_fd(int lfd, u32 id, const struct fd_parms *p, int fa
xfree(ie.src_addr);
xfree(ie.dst_addr);
xfree(ie.ifname);
xfree(ie.ping_grp_range);
return err;
}

Expand Down Expand Up @@ -875,6 +899,20 @@ static int open_inet_sk(struct file_desc *d, int *new_fd)
if (run_setsockcreatecon(fle->fe))
return -1;

if (ie->type == SOCK_DGRAM && (ie->proto == IPPROTO_ICMP || ie->proto == IPPROTO_ICMPV6)) {
if (strlen(ie->ping_grp_range) > 1) {
int ret;
struct sysctl_req req[] = {
{ "net/ipv4/ping_group_range", ie->ping_grp_range, CTL_STR(strlen(ie->ping_grp_range)) },
};
ret = sysctl_op(req, ARRAY_SIZE(req), CTL_WRITE, CLONE_NEWNET);
if (ret < 0) {
pr_perror("Failed to set ping_group_range");
return -1;
}
}
}

sk = socket(ie->family, ie->type, ie->proto);
if (sk < 0) {
pr_perror("Can't create inet socket");
Expand Down Expand Up @@ -915,8 +953,9 @@ static int open_inet_sk(struct file_desc *d, int *new_fd)
}

if (ie->src_port) {
if (inet_bind(sk, ii))
goto err;
if (ie->proto != IPPROTO_ICMP && ie->proto != IPPROTO_ICMPV6)
if (inet_bind(sk, ii))
goto err;
}

/*
Expand Down
34 changes: 32 additions & 2 deletions criu/sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const char *socket_proto_name(unsigned int proto, char *nm, size_t size)
[IPPROTO_IPV6] = __stringify_1(IPPROTO_IPV6), [IPPROTO_RSVP] = __stringify_1(IPPROTO_RSVP),
[IPPROTO_GRE] = __stringify_1(IPPROTO_GRE), [IPPROTO_ESP] = __stringify_1(IPPROTO_ESP),
[IPPROTO_AH] = __stringify_1(IPPROTO_AH), [IPPROTO_UDPLITE] = __stringify_1(IPPROTO_UDPLITE),
[IPPROTO_RAW] = __stringify_1(IPPROTO_RAW),
[IPPROTO_RAW] = __stringify_1(IPPROTO_RAW), [IPPROTO_ICMPV6] = __stringify_1(IPPROTO_ICMPV6),
};
return __socket_const_name(nm, size, protos, ARRAY_SIZE(protos), proto);
}
Expand Down Expand Up @@ -131,10 +131,12 @@ enum socket_cl_bits {
INET_UDP_CL_BIT,
INET_UDPLITE_CL_BIT,
INET_RAW_CL_BIT,
INET_ICMP_CL_BIT,
INET6_TCP_CL_BIT,
INET6_UDP_CL_BIT,
INET6_UDPLITE_CL_BIT,
INET6_RAW_CL_BIT,
INET6_ICMP_CL_BIT,
UNIX_CL_BIT,
PACKET_CL_BIT,
_MAX_CL_BIT,
Expand All @@ -161,6 +163,8 @@ static inline enum socket_cl_bits get_collect_bit_nr(unsigned int family, unsign
return INET_UDPLITE_CL_BIT;
if (proto == IPPROTO_RAW)
return INET_RAW_CL_BIT;
if (proto == IPPROTO_ICMP)
return INET_ICMP_CL_BIT;
}
if (family == AF_INET6) {
if (proto == IPPROTO_TCP)
Expand All @@ -171,6 +175,8 @@ static inline enum socket_cl_bits get_collect_bit_nr(unsigned int family, unsign
return INET6_UDPLITE_CL_BIT;
if (proto == IPPROTO_RAW)
return INET6_RAW_CL_BIT;
if (proto == IPPROTO_ICMPV6)
return INET6_ICMP_CL_BIT;
}

pr_err("Unknown pair family %d proto %d\n", family, proto);
Expand Down Expand Up @@ -282,6 +288,12 @@ void preload_socket_modules(void)
req.r.i.sdiag_protocol = IPPROTO_RAW;
probe_diag(nl, &req, -ENOENT);

req.r.i.sdiag_protocol = IPPROTO_ICMP;
probe_diag(nl, &req, -ENOENT);

req.r.i.sdiag_protocol = IPPROTO_ICMPV6;
probe_diag(nl, &req, -ENOENT);

close(nl);
pr_info("Done probing\n");
}
Expand Down Expand Up @@ -773,6 +785,10 @@ static int inet_receive_one(struct nlmsghdr *h, struct ns_id *ns, void *arg)
case IPPROTO_RAW:
type = SOCK_RAW;
break;
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
type = SOCK_DGRAM;
break;
default:
BUG_ON(1);
return -1;
Expand All @@ -797,7 +813,7 @@ static int collect_err(int err, struct ns_id *ns, void *arg)
char family[32], proto[32];
char msg[256];

snprintf(msg, sizeof(msg), "Sockects collect procedure family %s proto %s",
snprintf(msg, sizeof(msg), "Sockets collect procedure family %s proto %s",
socket_family_name(gr->family, family, sizeof(family)),
socket_proto_name(gr->protocol, proto, sizeof(proto)));

Expand Down Expand Up @@ -905,6 +921,13 @@ int collect_sockets(struct ns_id *ns)
if (tmp)
err = tmp;

/* Collect IPv4 ICMP sockets */
req.r.i.sdiag_family = AF_INET;
req.r.i.sdiag_protocol = IPPROTO_ICMP;
req.r.i.idiag_ext = 0;
req.r.i.idiag_states = -1; /* All */
set_collect_bit(req.r.n.sdiag_family, req.r.n.sdiag_protocol);
rst0git marked this conversation as resolved.
Show resolved Hide resolved

/* Collect IPv6 TCP sockets */
req.r.i.sdiag_family = AF_INET6;
req.r.i.sdiag_protocol = IPPROTO_TCP;
Expand Down Expand Up @@ -944,6 +967,13 @@ int collect_sockets(struct ns_id *ns)
if (tmp)
err = tmp;

/* Collect IPv6 ICMP sockets */
req.r.i.sdiag_family = AF_INET6;
req.r.i.sdiag_protocol = IPPROTO_ICMPV6;
req.r.i.idiag_ext = 0;
req.r.i.idiag_states = -1; /* All */
set_collect_bit(req.r.n.sdiag_family, req.r.n.sdiag_protocol);

req.r.p.sdiag_family = AF_PACKET;
req.r.p.sdiag_protocol = 0;
req.r.p.pdiag_show = PACKET_SHOW_INFO | PACKET_SHOW_MCLIST | PACKET_SHOW_FANOUT | PACKET_SHOW_RING_CFG;
Expand Down
1 change: 1 addition & 0 deletions images/sk-inet.proto
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@ message inet_sk_entry {
optional uint32 ns_id = 18;
optional sk_shutdown shutdown = 19;
optional tcp_opts_entry tcp_opts = 20;
optional string ping_grp_range = 21;
}
16 changes: 16 additions & 0 deletions test/zdtm/lib/ns.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <sys/prctl.h>

#include "zdtmtst.h"
#include "sysctl.h"
#include "ns.h"

int criu_status_in = -1, criu_status_in_peer = -1, criu_status_out = -1;
Expand Down Expand Up @@ -157,6 +158,17 @@ static int prepare_mntns(void)
return 0;
}

static int set_ping_group_range(void)
{
// Allow GIDs 0-58468 to open an unprivileged ICMP socket
if (sysctl_write_str("/proc/sys/net/ipv4/ping_group_range", "0 58468")) {
fprintf(stderr, "sysctl_write_str() failed: %m\n");
return -1;
}

return 0;
}

static int prepare_namespaces(void)
{
if (setuid(0) || setgid(0) || setgroups(0, NULL)) {
Expand Down Expand Up @@ -332,6 +344,10 @@ int ns_init(int argc, char **argv)
if (create_timens())
exit(1);

if (set_ping_group_range() < 0) {
exit(1);
}

if (init_notify()) {
fprintf(stderr, "Can't init pre-dump notification: %m");
exit(1);
Expand Down
27 changes: 27 additions & 0 deletions test/zdtm/lib/sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,30 @@ int sysctl_write_int(const char *name, int val)
close(fd);
return ret;
}

int sysctl_write_str(const char *name, const char *val)
{
int fd;
int ret;
char buf[16];

fd = open(name, O_WRONLY);
if (fd < 0) {
pr_perror("Can't open %s", name);
return fd;
}

sprintf(buf, "%s\n", val);

ret = write(fd, buf, strlen(buf));
if (ret < 0) {
pr_perror("Can't write %s into %s", val, name);
ret = -errno;
goto err;
}

ret = 0;
err:
close(fd);
return ret;
}
1 change: 1 addition & 0 deletions test/zdtm/lib/sysctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@

extern int sysctl_read_int(const char *name, int *data);
extern int sysctl_write_int(const char *name, int val);
extern int sysctl_write_str(const char *name, const char *val);

#endif
2 changes: 2 additions & 0 deletions test/zdtm/static/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ TST_NOFILE := \
socket_udp-corked \
socket6_udp \
socket_udp_shutdown \
socket_icmp \
socket6_icmp \
sk-freebind \
sk-freebind-false \
socket_udplite \
Expand Down
95 changes: 95 additions & 0 deletions test/zdtm/static/socket6_icmp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include "zdtmtst.h"

const char *test_doc = "static test for IP6/ICMP socket\n";
const char *test_author = "समीर सिंह Sameer Singh <[email protected]>\n";

/* Description:
* Send a ping to localhost using IP6/ICMP socket
*/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <netdb.h>

#define PACKET_SIZE 64
#define RECV_TIMEOUT 1

static int echo_id = 1234;

int main(int argc, char **argv)
{
int ret, sock, seq = 0, recv_len = 0;
char packet[PACKET_SIZE], recv_packet[PACKET_SIZE];

struct timeval tv;
struct icmp6_hdr icmp_header, *icmp_reply;
struct sockaddr_in6 addr, recv_addr;
socklen_t addr_len;

test_init(argc, argv);

sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
if (sock < 0) {
pr_perror("Can't create socket");
return 1;
}

tv.tv_sec = RECV_TIMEOUT;
tv.tv_usec = 0;
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
pr_perror("Can't set socket option");
return 1;
}

memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
inet_pton(AF_INET6, "::1", &addr.sin6_addr);

memset(&icmp_header, 0, sizeof(icmp_header));
icmp_header.icmp6_type = ICMP6_ECHO_REQUEST;
icmp_header.icmp6_code = 0;
icmp_header.icmp6_id = echo_id;
icmp_header.icmp6_seq = seq;

memcpy(packet, &icmp_header, sizeof(icmp_header));
memset(packet + sizeof(icmp_header), 0xa5,
PACKET_SIZE - sizeof(icmp_header));

test_daemon();
test_waitsig();

ret = sendto(sock, packet, PACKET_SIZE, 0,
(struct sockaddr *)&addr, sizeof(addr));

if (ret < 0) {
pr_perror("Can't send");
return 1;
}

addr_len = sizeof(recv_addr);

recv_len = recvfrom(sock, recv_packet, sizeof(recv_packet), 0,
(struct sockaddr *)&recv_addr, &addr_len);

if (recv_len < 0) {
pr_perror("Can't recv");
return 1;
}

icmp_reply = (struct icmp6_hdr *)recv_packet;

if (icmp_reply->icmp6_type != ICMP6_ECHO_REPLY) {
fail("Got no ICMP_ECHO_REPLY");
return 1;
}

close(sock);
pass();
return 0;
}
1 change: 1 addition & 0 deletions test/zdtm/static/socket6_icmp.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{'flavor': 'h ns'}
Loading