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 3 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
7 changes: 5 additions & 2 deletions criu/sk-inet.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,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 @@ -915,8 +917,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 @@ -58,14 +58,14 @@
const char *socket_proto_name(unsigned int proto, char *nm, size_t size)
{
static const char *protos[] = {
[IPPROTO_IP] = __stringify_1(IPPROTO_IP), [IPPROTO_ICMP] = __stringify_1(IPPROTO_ICMP),

Check warning on line 61 in criu/sockets.c

View workflow job for this annotation

GitHub Actions / build

[IPPROTO_IGMP] = __stringify_1(IPPROTO_IGMP), [IPPROTO_IPIP] = __stringify_1(IPPROTO_IPIP),
[IPPROTO_TCP] = __stringify_1(IPPROTO_TCP), [IPPROTO_EGP] = __stringify_1(IPPROTO_EGP),
[IPPROTO_UDP] = __stringify_1(IPPROTO_UDP), [IPPROTO_DCCP] = __stringify_1(IPPROTO_DCCP),
[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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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
2 changes: 2 additions & 0 deletions test/zdtm/static/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ TST_NOFILE := \
socket_udp-corked \
socket6_udp \
socket_udp_shutdown \
socket_icmp \
socket6_icmp \
sk-freebind \
sk-freebind-false \
socket_udplite \
Expand Down
101 changes: 101 additions & 0 deletions test/zdtm/static/socket6_icmp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "zdtmtst.h"
#include "sysctl.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 <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);

// Allow GIDs 0-58468 to open an unprivileged ICMP socket
if (sysctl_write_str("/proc/sys/net/ipv4/ping_group_range", "0 58468")) {
pr_perror("sysctl_write_str() failed");
return 1;
}

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 @@
{'flags': 'suid', 'flavor': 'h ns'}
102 changes: 102 additions & 0 deletions test/zdtm/static/socket_icmp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include "zdtmtst.h"
#include "sysctl.h"

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

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

#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.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;
char packet[PACKET_SIZE], recv_packet[PACKET_SIZE];

struct timeval tv;
struct icmphdr icmp_header, *icmp_reply;
struct sockaddr_in addr, recv_addr;
socklen_t addr_len;

test_init(argc, argv);

// Allow GIDs 0-58468 to open an unprivileged ICMP socket
if (sysctl_write_str("/proc/sys/net/ipv4/ping_group_range", "0 58468")) {
pr_perror("sysctl_write_str() failed");
return 1;
}

sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP);
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.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");

memset(&icmp_header, 0, sizeof(icmp_header));
icmp_header.type = ICMP_ECHO;
icmp_header.code = 0;
icmp_header.un.echo.id = echo_id;
icmp_header.un.echo.sequence = 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) {
fail("Can't send");
return 1;
}

addr_len = sizeof(recv_addr);

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

if (ret < 0) {
fail("Can't recv");
return 1;
}

icmp_reply = (struct icmphdr *)recv_packet;

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

close(sock);

pass();
return 0;
}
1 change: 1 addition & 0 deletions test/zdtm/static/socket_icmp.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{'flags': 'suid', 'flavor': 'h ns'}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is it suid? Why uns is excluded?

Copy link
Member

@Snorch Snorch Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. If we remove suid we get:
04:09:54.090:     4: ERR: sysctl.c:34: Can't open /proc/sys/net/ipv4/ping_group_range (errno = 13 (Permission denied))

Which is EACCES as file have permissions "0644", and non-root is not allowed to write it. But we want to avoid suid to check non-root users too. So we can probably just remove setting ping_group_range from test and rely on it being set in second patch for all tests.

So please remove suid together with setting ping_group_range from test/zdtm/static/socket*_icmp.c and let's see if it works.

  1. If we add uns we get:
b'(00.004760)      1: Error (criu/net.c:2180): net: unix: Failed to write net/ipv4/<sysctls>'

I see that we get EINVAL when writing to ping_group_range on criu restore.

Likely the low/high uids we try to set got considered invalid in the userns of the caller (CRIU). We should probably just enter proper userns at that point of setting ping_group_range, I will try to fix this.

25 changes: 25 additions & 0 deletions test/zdtm_ct.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mount.h>
#include <unistd.h>
#include <time.h>
Expand Down Expand Up @@ -91,6 +92,27 @@ static int create_timens(void)
return 0;
}

int sysctl_write_str(const char *name, char *data)
{
int fd, ret;

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

ret = write(fd, data, strlen(data));
if (ret < 0) {
fprintf(stderr, "Can't write %s into %s", data, name);
close(fd);
return -1;
}
close(fd);

return 0;
}

int main(int argc, char **argv)
{
uid_t uid;
Expand All @@ -112,6 +134,9 @@ int main(int argc, char **argv)
if (!uid) {
if (create_timens())
exit(1);
// Allow GIDs 0-58468 to open an unprivileged ICMP socket
if (sysctl_write_str("/proc/sys/net/ipv4/ping_group_range", "0 58468"))
exit(1);
if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL)) {
fprintf(stderr, "mount(/, S_REC | MS_SLAVE)): %m");
return 1;
Expand Down
Loading