[CRIU] [PATCH 12/15] inet: raw -- Add socket-raw test
Cyrill Gorcunov
gorcunov at gmail.com
Thu Sep 13 22:57:55 MSK 2018
Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
test/zdtm/static/Makefile | 1 +
test/zdtm/static/socket-raw.c | 382 +++++++++++++++++++++++++++++++
test/zdtm/static/socket-raw.desc | 1 +
3 files changed, 384 insertions(+)
create mode 100644 test/zdtm/static/socket-raw.c
create mode 100644 test/zdtm/static/socket-raw.desc
diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile
index fe67747db954..7b8d663778b6 100644
--- a/test/zdtm/static/Makefile
+++ b/test/zdtm/static/Makefile
@@ -66,6 +66,7 @@ TST_NOFILE := \
sockets02 \
sockets_spair \
socket_queues \
+ socket-raw \
socket-tcp \
socket-tcp-reseted \
socket-tcp6 \
diff --git a/test/zdtm/static/socket-raw.c b/test/zdtm/static/socket-raw.c
new file mode 100644
index 000000000000..355addb1c6a5
--- /dev/null
+++ b/test/zdtm/static/socket-raw.c
@@ -0,0 +1,382 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <linux/icmp.h>
+#include <linux/icmpv6.h>
+
+#include <limits.h>
+#include <fcntl.h>
+
+#include "zdtmtst.h"
+
+/*
+ * Some code snippets are taken from
+ * http://www.binarytides.com/raw-udp-sockets-c-linux/
+ */
+
+const char *test_doc = "Test RAW sockets (IPv4,6)\n";
+const char *test_author = "Cyrill Gorcunov <gorcunov at openvz.org>";
+
+#ifndef SO_IP_SET
+# define SO_IP_SET 83
+#endif
+
+#ifndef IP_SET_OP_VERSION
+# define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */
+#endif
+
+#define pr_debug(format, arg...) test_msg("DBG: %s:%d: " format, __FILE__, __LINE__, ## arg)
+
+struct ip_set_req_version {
+ unsigned int op;
+ unsigned int version;
+};
+
+struct pseudo_header {
+ uint32_t source_address;
+ uint32_t dest_address;
+ uint8_t placeholder;
+ uint8_t protocol;
+ uint16_t udp_length;
+};
+
+static int stop_icmp(int sk_icmp, int sk_icmpv6)
+{
+ struct icmp6_filter filter6 = { };
+ struct icmp_filter filter = { };
+ socklen_t aux;
+ int ret = 0;
+
+ aux = sizeof(filter);
+ ret = getsockopt(sk_icmp, SOL_RAW, ICMP_FILTER, &filter, &aux);
+ if (ret < 0) {
+ pr_perror("stop_icmp: Can't fetch icmp filter");
+ return ret;
+ }
+
+ if (filter.data != (1 << ICMP_TIMESTAMP)) {
+ pr_err("data mismatch on icmp filter %d != %d\n",
+ filter.data, (1 << ICMP_TIMESTAMP));
+ return -1;
+ }
+
+ aux = sizeof(filter6);
+ ret = getsockopt(sk_icmpv6, SOL_ICMPV6, ICMPV6_FILTER, &filter6, &aux);
+ if (ret < 0) {
+ pr_perror("stop_icmp: Can't fetch icmpv6 filter");
+ return ret;
+ }
+
+ if (filter6.data[0] != (1 << ICMP_TIMESTAMP)) {
+ pr_err("data mismatch on icmp filter %d != %d\n",
+ filter6.data[0], (1 << ICMP_TIMESTAMP));
+ return -1;
+ }
+
+ return ret;
+}
+
+static int start_icmp(int sk_icmp, int sk_icmpv6,
+ const char *a4, const char *a6, int port)
+{
+ struct sockaddr_in addr_client;
+ struct icmp6_filter filter6 = { };
+ struct icmp_filter filter = { };
+ int ret = 0;
+
+ memset(&addr_client, 0, sizeof(addr_client));
+
+ addr_client.sin_family = AF_INET;
+ addr_client.sin_port = htons(port);
+ addr_client.sin_addr.s_addr = inet_addr(a4);
+
+ ret = bind(sk_icmp, (struct sockaddr *)&addr_client, sizeof(addr_client));
+ if (ret < 0) {
+ pr_perror("start_icmp: Can't bind RAW client socket");
+ return ret;
+ }
+ pr_debug("start_icmp: Bound sk_icmp\n");
+
+ filter.data = (1 << ICMP_TIMESTAMP);
+ ret = setsockopt(sk_icmp, SOL_RAW, ICMP_FILTER, &filter, sizeof(filter));
+ if (ret < 0) {
+ pr_perror("start_icmp: Can't setup icmp filter");
+ return ret;
+ }
+
+ filter6.data[0] = (1 << ICMP_TIMESTAMP);
+ ret = setsockopt(sk_icmpv6, SOL_ICMPV6, ICMPV6_FILTER, &filter6, sizeof(filter6));
+ if (ret < 0) {
+ pr_perror("start_icmp: Can't setup icmpv6 filter");
+ return ret;
+ }
+
+ return ret;
+}
+
+static unsigned short csum(unsigned short *ptr, int nbytes)
+{
+ unsigned short oddbyte;
+ register short answer;
+ register long sum;
+
+ sum = 0;
+ while (nbytes > 1) {
+ sum += *ptr++;
+ nbytes -= 2;
+ }
+
+ if (nbytes == 1) {
+ oddbyte = 0;
+ *((unsigned char *)&oddbyte) = *(unsigned char *)ptr;
+ sum += oddbyte;
+ }
+
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum = sum + (sum >> 16);
+ answer = (short)~sum;
+
+ return answer;
+}
+
+int main(int argc, char *argv[])
+{
+ const char string_data[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ const char string_client_ip[] = "127.0.0.12";
+ const char string_serv_ip[] = "127.0.0.10";
+ const char string_client_icmp_ip[] = "127.0.0.14";
+ const char string_client_icmpv6_ip[] = "::14";
+ char datagram[512], *data, *pseudogram;
+ char receiver[512];
+
+ struct ip_set_req_version req_version;
+ socklen_t size = sizeof(req_version);
+
+ int sk_udp, sk_udp_serv;
+ int sk_raw, sk6_raw;
+ int sk_icmp, sk_icmpv6;
+
+ struct udphdr *udph = (struct udphdr *)(datagram + sizeof(struct ip));
+ struct iphdr *iph = (struct iphdr *)datagram;
+ struct sockaddr_in addr_serv, addr_client;
+ struct pseudo_header psh;
+
+ int port_client = 8080;
+ int port_serv = 8081;
+
+ int psize, one = 1;
+ const int *val = &one;
+
+ socklen_t len = sizeof(struct sockaddr_in);
+ int ret, status;
+
+ pid_t pid;
+
+ task_waiter_t waiter;
+
+ test_init(argc, argv);
+
+ task_waiter_init(&waiter);
+
+ sk_raw = socket(PF_INET, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW);
+ if (sk_raw < 0) {
+ pr_perror("Can't create IPv4 raw socket");
+ exit(1);
+ }
+ pr_debug("sk_raw %d\n", sk_raw);
+
+ /* Simply to make sure it can be recreated on restore */
+ sk6_raw = socket(PF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW);
+ if (sk6_raw < 0) {
+ pr_perror("Can't create IPv6 raw socket");
+ exit(1);
+ }
+ pr_debug("sk6_raw %d\n", sk6_raw);
+
+ sk_udp = socket(PF_INET, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_UDP);
+ if (sk_udp < 0) {
+ pr_perror("Can't create IPv4 raw-udp socket");
+ exit(1);
+ }
+ pr_debug("sk_udp %d\n", sk_udp);
+
+ sk_icmp = socket(PF_INET, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_ICMP);
+ if (sk_icmp < 0) {
+ pr_perror("Can't create IPv4 raw icmp socket");
+ exit(1);
+ }
+ pr_debug("sk_icmp %d\n", sk_icmp);
+
+ sk_icmpv6 = socket(PF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_ICMPV6);
+ if (sk_icmpv6 < 0) {
+ pr_perror("Can't create IPv6 raw icmpv6 socket");
+ exit(1);
+ }
+ pr_debug("sk_icmpv6 %d\n", sk_icmpv6);
+
+ sk_udp_serv = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sk_udp_serv < 0) {
+ pr_perror("Can't create DGRAM server socket");
+ exit(1);
+ }
+ pr_debug("sk_udp_serv %d\n", sk_udp_serv);
+
+ memset(datagram, 0, sizeof(datagram));
+ memset(receiver, 0, sizeof(receiver));
+ memset(&addr_serv, 0, sizeof(addr_serv));
+ memset(&addr_client, 0, sizeof(addr_client));
+
+ addr_client.sin_family = AF_INET;
+ addr_client.sin_port = htons(port_client);
+ addr_client.sin_addr.s_addr = inet_addr(string_client_ip);
+
+ addr_serv.sin_family = AF_INET;
+ addr_serv.sin_port = htons(port_serv);
+ addr_serv.sin_addr.s_addr = inet_addr(string_serv_ip);
+
+ ret = bind(sk_udp_serv, (struct sockaddr *)&addr_serv, sizeof(addr_serv));
+ if (ret < 0) {
+ pr_perror("Can't bind DGRAM server socket");
+ return 1;
+ }
+ pr_debug("Bound sk_udp_serv\n");
+
+ ret = bind(sk_udp, (struct sockaddr *)&addr_client, sizeof(addr_client));
+ if (ret < 0) {
+ pr_perror("Can't bind DGRAM client socket");
+ return 1;
+ }
+ pr_debug("Bound sk_udp\n");
+
+ if (start_icmp(sk_icmp, sk_icmpv6, string_client_icmp_ip,
+ string_client_icmpv6_ip, port_client))
+ return 1;
+
+ data = datagram + sizeof(struct iphdr) + sizeof(struct udphdr);
+ strcpy(data, string_data);
+
+ iph->ihl = 5;
+ iph->version = 4;
+ iph->tos = 0;
+ iph->tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + strlen(string_data);
+ iph->id = htonl(54321);
+ iph->frag_off = 0;
+ iph->ttl = 255;
+ iph->protocol = IPPROTO_UDP;
+ iph->check = 0;
+ iph->saddr = inet_addr(string_client_ip);
+ iph->daddr = addr_serv.sin_addr.s_addr;
+ iph->check = csum((unsigned short *)datagram, sizeof(struct iphdr));
+
+ udph->source = htons(port_client);
+ udph->dest = htons(port_serv);
+ udph->len = htons(8 + strlen(data));
+ udph->check = 0;
+
+ psh.source_address = inet_addr(string_client_ip);
+ psh.dest_address = addr_serv.sin_addr.s_addr;
+ psh.placeholder = 0;
+ psh.protocol = IPPROTO_UDP;
+ psh.udp_length = htons(sizeof(struct udphdr) + strlen(string_data));
+
+ psize = sizeof(psh) + sizeof(struct udphdr) + strlen(string_data);
+ pseudogram = malloc(psize);
+ if (!pseudogram) {
+ pr_err("No free memory\n");
+ exit(1);
+ }
+
+ memcpy(pseudogram, (char *)&psh , sizeof(psh));
+ memcpy(pseudogram + sizeof(psh), udph, sizeof(*udph) + strlen(string_data));
+
+ udph->check = csum((unsigned short *)pseudogram, psize);
+ free(pseudogram);
+
+ if (setsockopt(sk_udp, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) {
+ pr_perror("Error setting IP_HDRINCL");
+ exit(1);
+ }
+
+ pid = test_fork();
+ if (pid == 0) {
+ task_waiter_wait4(&waiter, 2);
+ pr_debug("Gonna read data\n");
+ ret = recvfrom(sk_udp_serv, receiver, sizeof(receiver), 0,
+ (struct sockaddr *)&addr_client, &len);
+ if (ret < 0) {
+ task_waiter_complete(&waiter, 2);
+ fail("Can't read data");
+ exit(1);
+ }
+ receiver[ret] = '\0';
+ pr_debug("Read %d bytes\n", ret);
+
+ task_waiter_complete(&waiter, 3);
+
+ if (strcmp(receiver, string_data)) {
+ pr_err("Data mismatch (got %s but expected %s)\n",
+ receiver, string_data);
+ exit(1);
+ } else
+ pr_debug("Data match\n");
+ exit(0);
+ } else if (pid < 0) {
+ pr_err("Can't fork\n");
+ exit(1);
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ if (sendto(sk_udp, datagram, iph->tot_len, 0,
+ (struct sockaddr *)&addr_serv, sizeof(addr_serv)) < 0) {
+ kill(pid, SIGKILL);
+ fail("Can't send RAW data");
+ exit(1);
+ }
+
+ task_waiter_complete(&waiter, 2);
+ pr_debug("Sent %d bytes\n", (int)iph->tot_len);
+ task_waiter_wait4(&waiter, 3);
+
+ ret = wait(&status);
+ if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status)) {
+ kill(pid, SIGKILL);
+ fail("Failed waiting server\n");
+ exit(1);
+ }
+
+ req_version.op = IP_SET_OP_VERSION;
+ ret = getsockopt(sk_raw, SOL_IP, SO_IP_SET, &req_version, &size);
+ if (ret) {
+ pr_perror("xt_set getsockopt");
+ if (errno != ENOPROTOOPT) {
+ fail("Can't fetch SO_IP_SET");
+ exit(1);
+ }
+ } else
+ test_msg("SO_IP_SET version = %d\n", req_version.version);
+
+ if (stop_icmp(sk_icmp, sk_icmpv6)) {
+ fail("Failed on ICMP sockets");
+ exit(1);
+ }
+
+ pass();
+ return 0;
+}
diff --git a/test/zdtm/static/socket-raw.desc b/test/zdtm/static/socket-raw.desc
new file mode 100644
index 000000000000..359b344c3620
--- /dev/null
+++ b/test/zdtm/static/socket-raw.desc
@@ -0,0 +1 @@
+{'flags': 'suid', 'feature': 'net_diag_raw'}
--
2.17.1
More information about the CRIU
mailing list