diff options
author | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2019-05-09 15:54:32 +0000 |
---|---|---|
committer | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2019-05-09 15:54:32 +0000 |
commit | e1c4560cd38d5d58c43de18b85398b61eb1bc0a9 (patch) | |
tree | 238c333fe37657f08c4af542813dd1f2ec369f87 /regress/sys/netinet6/rip6cksum | |
parent | ddaf007d2ca1ad97fc037cf902214116abcae919 (diff) |
Test IPv6 raw sockets with checksum calculation in kernel.
Diffstat (limited to 'regress/sys/netinet6/rip6cksum')
-rw-r--r-- | regress/sys/netinet6/rip6cksum/LICENSE | 15 | ||||
-rw-r--r-- | regress/sys/netinet6/rip6cksum/Makefile | 137 | ||||
-rw-r--r-- | regress/sys/netinet6/rip6cksum/README | 6 | ||||
-rw-r--r-- | regress/sys/netinet6/rip6cksum/rip6cksum.c | 202 | ||||
-rwxr-xr-x | regress/sys/netinet6/rip6cksum/sendrecv.py | 94 |
5 files changed, 454 insertions, 0 deletions
diff --git a/regress/sys/netinet6/rip6cksum/LICENSE b/regress/sys/netinet6/rip6cksum/LICENSE new file mode 100644 index 00000000000..897cd3e6996 --- /dev/null +++ b/regress/sys/netinet6/rip6cksum/LICENSE @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/regress/sys/netinet6/rip6cksum/Makefile b/regress/sys/netinet6/rip6cksum/Makefile new file mode 100644 index 00000000000..0ee1e197d6f --- /dev/null +++ b/regress/sys/netinet6/rip6cksum/Makefile @@ -0,0 +1,137 @@ +# $OpenBSD: Makefile,v 1.1.1.1 2019/05/09 15:54:31 bluhm Exp $ + +# The following ports must be installed: +# +# python-2.7 interpreted object-oriented programming language +# py-libdnet python interface to libdnet +# scapy powerful interactive packet manipulation in python + +.if ! exists(/usr/local/bin/python2) || ! exists(/usr/local/bin/scapy) +regress: + @echo Install python and the scapy module for additional tests. + @echo SKIPPED +.endif + +PROG = rip6cksum +WARNINGS = yes + +REGRESS_TARGETS += run-error-negative +run-error-negative: + @echo "\n======== $@ ========" + # set socket option IPV6_CHECKSUM to -2, expect error + ${SUDO} ./rip6cksum -c -2 -e + +REGRESS_TARGETS += run-error-odd +run-error-odd: + @echo "\n======== $@ ========" + # set socket option IPV6_CHECKSUM to 1, expect error + ${SUDO} ./rip6cksum -c 1 -e + +REGRESS_TARGETS += run-no-cksum +run-no-cksum: + # send and receive packet without checksum + @echo "\n======== $@ ========" + ${SUDO} ./rip6cksum -r 32 -s 8 -w -- \ + python2 -u ${.CURDIR}/sendrecv.py -r 8 -s 32 + +REGRESS_TARGETS += run-bad-cksum +run-bad-cksum: + # enable checksum, send packet without checksum, expect icmp + @echo "\n======== $@ ========" + ${SUDO} ./rip6cksum -c 0 -- \ + python2 -u ${.CURDIR}/sendrecv.py -i -r 32 -s 32 + +REGRESS_TARGETS += run-disable-cksum +run-disable-cksum: + # send and receive packet with explicitly disabled checksum + @echo "\n======== $@ ========" + ${SUDO} ./rip6cksum -c -1 -r 32 -s 8 -w -- \ + python2 -u ${.CURDIR}/sendrecv.py -r 8 -s 32 + +REGRESS_TARGETS += run-ckoff-0 +run-ckoff-0: + @echo "\n======== $@ ========" + # use checksum at offset 0 + ${SUDO} ./rip6cksum -c 0 -r 32 -s 8 -w -- \ + python2 -u ${.CURDIR}/sendrecv.py -c 0 -r 8 -s 32 + +REGRESS_TARGETS += run-ckoff-0-empty +run-ckoff-0-empty: + @echo "\n======== $@ ========" + # use checksum at offset 0, but packet is empty, expect icmp + ${SUDO} ./rip6cksum -c 0 -- \ + python2 -u ${.CURDIR}/sendrecv.py -i -r 0 -s 0 + +REGRESS_TARGETS += run-ckoff-0-short +run-ckoff-0-short: + @echo "\n======== $@ ========" + # use checksum at offset 0, but packet is only 1 byte long, expect icmp + ${SUDO} ./rip6cksum -c 0 -- \ + python2 -u ${.CURDIR}/sendrecv.py -i -r 1 -s 1 + +REGRESS_TARGETS += run-ckoff-0-exact +run-ckoff-0-exact: + @echo "\n======== $@ ========" + # use checksum at offset 0, packet is exactly 2 bytes long + ${SUDO} ./rip6cksum -c 0 -r 2 -s 2 -w -- \ + python2 -u ${.CURDIR}/sendrecv.py -c 0 -s 2 + +REGRESS_TARGETS += run-ckoff-0-long +run-ckoff-0-long: + @echo "\n======== $@ ========" + # use checksum at offset 0, packet is 3 bytes long + ${SUDO} ./rip6cksum -c 0 -r 3 -s 3 -w -- \ + python2 -u ${.CURDIR}/sendrecv.py -c 0 -s 3 + +REGRESS_TARGETS += run-ckoff-2 +run-ckoff-2: + @echo "\n======== $@ ========" + # use checksum at offset 2 + ${SUDO} ./rip6cksum -c 2 -r 32 -s 8 -w -- \ + python2 -u ${.CURDIR}/sendrecv.py -c 2 -r 8 -s 32 + +REGRESS_TARGETS += run-ckoff-2-empty +run-ckoff-2-empty: + @echo "\n======== $@ ========" + # use checksum at offset 2, but packet is empty, expect icmp + ${SUDO} ./rip6cksum -c 2 -- \ + python2 -u ${.CURDIR}/sendrecv.py -i -r 0 -s 0 + +REGRESS_TARGETS += run-ckoff-2-short-1 +run-ckoff-2-short-1: + @echo "\n======== $@ ========" + # use checksum at offset 2, but packet is only 1 byte long, expect icmp + ${SUDO} ./rip6cksum -c 2 -- \ + python2 -u ${.CURDIR}/sendrecv.py -i -r 1 -s 1 + +REGRESS_TARGETS += run-ckoff-2-short-2 +run-ckoff-2-short-2: + @echo "\n======== $@ ========" + # use checksum at offset 2, but packet is only 2 byte long, expect icmp + ${SUDO} ./rip6cksum -c 2 -- \ + python2 -u ${.CURDIR}/sendrecv.py -i -r 2 -s 2 + +REGRESS_TARGETS += run-ckoff-2-short-3 +run-ckoff-2-short-3: + @echo "\n======== $@ ========" + # use checksum at offset 2, but packet is only 3 byte long, expect icmp + ${SUDO} ./rip6cksum -c 2 -- \ + python2 -u ${.CURDIR}/sendrecv.py -i -r 3 -s 3 + +REGRESS_TARGETS += run-ckoff-2-exact +run-ckoff-2-exact: + @echo "\n======== $@ ========" + # use checksum at offset 2, packet is exactly 4 bytes long + ${SUDO} ./rip6cksum -c 2 -r 4 -s 4 -w -- \ + python2 -u ${.CURDIR}/sendrecv.py -c 2 -s 4 + +REGRESS_TARGETS += run-ckoff-2-long +run-ckoff-2-long: + @echo "\n======== $@ ========" + # use checksum at offset 2, packet is 5 bytes long + ${SUDO} ./rip6cksum -c 2 -r 5 -s 5 -w -- \ + python2 -u ${.CURDIR}/sendrecv.py -c 2 -s 5 + +${REGRESS_TARGETS}: ${PROG} + +.include <bsd.regress.mk> diff --git a/regress/sys/netinet6/rip6cksum/README b/regress/sys/netinet6/rip6cksum/README new file mode 100644 index 00000000000..4635b5c60b2 --- /dev/null +++ b/regress/sys/netinet6/rip6cksum/README @@ -0,0 +1,6 @@ +Test IPv6 raw sockets with checksum calculation in kernel. + +The C program rip6cksum creates a raw socket, binds and connects +it, and sets the checksum option. Then it forks the scapy program +sendrecv. It sends a protocol 255 packet and expects an answer or +ICMP6 error. diff --git a/regress/sys/netinet6/rip6cksum/rip6cksum.c b/regress/sys/netinet6/rip6cksum/rip6cksum.c new file mode 100644 index 00000000000..7a701055462 --- /dev/null +++ b/regress/sys/netinet6/rip6cksum/rip6cksum.c @@ -0,0 +1,202 @@ +/* $OpenBSD: rip6cksum.c,v 1.1.1.1 2019/05/09 15:54:31 bluhm Exp $ */ +/* + * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <err.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <netinet/in.h> + +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/wait.h> + +void __dead usage(void); + +void __dead +usage(void) +{ + fprintf(stderr, "rip6cksum [-ehw] [-c ckoff] [-r recvsz] [-s sendsz] " + "[-- scapy ...]\n" + " -c ckoff set checksum offset within rip header\n" + " -e expect error when setting ckoff\n" + " -h help, show usage\n" + " -r recvsz expected payload size from socket\n" + " -s sendsz send payload of given size to socket\n" + " -w wait for packet on socket, timeout 10 seconds\n" + " scapy ... run scapy program after socket setup\n" + ); + exit(1); +} + +const struct in6_addr loop6 = IN6ADDR_LOOPBACK_INIT; +int +main(int argc, char *argv[]) +{ + int s, ch, eflag, cflag, rflag, sflag, wflag; + int ckoff; + size_t recvsz, sendsz; + const char *errstr; + struct sockaddr_in6 sin6; + + if (setvbuf(stdout, NULL, _IOLBF, 0) != 0) + err(1, "setvbuf stdout line buffered"); + + eflag = cflag = rflag = sflag = wflag = 0; + while ((ch = getopt(argc, argv, "c:ehr:s:w")) != -1) { + switch (ch) { + case 'c': + ckoff = strtonum(optarg, INT_MIN, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "ckoff is %s: %s", errstr, optarg); + cflag = 1; + break; + case 'e': + eflag = 1; + break; + case 'r': + recvsz = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "recvsz is %s: %s", errstr, optarg); + rflag = 1; + break; + case 's': + sendsz = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "sendsz is %s: %s", errstr, optarg); + sflag = 1; + break; + case 'w': + wflag = 1; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + printf("socket inet6 raw 255\n"); + s = socket(AF_INET6, SOCK_RAW, 255); + if (s == -1) + err(1, "socket raw"); + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = loop6; + printf("bind ::1\n"); + if (bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) == -1) + err(1, "bind ::1"); + printf("connect ::1\n"); + if (connect(s, (struct sockaddr *)&sin6, sizeof(sin6)) == -1) + err(1, "connect ::1"); + + if (cflag) { + printf("setsockopt ipv6 checksum %d\n", ckoff); + if (setsockopt(s, IPPROTO_IPV6, IPV6_CHECKSUM, &ckoff, + sizeof(ckoff)) == -1) { + if (!eflag) + err(1, "setsockopt ckoff"); + printf("setsockopt failed as expected: %s\n", + strerror(errno)); + } else { + if (eflag) + errx(1, "setsockopt succeeded unexpectedly"); + } + } + + if (argc) { + pid_t pid; + + printf("fork child process\n"); + pid = fork(); + if (pid == -1) + err(1, "fork"); + if (pid == 0) { + /* child */ + printf("execute %s\n", argv[0]); + execvp(argv[0], argv); + err(1, "execvp %s", argv[0]); + } + printf("child pid %d\n", pid); + } + + if (wflag) { + int n; + ssize_t r; + size_t rsz; + fd_set fds; + struct timeval to; + char buf[1<<16]; + + FD_ZERO(&fds); + FD_SET(s, &fds); + to.tv_sec = 10; + to.tv_usec = 0; + printf("select socket read\n"); + n = select(s + 1, &fds, NULL, NULL, &to); + switch (n) { + case -1: + err(1, "select"); + case 0: + errx(1, "timeout"); + default: + printf("selected %d\n", n); + } + printf("recv packet\n"); + r = recv(s, buf, sizeof(buf), 0); + if (r < 0) + err(1, "recv"); + rsz = r; + printf("received payload size %zd\n", rsz); + if (rflag) { + if (rsz != recvsz) + errx(1, "wrong payload size, expected %zu", recvsz); + } + } + + if (sflag) { + size_t i; + char *buf; + + buf = malloc(sendsz); + if (buf == NULL) + err(1, "malloc sendsz"); + for (i = 0; i < sendsz; i++) + buf[i] = i & 0xff; + printf("send payload of size %zu\n", sendsz); + if (send(s, buf, sendsz, 0) == -1) + err(1, "send"); + free(buf); + } + + if (argc) { + int status; + + printf("wait for child\n"); + if (wait(&status) == -1) + err(1, "wait"); + if (status != 0) + errx(1, "child program %s status %d", argv[0], status); + printf("child program %s status %d\n", argv[0], status); + } + + return 0; +} diff --git a/regress/sys/netinet6/rip6cksum/sendrecv.py b/regress/sys/netinet6/rip6cksum/sendrecv.py new file mode 100755 index 00000000000..d02fb7d8e40 --- /dev/null +++ b/regress/sys/netinet6/rip6cksum/sendrecv.py @@ -0,0 +1,94 @@ +#!/usr/local/bin/python2.7 +# $OpenBSD: sendrecv.py,v 1.1.1.1 2019/05/09 15:54:31 bluhm Exp $ + +import os +from scapy.all import * +from struct import pack +import getopt, sys + +def usage(): + print "raw6-sendrecv [-hi] [-c ckoff] [-r recvsz] [-s sendsz]" + print " -c ckoff set checksum offset within payload" + print " -h help, show usage" + print " -i expect icmp6 error message as response" + print " -r recvsz expected payload size" + print " -s sendsz set payload size" + exit(1) + +opts, args = getopt.getopt(sys.argv[1:], "c:hir:s:") + +ip = IPv6(src="::1", dst="::1", nh=255) + +ckoff = None +icmp = False +recvsz = None +sendsz = None +for o, a in opts: + if o == "-c": + ckoff = int(a) + elif o == "-i": + icmp = True + elif o == "-r": + recvsz = int(a) + elif o == "-s": + sendsz = int(a) + else: + usage() + +payload = ""; +if sendsz is not None: + for i in range(sendsz): + payload += chr(i & 0xff) + print "payload length is", len(payload) + +if ckoff is not None: + payload = payload[:ckoff] + pack("xx") + payload[ckoff+2:] + cksum = in6_chksum(255, ip, payload) + print "calculated checksum is", cksum + payload = payload[:ckoff] + pack("!H", cksum) + payload[ckoff+2:] + +req=ip/payload +# As we are sending from ::1 to ::1 we sniff our own packet as answer. +# Add a filter that matches on the expected answer using the payload size. +if icmp: + filter="icmp6" + if recvsz is not None: + filter += (" and len = %d" % (4 + 40 + 8 + 40 + recvsz)) +else: + filter="proto 255" + if recvsz is not None: + filter += (" and len = %d" % (4 + 40 + recvsz)) +print "filter", filter +ans=sr(req, iface="lo0", filter=filter, timeout=10) +print ans +res=ans[0][0][1] +res.show() + +print "response protocol next header is", res.nh +if icmp: + if res.nh != 58: + print "response wrong protocol, expected icmp6" + exit(1) + print "response icmp6 type is", res.payload.type + if res.payload.type != 4: + print "response wrong icmp6 type, expected parameter problem" + exit(1) + exit(0) + +if res.nh != 255: + print "response with wrong protocol, expected 255, got" + exit(1) + +cksum = in6_chksum(255, res, res.payload.load) +print "received checksum is", cksum +if ckoff is not None and cksum != 0: + print "received invalid checksum", cksum + exit(1) + +print "received payload length is", len(res.payload.load) +if recvsz is not None: + if len(res.payload.load) != recvsz: + print "wrong payload length, expected", recvsz + exit(1) + +exit(0) |