diff options
Diffstat (limited to 'tools/regression/netinet')
39 files changed, 4911 insertions, 0 deletions
diff --git a/tools/regression/netinet/arphold/Makefile b/tools/regression/netinet/arphold/Makefile new file mode 100644 index 000000000000..97cedfc3f9d1 --- /dev/null +++ b/tools/regression/netinet/arphold/Makefile @@ -0,0 +1,5 @@ +PROG= arphold +MAN= +CFLAGS+= -Wall + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/arphold/arphold.c b/tools/regression/netinet/arphold/arphold.c new file mode 100644 index 000000000000..c49166ed4bc4 --- /dev/null +++ b/tools/regression/netinet/arphold/arphold.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2010 Hudson River Trading LLC + * Written by George Neville-Neil gnn@freebsd.org + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Description: The following is a test of the arp entry packet queues + * which replaced the single packet hold entry that existed in the BSDs + * since time immemorial. The test process is: + * + * 1) Find out the current system limit (maxhold) + * 2) Using an IP address for which we do not yet have an entry + * load up an ARP entry packet queue with exactly that many packets. + * 3) Check the arp dropped stat to make sure that we have not dropped + * any packets as yet. + * 4) Add one more packet to the queue. + * 5) Make sure that only one packet was dropped. + * + * CAVEAT: The ARP timer will flush the queue after 1 second so it is + * important not to run this code in a fast loop or the test will + * fail. + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if_arp.h> + +#define MSG_SIZE 1024 +#define PORT 6969 + +int +main(int argc, char **argv) +{ + + int sock; + int maxhold; + size_t size = sizeof(maxhold); + struct sockaddr_in dest; + char message[MSG_SIZE]; + struct arpstat arpstat; + size_t len = sizeof(arpstat); + unsigned long dropped = 0; + + memset(&message, 1, sizeof(message)); + + if (sysctlbyname("net.link.ether.inet.maxhold", &maxhold, &size, + NULL, 0) < 0) { + perror("not ok 1 - sysctlbyname failed"); + exit(1); + } + +#ifdef DEBUG + printf("maxhold is %d\n", maxhold); +#endif /* DEBUG */ + + if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("not ok 1 - could not open socket"); + exit(1); + } + + bzero(&dest, sizeof(dest)); + if (inet_pton(AF_INET, argv[1], &dest.sin_addr.s_addr) != 1) { + perror("not ok 1 - could not parse address"); + exit(1); + } + dest.sin_len = sizeof(dest); + dest.sin_family = AF_INET; + dest.sin_port = htons(PORT); + + if (sysctlbyname("net.link.ether.arp.stats", &arpstat, &len, + NULL, 0) < 0) { + perror("not ok 1 - could not get initial arp stats"); + exit(1); + } + + dropped = arpstat.dropped; +#ifdef DEBUG + printf("dropped before %ld\n", dropped); +#endif /* DEBUG */ + + /* + * Load up the queue in the ARP entry to the maximum. + * We should not drop any packets at this point. + */ + + while (maxhold > 0) { + if (sendto(sock, message, sizeof(message), 0, + (struct sockaddr *)&dest, sizeof(dest)) < 0) { + perror("not ok 1 - could not send packet"); + exit(1); + } + maxhold--; + } + + if (sysctlbyname("net.link.ether.arp.stats", &arpstat, &len, + NULL, 0) < 0) { + perror("not ok 1 - could not get new arp stats"); + exit(1); + } + +#ifdef DEBUG + printf("dropped after %ld\n", arpstat.dropped); +#endif /* DEBUG */ + + if (arpstat.dropped != dropped) { + printf("not ok 1 - Failed, drops changed:" + "before %ld after %ld\n", dropped, arpstat.dropped); + exit(1); + } + + dropped = arpstat.dropped; + + /* Now add one extra and make sure it is dropped. */ + if (sendto(sock, message, sizeof(message), 0, + (struct sockaddr *)&dest, sizeof(dest)) < 0) { + perror("not ok 1 - could not send packet"); + exit(1); + } + + if (sysctlbyname("net.link.ether.arp.stats", &arpstat, &len, + NULL, 0) < 0) { + perror("not ok 1 - could not get new arp stats"); + exit(1); + } + + if (arpstat.dropped != (dropped + 1)) { + printf("not ok 1 - Failed to drop one packet: before" + " %ld after %ld\n", dropped, arpstat.dropped); + exit(1); + } + + printf("ok\n"); + return (0); +} diff --git a/tools/regression/netinet/arphold/arphold.t b/tools/regression/netinet/arphold/arphold.t new file mode 100644 index 000000000000..a56784c8ea78 --- /dev/null +++ b/tools/regression/netinet/arphold/arphold.t @@ -0,0 +1,6 @@ +#!/bin/sh +# + +make arphold 2>&1 > /dev/null + +./arphold 192.168.1.222 diff --git a/tools/regression/netinet/ip_id_period/ip_id_period.py b/tools/regression/netinet/ip_id_period/ip_id_period.py new file mode 100644 index 000000000000..2e97df499ae2 --- /dev/null +++ b/tools/regression/netinet/ip_id_period/ip_id_period.py @@ -0,0 +1,76 @@ +# Copyright (C) 2008 Michael J. Silbersack. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice unmodified, this list of conditions, and the following +# disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# +# This is a regression test to verify the proper behavior of IP ID generation +# code. It will push 200000 packets, then report back what the min and max +# periods it saw for different IDs were. + +from __future__ import print_function +import os +import signal +import subprocess +import time + +if os.path.exists('results.pcap'): + os.remove('results.pcap') +tcpdump = subprocess.Popen('tcpdump -n -i lo0 -w results.pcap icmp', shell=True) +time.sleep(1) # Give tcpdump time to start + +os.system('sysctl net.inet.icmp.icmplim=0') +os.system('ping -q -i .001 -c 100000 127.0.0.1') + +time.sleep(3) # Give tcpdump time to catch up +os.kill(tcpdump.pid, signal.SIGTERM) + +os.system('tcpdump -n -v -r results.pcap > results.txt') + +id_lastseen = {} +id_minperiod = {} + +count = 0 +for line in open('results.txt').readlines(): + id = int(line.split(' id ')[1].split(',')[0]) + if id in id_lastseen: + period = count - id_lastseen[id] + if id not in id_minperiod or period < id_minperiod[id]: + id_minperiod[id] = period + id_lastseen[id] = count + count += 1 + +sorted_minperiod = list(zip(*reversed(list(zip(*list(id_minperiod.items())))))) +sorted_minperiod.sort() + +print("Lowest 10 ID periods detected:") +x = 0 +while x < 10: + id_tuple = sorted_minperiod.pop(0) + print("id: %d period: %d" % (id_tuple[1], id_tuple[0])) + x += 1 + +print("Highest 10 ID periods detected:") +x = 0 +while x < 10: + id_tuple = sorted_minperiod.pop() + print("id: %d period: %d" % (id_tuple[1], id_tuple[0])) + x += 1 diff --git a/tools/regression/netinet/ipbroadcast/Makefile b/tools/regression/netinet/ipbroadcast/Makefile new file mode 100644 index 000000000000..2036936e8da6 --- /dev/null +++ b/tools/regression/netinet/ipbroadcast/Makefile @@ -0,0 +1,7 @@ +# +# + +PROG= ipbroadcast +MAN= + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/ipbroadcast/ipbroadcast.c b/tools/regression/netinet/ipbroadcast/ipbroadcast.c new file mode 100644 index 000000000000..9315ebe2b6ad --- /dev/null +++ b/tools/regression/netinet/ipbroadcast/ipbroadcast.c @@ -0,0 +1,352 @@ +/*- + * Copyright (c) 2007 Bruce M. Simpson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Test utility for IPv4 broadcast sockets. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <err.h> +#include <errno.h> +#include <getopt.h> +#include <pwd.h> +#include <unistd.h> +#include <netdb.h> +#include <libgen.h> + +#ifndef IP_SENDIF +#define IP_SENDIF 24 /* XXX */ +#endif + +#ifndef IPPROTO_ZEROHOP +#define IPPROTO_ZEROHOP 114 /* any 0-hop protocol */ +#endif + +#define DEFAULT_PORT 6698 +#define DEFAULT_PAYLOAD_SIZE 24 +#define DEFAULT_TTL 1 + +#define MY_CMSG_SIZE \ + CMSG_SPACE(sizeof(struct in_addr)) + \ + CMSG_SPACE(sizeof(struct sockaddr_dl)) + +static char *progname = NULL; + +static void +usage(void) +{ + + fprintf(stderr, "IPv4 broadcast test program. Sends a %d byte UDP " + "datagram to <dest>:<port>.\n\n", DEFAULT_PAYLOAD_SIZE); + fprintf(stderr, +"usage: %s [-1] [-A laddr] [-b] [-B] [-d] [-i iface] [-l len]\n" +" [-p port] [-R] [-s srcaddr] [-t ttl] <dest>\n", + progname); + fprintf(stderr, "-1: Set IP_ONESBCAST\n"); + fprintf(stderr, "-A: specify laddr (default: INADDR_ANY)\n"); + fprintf(stderr, "-b: bind socket to <laddr>:<lport>\n"); + fprintf(stderr, "-B: Set SO_BROADCAST\n"); + fprintf(stderr, "-d: Set SO_DONTROUTE\n"); + fprintf(stderr, "-i: Set IP_SENDIF <iface> (if supported)\n"); + fprintf(stderr, "-l: Set payload size to <len>\n"); + fprintf(stderr, "-p: Set local and remote port (default: %d)\n", + DEFAULT_PORT); + fprintf(stderr, "-R: Use raw IP (protocol %d)\n", IPPROTO_ZEROHOP); + fprintf(stderr, "-s: Set IP_SENDSRCADDR to <srcaddr>\n"); + fprintf(stderr, "-t: Set IP_TTL to <ttl>\n"); + + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + char *buf; + char cmsgbuf[MY_CMSG_SIZE]; + struct iovec iov[1]; + struct msghdr msg; + struct sockaddr_in dsin; + struct sockaddr_in laddr; + struct cmsghdr *cmsgp; + struct in_addr dstaddr; + char *ifname; + char *laddr_s; + char *srcaddr_s; + int ch; + int dobind; + int dobroadcast; + int dontroute; + int doonesbcast; + int dorawip; + size_t buflen; + ssize_t nbytes; + int portno; + int ret; + int s; + socklen_t soptlen; + int soptval; + int ttl; + + dobind = 0; + dobroadcast = 0; + dontroute = 0; + doonesbcast = 0; + dorawip = 0; + + ifname = NULL; + dstaddr.s_addr = INADDR_ANY; + laddr_s = NULL; + srcaddr_s = NULL; + portno = DEFAULT_PORT; + ttl = DEFAULT_TTL; + + buf = NULL; + buflen = DEFAULT_PAYLOAD_SIZE; + + progname = basename(argv[0]); + while ((ch = getopt(argc, argv, "1A:bBdi:l:p:Rs:t:")) != -1) { + switch (ch) { + case '1': + doonesbcast = 1; + break; + case 'A': + laddr_s = optarg; + break; + case 'b': + dobind = 1; + break; + case 'B': + dobroadcast = 1; + break; + case 'd': + dontroute = 1; + break; + case 'i': + ifname = optarg; + break; + case 'l': + buflen = atoi(optarg); + break; + case 'p': + portno = atoi(optarg); + break; + case 'R': + dorawip = 1; + break; + case 's': + srcaddr_s = optarg; + break; + case 't': + ttl = atoi(optarg); + break; + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + if (argv[0] == NULL || inet_aton(argv[0], &dstaddr) == 0) + usage(); + /* IP_SENDSRCADDR and IP_SENDIF are mutually exclusive just now. */ + if (srcaddr_s != NULL && ifname != NULL) + usage(); + if (dorawip) { + if (geteuid() != 0) + fprintf(stderr, "WARNING: not running as root.\n"); + s = socket(PF_INET, SOCK_RAW, IPPROTO_ZEROHOP); + } else { + s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + } + if (s == -1) { + perror("socket"); + exit(EXIT_FAILURE); + } + + if (dontroute) { + soptval = 1; + soptlen = sizeof(soptval); + ret = setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &soptval, + soptlen); + if (ret == -1) { + perror("setsockopt SO_DONTROUTE"); + close(s); + exit(EXIT_FAILURE); + } + } + + if (dobroadcast) { + soptval = 1; + soptlen = sizeof(soptval); + ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &soptval, + soptlen); + if (ret == -1) { + perror("setsockopt SO_BROADCAST"); + close(s); + exit(EXIT_FAILURE); + } + } + + soptval = ttl; + soptlen = sizeof(soptval); + ret = setsockopt(s, IPPROTO_IP, IP_TTL, &soptval, soptlen); + if (ret == -1) { + perror("setsockopt IPPROTO_IP IP_TTL"); + close(s); + exit(EXIT_FAILURE); + } + + if (doonesbcast) { + soptval = 1; + soptlen = sizeof(soptval); + ret = setsockopt(s, IPPROTO_IP, IP_ONESBCAST, &soptval, + soptlen); + if (ret == -1) { + perror("setsockopt IP_ONESBCAST"); + close(s); + exit(EXIT_FAILURE); + } + } + + if (dobind) { + memset(&laddr, 0, sizeof(struct sockaddr_in)); + laddr.sin_family = AF_INET; + laddr.sin_len = sizeof(struct sockaddr_in); + if (laddr_s != NULL) { + laddr.sin_addr.s_addr = inet_addr(laddr_s); + } else + laddr.sin_addr.s_addr = INADDR_ANY; + laddr.sin_port = htons(portno); + ret = bind(s, (struct sockaddr *)&laddr, sizeof(laddr)); + if (ret == -1) { + perror("bind"); + close(s); + exit(EXIT_FAILURE); + } + } + + memset(&dsin, 0, sizeof(struct sockaddr_in)); + dsin.sin_family = AF_INET; + dsin.sin_len = sizeof(struct sockaddr_in); + dsin.sin_addr.s_addr = dstaddr.s_addr; + dsin.sin_port = htons(portno); + + buf = malloc(buflen); + if (buf == NULL) { + perror("malloc"); + close(s); + exit(EXIT_FAILURE); + } + memset(iov, 0, sizeof(iov)); + iov[0].iov_base = buf; + iov[0].iov_len = buflen; + + memset(&msg, 0, sizeof(struct msghdr)); + msg.msg_name = &dsin; + msg.msg_namelen = sizeof(dsin); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + /* Assume we fill out a control msg; macros need to see buf ptr */ + msg.msg_control = cmsgbuf; + msg.msg_controllen = 0; + memset(cmsgbuf, 0, MY_CMSG_SIZE); + + /* IP_SENDSRCADDR and IP_SENDIF are mutually exclusive just now. */ + if (srcaddr_s != NULL) { + msg.msg_controllen += CMSG_SPACE(sizeof(struct in_addr)); + cmsgp = CMSG_FIRSTHDR(&msg); + cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + cmsgp->cmsg_level = IPPROTO_IP; + cmsgp->cmsg_type = IP_SENDSRCADDR; + memcpy(CMSG_DATA(cmsgp), + &(struct in_addr){ inet_addr(srcaddr_s) }, + sizeof(struct in_addr)); + } + + if (ifname != NULL) { +#ifdef IP_SENDIF + msg.msg_controllen += CMSG_SPACE(sizeof(struct sockaddr_dl)); + cmsgp = CMSG_FIRSTHDR(&msg); + cmsgp->cmsg_len = CMSG_LEN(sizeof(struct sockaddr_dl)); + cmsgp->cmsg_level = IPPROTO_IP; + cmsgp->cmsg_type = IP_SENDIF; + +#ifdef DIAGNOSTIC + fprintf(stderr, "DEBUG: cmsgp->cmsg_len is %d\n", + cmsgp->cmsg_len); +#endif + memcpy(CMSG_DATA(cmsgp), &(struct sockaddr_dl){ + .sdl_family = AF_LINK, + .sdl_len = sizeof(struct sockaddr_dl), + .sdl_index = if_nametoindex(ifname) }, + sizeof(struct sockaddr_dl)); +#ifdef DIAGNOSTIC + fprintf(stderr, "DEBUG: sdl->sdl_family is %d\n", + sdl->sdl_family); + fprintf(stderr, "DEBUG: sdl->sdl_len is %d\n", + sdl->sdl_len); + fprintf(stderr, "DEBUG: sdl->sdl_index is %d\n", + sdl->sdl_index); +#endif +#else + fprintf(stderr, "WARNING: IP_SENDIF not supported, ignored.\n"); +#endif + } + + if (msg.msg_controllen == 0) + msg.msg_control = NULL; + + nbytes = sendmsg(s, &msg, (dontroute ? MSG_DONTROUTE : 0)); + if (nbytes == -1) { + perror("sendmsg"); + close(s); + exit(EXIT_FAILURE); + } + + close(s); + + exit(EXIT_SUCCESS); +} diff --git a/tools/regression/netinet/ipdivert/Makefile b/tools/regression/netinet/ipdivert/Makefile new file mode 100644 index 000000000000..211741520165 --- /dev/null +++ b/tools/regression/netinet/ipdivert/Makefile @@ -0,0 +1,10 @@ +# +# + +PROG= ipdivert +SRCS= ipdivert.c +MAN= + +WARNS?= 2 + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/ipdivert/ipdivert.c b/tools/regression/netinet/ipdivert/ipdivert.c new file mode 100644 index 000000000000..80870c297d26 --- /dev/null +++ b/tools/regression/netinet/ipdivert/ipdivert.c @@ -0,0 +1,164 @@ +/*- + * Copyright (c) 2010-2011 Juniper Networks, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson under contract + * to Juniper Networks, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This is a test tool for IP divert sockets. For the time being, it just + * exercise creation and binding of sockets, rather than their divert + * behaviour. It would be highly desirable to broaden this test tool to + * include packet injection and diversion. + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static void +ok(const char *test) +{ + + fprintf(stderr, "%s: OK\n", test); +} + +static void +fail(const char *test, const char *note) +{ + + fprintf(stderr, "%s - %s: FAIL (%s)\n", test, note, strerror(errno)); + exit(1); +} + +static void +failx(const char *test, const char *note) +{ + + fprintf(stderr, "%s - %s: FAIL\n", test, note); + exit(1); +} + +static int +ipdivert_create(const char *test) +{ + int s; + + s = socket(PF_DIVERT, SOCK_RAW, 0); + if (s < 0) + fail(test, "socket"); + return (s); +} + +static void +ipdivert_close(const char *test, int s) +{ + + if (close(s) < 0) + fail(test, "close"); +} + +static void +ipdivert_bind(const char *test, int s, u_short port, int expect) +{ + struct sockaddr_in sin; + int err; + + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(port); + + err = bind(s, (struct sockaddr *)&sin, sizeof(sin)); + if (err < 0) { + if (expect == 0) + fail(test, "bind"); + if (errno != expect) + fail(test, "bind"); + } else { + if (expect != 0) + failx(test, "bind"); + } +} + +int +main(int argc, char *argv[]) +{ + const char *test; + int s1, s2; + + /* + * First test: create and close an IP divert socket. + */ + test = "create_close"; + s1 = ipdivert_create(test); + ipdivert_close(test, s1); + ok(test); + + /* + * Second test: create, bind, and close an IP divert socket. + */ + test = "create_bind_close"; + s1 = ipdivert_create(test); + ipdivert_bind(test, s1, 1000, 0); + ipdivert_close(test, s1); + ok(test); + + /* + * Third test: create two sockets, bind to different ports, and close. + * This should succeed due to non-conflict on the port numbers. + */ + test = "create2_bind2_close2"; + s1 = ipdivert_create(test); + s2 = ipdivert_create(test); + ipdivert_bind(test, s1, 1000, 0); + ipdivert_bind(test, s2, 1001, 0); + ipdivert_close(test, s1); + ipdivert_close(test, s2); + ok(test); + + /* + * Fourth test: create two sockets, bind to the *same* port, and + * close. This should fail due to conflicting port numbers. + */ + test = "create2_bind2_conflict_close2"; + s1 = ipdivert_create(test); + s2 = ipdivert_create(test); + ipdivert_bind(test, s1, 1000, 0); + ipdivert_bind(test, s2, 1000, EADDRINUSE); + ipdivert_close(test, s1); + ipdivert_close(test, s2); + ok(test); + + return (0); +} diff --git a/tools/regression/netinet/ipmulticast/Makefile b/tools/regression/netinet/ipmulticast/Makefile new file mode 100644 index 000000000000..0c12831aa399 --- /dev/null +++ b/tools/regression/netinet/ipmulticast/Makefile @@ -0,0 +1,10 @@ +# +# + +PROG= ipmulticast +SRCS= ipmulticast.c +MAN= + +WARNS?= 2 + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/ipmulticast/ipmulticast.c b/tools/regression/netinet/ipmulticast/ipmulticast.c new file mode 100644 index 000000000000..eb06c544a7ea --- /dev/null +++ b/tools/regression/netinet/ipmulticast/ipmulticast.c @@ -0,0 +1,784 @@ +/*- + * Copyright (c) 2007 Bruce M. Simpson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Regression test utility for RFC 3678 Advanced Multicast API in FreeBSD. + * + * TODO: Test the SSM paths. + * TODO: Support INET6. The code has been written to facilitate this later. + * TODO: Merge multicast socket option tests from ipsockopt. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <getopt.h> +#include <libgen.h> +#include <pwd.h> +#include <setjmp.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <time.h> +#include <unistd.h> + +#ifndef __SOCKUNION_DECLARED +union sockunion { + struct sockaddr_storage ss; + struct sockaddr sa; + struct sockaddr_dl sdl; + struct sockaddr_in sin; +#ifdef INET6 + struct sockaddr_in6 sin6; +#endif +}; +typedef union sockunion sockunion_t; +#define __SOCKUNION_DECLARED +#endif /* __SOCKUNION_DECLARED */ + +#define ADDRBUF_LEN 16 +#define DEFAULT_GROUP_STR "238.1.1.0" +#define DEFAULT_IFNAME "lo0" +#define DEFAULT_IFADDR_STR "127.0.0.1" +#define DEFAULT_PORT 6698 +#define DEFAULT_TIMEOUT 0 /* don't wait for traffic */ +#define RXBUFSIZE 2048 + +static sockunion_t basegroup; +static const char *basegroup_str = NULL; +static int dobindaddr = 0; +static int dodebug = 1; +static int doipv4 = 0; +static int domiscopts = 0; +static int dorandom = 0; +static int doreuseport = 0; +static int dossm = 0; +static int dossf = 0; +static int doverbose = 0; +static sockunion_t ifaddr; +static const char *ifaddr_str = NULL; +static uint32_t ifindex = 0; +static const char *ifname = NULL; +struct in_addr *ipv4_sources = NULL; +static jmp_buf jmpbuf; +static size_t nmcastgroups = IP_MAX_MEMBERSHIPS; +static size_t nmcastsources = 0; +static uint16_t portno = DEFAULT_PORT; +static char *progname = NULL; +struct sockaddr_storage *ss_sources = NULL; +static uint32_t timeout = 0; + +static int do_asm_ipv4(void); +static int do_asm_pim(void); +#ifdef notyet +static int do_misc_opts(void); +#endif +static int do_ssf_ipv4(void); +static int do_ssf_pim(void); +static int do_ssm_ipv4(void); +static int do_ssm_pim(void); +static int open_and_bind_socket(sockunion_t *); +static int recv_loop_with_match(int, sockunion_t *, sockunion_t *); +static void signal_handler(int); +static void usage(void); + +/* + * Test the IPv4 set/getipv4sourcefilter() libc API functions. + * Build a single socket. + * Join a source group. + * Repeatedly change the source filters via setipv4sourcefilter. + * Read it back with getipv4sourcefilter up to IP_MAX_SOURCES + * and check for inconsistency. + */ +static int +do_ssf_ipv4(void) +{ + + fprintf(stderr, "not yet implemented\n"); + return (0); +} + +/* + * Test the protocol-independent set/getsourcefilter() functions. + */ +static int +do_ssf_pim(void) +{ + + fprintf(stderr, "not yet implemented\n"); + return (0); +} + +/* + * Test the IPv4 ASM API. + * Repeatedly join, block sources, unblock and leave groups. + */ +static int +do_asm_ipv4(void) +{ + int error; + char gaddrbuf[ADDRBUF_LEN]; + int i; + sockunion_t laddr; + struct ip_mreq mreq; + struct ip_mreq_source mreqs; + in_addr_t ngroupbase; + char saddrbuf[ADDRBUF_LEN]; + int sock; + sockunion_t tmpgroup; + sockunion_t tmpsource; + + memset(&mreq, 0, sizeof(struct ip_mreq)); + memset(&mreqs, 0, sizeof(struct ip_mreq_source)); + memset(&laddr, 0, sizeof(sockunion_t)); + + if (dobindaddr) { + laddr = ifaddr; + } else { + laddr.sin.sin_family = AF_INET; + laddr.sin.sin_len = sizeof(struct sockaddr_in); + laddr.sin.sin_addr.s_addr = INADDR_ANY; + } + laddr.sin.sin_port = htons(portno); + + tmpgroup = basegroup; + ngroupbase = ntohl(basegroup.sin.sin_addr.s_addr) + 1; /* XXX */ + tmpgroup.sin.sin_addr.s_addr = htonl(ngroupbase); + + sock = open_and_bind_socket(&laddr); + if (sock == -1) + return (EX_OSERR); + + for (i = 0; i < (signed)nmcastgroups; i++) { + mreq.imr_multiaddr.s_addr = htonl((ngroupbase + i)); + mreq.imr_interface = ifaddr.sin.sin_addr; + if (doverbose) { + inet_ntop(AF_INET, &mreq.imr_multiaddr, gaddrbuf, + sizeof(gaddrbuf)); + fprintf(stderr, "IP_ADD_MEMBERSHIP %s %s\n", + gaddrbuf, inet_ntoa(mreq.imr_interface)); + } + error = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(struct ip_mreq)); + if (error < 0) { + warn("setsockopt IP_ADD_MEMBERSHIP"); + close(sock); + return (EX_OSERR); + } + } + + /* + * If no test sources auto-generated or specified on command line, + * skip source filter portion of ASM test. + */ + if (nmcastsources == 0) + goto skipsources; + + /* + * Begin blocking sources on the first group chosen. + */ + for (i = 0; i < (signed)nmcastsources; i++) { + mreqs.imr_multiaddr = tmpgroup.sin.sin_addr; + mreqs.imr_interface = ifaddr.sin.sin_addr; + mreqs.imr_sourceaddr = ipv4_sources[i]; + if (doverbose) { + inet_ntop(AF_INET, &mreqs.imr_multiaddr, gaddrbuf, + sizeof(gaddrbuf)); + inet_ntop(AF_INET, &mreqs.imr_sourceaddr, saddrbuf, + sizeof(saddrbuf)); + fprintf(stderr, "IP_BLOCK_SOURCE %s %s %s\n", + gaddrbuf, inet_ntoa(mreqs.imr_interface), + saddrbuf); + } + error = setsockopt(sock, IPPROTO_IP, IP_BLOCK_SOURCE, &mreqs, + sizeof(struct ip_mreq_source)); + if (error < 0) { + warn("setsockopt IP_BLOCK_SOURCE"); + close(sock); + return (EX_OSERR); + } + } + + /* + * Choose the first group and source for a match. + * Enter the I/O loop. + */ + memset(&tmpsource, 0, sizeof(sockunion_t)); + tmpsource.sin.sin_family = AF_INET; + tmpsource.sin.sin_len = sizeof(struct sockaddr_in); + tmpsource.sin.sin_addr = ipv4_sources[0]; + + error = recv_loop_with_match(sock, &tmpgroup, &tmpsource); + + /* + * Unblock sources. + */ + for (i = nmcastsources-1; i >= 0; i--) { + mreqs.imr_multiaddr = tmpgroup.sin.sin_addr; + mreqs.imr_interface = ifaddr.sin.sin_addr; + mreqs.imr_sourceaddr = ipv4_sources[i]; + if (doverbose) { + inet_ntop(AF_INET, &mreqs.imr_multiaddr, gaddrbuf, + sizeof(gaddrbuf)); + inet_ntop(AF_INET, &mreqs.imr_sourceaddr, saddrbuf, + sizeof(saddrbuf)); + fprintf(stderr, "IP_UNBLOCK_SOURCE %s %s %s\n", + gaddrbuf, inet_ntoa(mreqs.imr_interface), + saddrbuf); + } + error = setsockopt(sock, IPPROTO_IP, IP_UNBLOCK_SOURCE, &mreqs, + sizeof(struct ip_mreq_source)); + if (error < 0) { + warn("setsockopt IP_UNBLOCK_SOURCE"); + close(sock); + return (EX_OSERR); + } + } + +skipsources: + /* + * Leave groups. + */ + for (i = nmcastgroups-1; i >= 0; i--) { + mreq.imr_multiaddr.s_addr = htonl((ngroupbase + i)); + mreq.imr_interface = ifaddr.sin.sin_addr; + if (doverbose) { + inet_ntop(AF_INET, &mreq.imr_multiaddr, gaddrbuf, + sizeof(gaddrbuf)); + fprintf(stderr, "IP_DROP_MEMBERSHIP %s %s\n", + gaddrbuf, inet_ntoa(mreq.imr_interface)); + } + error = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, + &mreq, sizeof(struct ip_mreq)); + if (error < 0) { + warn("setsockopt IP_DROP_MEMBERSHIP"); + close(sock); + return (EX_OSERR); + } + } + + return (0); +} + +static int +do_asm_pim(void) +{ + + fprintf(stderr, "not yet implemented\n"); + return (0); +} + +#ifdef notyet +/* + * Test misceallaneous IPv4 options. + */ +static int +do_misc_opts(void) +{ + int sock; + + sock = open_and_bind_socket(NULL); + if (sock == -1) + return (EX_OSERR); + test_ip_uchar(sock, socktypename, IP_MULTICAST_TTL, + "IP_MULTICAST_TTL", 1); + close(sock); + + sock = open_and_bind_socket(NULL); + if (sock == -1) + return (EX_OSERR); + test_ip_boolean(sock, socktypename, IP_MULTICAST_LOOP, + "IP_MULTICAST_LOOP", 1, BOOLEAN_ANYONE); + close(sock); + + return (0); +} +#endif + +/* + * Test the IPv4 SSM API. + */ +static int +do_ssm_ipv4(void) +{ + + fprintf(stderr, "not yet implemented\n"); + return (0); +} + +/* + * Test the protocol-independent SSM API with IPv4 addresses. + */ +static int +do_ssm_pim(void) +{ + + fprintf(stderr, "not yet implemented\n"); + return (0); +} + +int +main(int argc, char *argv[]) +{ + struct addrinfo aih; + struct addrinfo *aip; + int ch; + int error; + int exitval; + size_t i; + struct in_addr *pina; + struct sockaddr_storage *pbss; + + ifname = DEFAULT_IFNAME; + ifaddr_str = DEFAULT_IFADDR_STR; + basegroup_str = DEFAULT_GROUP_STR; + ifname = DEFAULT_IFNAME; + portno = DEFAULT_PORT; + basegroup.ss.ss_family = AF_UNSPEC; + ifaddr.ss.ss_family = AF_UNSPEC; + + progname = basename(argv[0]); + while ((ch = getopt(argc, argv, "4bg:i:I:mM:p:rsS:tT:v")) != -1) { + switch (ch) { + case '4': + doipv4 = 1; + break; + case 'b': + dobindaddr = 1; + break; + case 'g': + basegroup_str = optarg; + break; + case 'i': + ifname = optarg; + break; + case 'I': + ifaddr_str = optarg; + break; + case 'm': + usage(); /* notyet */ + /*NOTREACHED*/ + domiscopts = 1; + break; + case 'M': + nmcastgroups = atoi(optarg); + break; + case 'p': + portno = atoi(optarg); + break; + case 'r': + doreuseport = 1; + break; + case 'S': + nmcastsources = atoi(optarg); + break; + case 's': + dossm = 1; + break; + case 't': + dossf = 1; + break; + case 'T': + timeout = atoi(optarg); + break; + case 'v': + doverbose = 1; + break; + default: + usage(); + break; + /*NOTREACHED*/ + } + } + argc -= optind; + argv += optind; + + memset(&aih, 0, sizeof(struct addrinfo)); + aih.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + aih.ai_family = PF_INET; + aih.ai_socktype = SOCK_DGRAM; + aih.ai_protocol = IPPROTO_UDP; + + /* + * Fill out base group. + */ + aip = NULL; + error = getaddrinfo(basegroup_str, NULL, &aih, &aip); + if (error != 0) { + fprintf(stderr, "%s: getaddrinfo: %s\n", progname, + gai_strerror(error)); + exit(EX_USAGE); + } + memcpy(&basegroup, aip->ai_addr, aip->ai_addrlen); + if (dodebug) { + fprintf(stderr, "debug: gai thinks %s is %s\n", + basegroup_str, inet_ntoa(basegroup.sin.sin_addr)); + } + freeaddrinfo(aip); + + assert(basegroup.ss.ss_family == AF_INET); + + /* + * If user specified interface as an address, and protocol + * specific APIs were selected, parse it. + * Otherwise, parse interface index from name if protocol + * independent APIs were selected (the default). + */ + if (doipv4) { + if (ifaddr_str == NULL) { + warnx("required argument missing: ifaddr"); + usage(); + /* NOTREACHED */ + } + aip = NULL; + error = getaddrinfo(ifaddr_str, NULL, &aih, &aip); + if (error != 0) { + fprintf(stderr, "%s: getaddrinfo: %s\n", progname, + gai_strerror(error)); + exit(EX_USAGE); + } + memcpy(&ifaddr, aip->ai_addr, aip->ai_addrlen); + if (dodebug) { + fprintf(stderr, "debug: gai thinks %s is %s\n", + ifaddr_str, inet_ntoa(ifaddr.sin.sin_addr)); + } + freeaddrinfo(aip); + } + + if (!doipv4) { + if (ifname == NULL) { + warnx("required argument missing: ifname"); + usage(); + /* NOTREACHED */ + } + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + err(EX_USAGE, "if_nametoindex"); + } + + /* + * Introduce randomness into group base if specified. + */ + if (dorandom) { + in_addr_t ngroupbase; + + srandomdev(); + ngroupbase = ntohl(basegroup.sin.sin_addr.s_addr); + ngroupbase |= ((random() % ((1 << 11) - 1)) << 16); + basegroup.sin.sin_addr.s_addr = htonl(ngroupbase); + } + + if (argc > 0) { + nmcastsources = argc; + if (doipv4) { + ipv4_sources = calloc(nmcastsources, + sizeof(struct in_addr)); + if (ipv4_sources == NULL) { + exitval = EX_OSERR; + goto out; + } + } else { + ss_sources = calloc(nmcastsources, + sizeof(struct sockaddr_storage)); + if (ss_sources == NULL) { + exitval = EX_OSERR; + goto out; + } + } + } + + /* + * Parse source list, if any were specified on the command line. + */ + assert(aih.ai_family == PF_INET); + pbss = ss_sources; + pina = ipv4_sources; + for (i = 0; i < (size_t)argc; i++) { + aip = NULL; + error = getaddrinfo(argv[i], NULL, &aih, &aip); + if (error != 0) { + fprintf(stderr, "getaddrinfo: %s\n", + gai_strerror(error)); + exitval = EX_USAGE; + goto out; + } + if (doipv4) { + struct sockaddr_in *sin = + (struct sockaddr_in *)aip->ai_addr; + *pina++ = sin->sin_addr; + } else { + memcpy(pbss++, aip->ai_addr, aip->ai_addrlen); + } + freeaddrinfo(aip); + } + + /* + * Perform the regression tests which the user requested. + */ +#ifdef notyet + if (domiscopts) { + exitval = do_misc_opts(); + if (exitval) + goto out; + } +#endif + if (doipv4) { + /* IPv4 protocol specific API tests */ + if (dossm) { + /* Source-specific multicast */ + exitval = do_ssm_ipv4(); + if (exitval) + goto out; + if (dossf) { + /* Do setipvsourcefilter() too */ + exitval = do_ssf_ipv4(); + } + } else { + /* Any-source multicast */ + exitval = do_asm_ipv4(); + } + } else { + /* Protocol independent API tests */ + if (dossm) { + /* Source-specific multicast */ + exitval = do_ssm_pim(); + if (exitval) + goto out; + if (dossf) { + /* Do setsourcefilter() too */ + exitval = do_ssf_pim(); + } + } else { + /* Any-source multicast */ + exitval = do_asm_pim(); + } + } + +out: + if (ipv4_sources != NULL) + free(ipv4_sources); + + if (ss_sources != NULL) + free(ss_sources); + + exit(exitval); +} + +static int +open_and_bind_socket(sockunion_t *bsu) +{ + int error, optval, sock; + + sock = -1; + + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + warn("socket"); + return (-1); + } + + if (doreuseport) { + optval = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval, + sizeof(optval)) < 0) { + warn("setsockopt SO_REUSEPORT"); + close(sock); + return (-1); + } + } + + if (bsu != NULL) { + error = bind(sock, &bsu->sa, bsu->sa.sa_len); + if (error == -1) { + warn("bind"); + close(sock); + return (-1); + } + } + + return (sock); +} + +/* + * Protocol-agnostic multicast I/O loop. + * + * Wait for 'timeout' seconds looking for traffic on group, so that manual + * or automated regression tests (possibly running on another host) have an + * opportunity to transmit within the group to test source filters. + * + * If the filter failed, this loop will report if we received traffic + * from the source we elected to monitor. + */ +static int +recv_loop_with_match(int sock, sockunion_t *group, sockunion_t *source) +{ + int error; + sockunion_t from; + char groupname[NI_MAXHOST]; + ssize_t len; + size_t npackets; + int jmpretval; + char rxbuf[RXBUFSIZE]; + char sourcename[NI_MAXHOST]; + + assert(source->sa.sa_family == AF_INET); + + /* + * Return immediately if we don't need to wait for traffic. + */ + if (timeout == 0) + return (0); + + error = getnameinfo(&group->sa, group->sa.sa_len, groupname, + NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (error) { + fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); + return (error); + } + + error = getnameinfo(&source->sa, source->sa.sa_len, sourcename, + NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (error) { + fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); + return (error); + } + + fprintf(stdout, + "Waiting %d seconds for inbound traffic on group %s\n" + "Expecting no traffic from blocked source: %s\n", + (int)timeout, groupname, sourcename); + + signal(SIGINT, signal_handler); + signal(SIGALRM, signal_handler); + + error = 0; + npackets = 0; + alarm(timeout); + while (0 == (jmpretval = setjmp(jmpbuf))) { + len = recvfrom(sock, rxbuf, RXBUFSIZE, 0, &from.sa, + (socklen_t *)&from.sa.sa_len); + if (dodebug) { + fprintf(stderr, "debug: packet received from %s\n", + inet_ntoa(from.sin.sin_addr)); + } + if (source && + source->sin.sin_addr.s_addr == from.sin.sin_addr.s_addr) + break; + npackets++; + } + + if (doverbose) { + fprintf(stderr, "Number of datagrams received from " + "non-blocked sources: %d\n", (int)npackets); + } + + switch (jmpretval) { + case SIGALRM: /* ok */ + break; + case SIGINT: /* go bye bye */ + fprintf(stderr, "interrupted\n"); + error = 20; + break; + case 0: /* Broke out of loop; saw a bad source. */ + fprintf(stderr, "FAIL: got packet from blocked source\n"); + error = EX_IOERR; + break; + default: + warnx("recvfrom"); + error = EX_OSERR; + break; + } + + signal(SIGINT, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + return (error); +} + +static void +signal_handler(int signo) +{ + + longjmp(jmpbuf, signo); +} + +static void +usage(void) +{ + + fprintf(stderr, "\nIP multicast regression test utility\n"); + fprintf(stderr, +"usage: %s [-4] [-b] [-g groupaddr] [-i ifname] [-I ifaddr] [-m]\n" +" [-M ngroups] [-p portno] [-r] [-R] [-s] [-S nsources] [-t] [-T timeout]\n" +" [-v] [blockaddr ...]\n\n", progname); + fprintf(stderr, "-4: Use IPv4 API " + "(default: Use protocol-independent API)\n"); + fprintf(stderr, "-b: bind listening socket to ifaddr " + "(default: INADDR_ANY)\n"); + fprintf(stderr, "-g: Base IPv4 multicast group to join (default: %s)\n", + DEFAULT_GROUP_STR); + fprintf(stderr, "-i: interface for multicast joins (default: %s)\n", + DEFAULT_IFNAME); + fprintf(stderr, "-I: IPv4 address to join groups on, if using IPv4 " + "API\n (default: %s)\n", DEFAULT_IFADDR_STR); +#ifdef notyet + fprintf(stderr, "-m: Test misc IPv4 multicast socket options " + "(default: off)\n"); +#endif + fprintf(stderr, "-M: Number of multicast groups to join " + "(default: %d)\n", (int)nmcastgroups); + fprintf(stderr, "-p: Set local and remote port (default: %d)\n", + DEFAULT_PORT); + fprintf(stderr, "-r: Set SO_REUSEPORT on (default: off)\n"); + fprintf(stderr, "-R: Randomize groups/sources (default: off)\n"); + fprintf(stderr, "-s: Test source-specific API " + "(default: test any-source API)\n"); + fprintf(stderr, "-S: Number of multicast sources to generate if\n" + " none specified on command line (default: %d)\n", + (int)nmcastsources); + fprintf(stderr, "-t: Test get/setNsourcefilter() (default: off)\n"); + fprintf(stderr, "-T: Timeout to wait for blocked traffic on first " + "group (default: %d)\n", DEFAULT_TIMEOUT); + fprintf(stderr, "-v: Be verbose (default: off)\n"); + fprintf(stderr, "\nRemaining arguments are treated as a list of IPv4 " + "sources to filter.\n\n"); + + exit(EX_USAGE); +} diff --git a/tools/regression/netinet/ipsockopt/Makefile b/tools/regression/netinet/ipsockopt/Makefile new file mode 100644 index 000000000000..a6b084118f04 --- /dev/null +++ b/tools/regression/netinet/ipsockopt/Makefile @@ -0,0 +1,7 @@ +# +# + +PROG= ipsockopt +MAN= + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/ipsockopt/ipsockopt.c b/tools/regression/netinet/ipsockopt/ipsockopt.c new file mode 100644 index 000000000000..fe74cedc5728 --- /dev/null +++ b/tools/regression/netinet/ipsockopt/ipsockopt.c @@ -0,0 +1,941 @@ +/*- + * Copyright (c) 2004 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static int dorandom = 0; +static int nmcastgroups = IP_MAX_MEMBERSHIPS; +static int verbose = 0; + +/* + * The test tool exercises IP-level socket options by interrogating the + * getsockopt()/setsockopt() APIs. It does not currently test that the + * intended semantics of each option are implemented (i.e., that setting IP + * options on the socket results in packets with the desired IP options in + * it). + */ + +/* + * get_socket() is a wrapper function that returns a socket of the specified + * type, and created with or without restored root privilege (if running + * with a real uid of root and an effective uid of some other user). This + * us to test whether the same rights are granted using a socket with a + * privileged cached credential vs. a socket with a regular credential. + */ +#define PRIV_ASIS 0 +#define PRIV_GETROOT 1 +static int +get_socket_unpriv(int type) +{ + + return (socket(PF_INET, type, 0)); +} + +static int +get_socket_priv(int type) +{ + uid_t olduid; + int sock; + + if (getuid() != 0) + errx(-1, "get_sock_priv: running without real uid 0"); + + olduid = geteuid(); + if (seteuid(0) < 0) + err(-1, "get_sock_priv: seteuid(0)"); + + sock = socket(PF_INET, type, 0); + + if (seteuid(olduid) < 0) + err(-1, "get_sock_priv: seteuid(%d)", olduid); + + return (sock); +} + +static int +get_socket(int type, int priv) +{ + + if (priv) + return (get_socket_priv(type)); + else + return (get_socket_unpriv(type)); +} + +/* + * Exercise the IP_OPTIONS socket option. Confirm the following properties: + * + * - That there is no initial set of options (length returned is 0). + * - That if we set a specific set of options, we can read it back. + * - That if we then reset the options, they go away. + * + * Use a UDP socket for this. + */ +static void +test_ip_options(int sock, const char *socktypename) +{ + u_int32_t new_options, test_options[2]; + socklen_t len; + + /* + * Start off by confirming the default IP options on a socket are to + * have no options set. + */ + len = sizeof(test_options); + if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) + err(-1, "test_ip_options(%s): initial getsockopt()", + socktypename); + + if (len != 0) + errx(-1, "test_ip_options(%s): initial getsockopt() returned " + "%d bytes", socktypename, len); + +#define TEST_MAGIC 0xc34e4212 +#define NEW_OPTIONS htonl(IPOPT_EOL | (IPOPT_NOP << 8) | (IPOPT_NOP << 16) \ + | (IPOPT_NOP << 24)) + + /* + * Write some new options into the socket. + */ + new_options = NEW_OPTIONS; + if (setsockopt(sock, IPPROTO_IP, IP_OPTIONS, &new_options, + sizeof(new_options)) < 0) + err(-1, "test_ip_options(%s): setsockopt(NOP|NOP|NOP|EOL)", + socktypename); + + /* + * Store some random cruft in a local variable and retrieve the + * options to make sure they set. Note that we pass in an array + * of u_int32_t's so that if whatever ended up in the option was + * larger than what we put in, we find out about it here. + */ + test_options[0] = TEST_MAGIC; + test_options[1] = TEST_MAGIC; + len = sizeof(test_options); + if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) + err(-1, "test_ip_options(%s): getsockopt() after set", + socktypename); + + /* + * Getting the right amount back is important. + */ + if (len != sizeof(new_options)) + errx(-1, "test_ip_options(%s): getsockopt() after set " + "returned %d bytes of data", socktypename, len); + + /* + * One possible failure mode is that the call succeeds but neglects to + * copy out the data. + */ + if (test_options[0] == TEST_MAGIC) + errx(-1, "test_ip_options(%s): getsockopt() after set didn't " + "return data", socktypename); + + /* + * Make sure we get back what we wrote on. + */ + if (new_options != test_options[0]) + errx(-1, "test_ip_options(%s): getsockopt() after set " + "returned wrong options (%08x, %08x)", socktypename, + new_options, test_options[0]); + + /* + * Now we reset the value to make sure clearing works. + */ + if (setsockopt(sock, IPPROTO_IP, IP_OPTIONS, NULL, 0) < 0) + err(-1, "test_ip_options(%s): setsockopt() to reset", + socktypename); + + /* + * Make sure it was really cleared. + */ + test_options[0] = TEST_MAGIC; + test_options[1] = TEST_MAGIC; + len = sizeof(test_options); + if (getsockopt(sock, IPPROTO_IP, IP_OPTIONS, test_options, &len) < 0) + err(-1, "test_ip_options(%s): getsockopt() after reset", + socktypename); + + if (len != 0) + errx(-1, "test_ip_options(%s): getsockopt() after reset " + "returned %d bytes", socktypename, len); +} + +/* + * This test checks the behavior of the IP_HDRINCL socket option, which + * allows users with privilege to specify the full header on an IP raw + * socket. We test that the option can only be used with raw IP sockets, not + * with UDP or TCP sockets. We also confirm that the raw socket is only + * available to a privileged user (subject to the UID when called). We + * confirm that it defaults to off + * + * Unlike other tests, doesn't use caller-provided socket. Probably should + * be fixed. + */ +static void +test_ip_hdrincl(void) +{ + int flag[2], sock; + socklen_t len; + + /* + * Try to receive or set the IP_HDRINCL flag on a TCP socket. + */ + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock == -1) + err(-1, "test_ip_hdrincl(): socket(SOCK_STREAM)"); + + flag[0] = -1; + len = sizeof(flag[0]); + if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) == 0) + err(-1, "test_ip_hdrincl(): initial getsockopt(IP_HDRINCL)"); + + if (errno != ENOPROTOOPT) + errx(-1, "test_ip_hdrincl(): initial getsockopt(IP_HDRINC) " + "returned %d (%s) not ENOPROTOOPT", errno, + strerror(errno)); + + flag[0] = 1; + if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) + == 0) + err(-1,"test_ip_hdrincl(): setsockopt(IP_HDRINCL) on TCP " + "succeeded\n"); + + if (errno != ENOPROTOOPT) + errx(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on TCP " + "returned %d (%s) not ENOPROTOOPT\n", errno, + strerror(errno)); + + close(sock); + + /* + * Try to receive or set the IP_HDRINCL flag on a UDP socket. + */ + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock == -1) + err(-1, "test_ip_hdrincl(): socket(SOCK_DGRAM"); + + flag[0] = -1; + len = sizeof(flag[0]); + if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) == 0) + err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on UDP " + "succeeded\n"); + + if (errno != ENOPROTOOPT) + errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on UDP " + "returned %d (%s) not ENOPROTOOPT\n", errno, + strerror(errno)); + + if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) + == 0) + err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on UDP " + "succeeded\n"); + + if (errno != ENOPROTOOPT) + errx(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL) on UDP " + "returned %d (%s) not ENOPROTOOPT\n", errno, + strerror(errno)); + + close(sock); + + /* + * Now try on a raw socket. Access ontrol should prevent non-root + * users from creating the raw socket, so check that here based on + * geteuid(). If we're non-root, we just return assuming the socket + * create fails since the remainder of the tests apply only on a raw + * socket. + */ + sock = socket(PF_INET, SOCK_RAW, 0); + if (geteuid() != 0) { + if (sock != -1) + errx(-1, "test_ip_hdrincl: created raw socket as " + "uid %d", geteuid()); + return; + } + if (sock == -1) + err(-1, "test_ip_hdrincl(): socket(PF_INET, SOCK_RAW)"); + + /* + * Make sure the initial value of the flag is 0 (disabled). + */ + flag[0] = -1; + flag[1] = -1; + len = sizeof(flag); + if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) + err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) on raw " + "socket"); + + if (len != sizeof(flag[0])) + errx(-1, "test_ip_hdrincl(): %d bytes returned on " + "initial get\n", len); + + if (flag[0] != 0) + errx(-1, "test_ip_hdrincl(): initial flag value of %d\n", + flag[0]); + + /* + * Enable the IP_HDRINCL flag. + */ + flag[0] = 1; + if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) + < 0) + err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL, 1)"); + + /* + * Check that the IP_HDRINCL flag was set. + */ + flag[0] = -1; + flag[1] = -1; + len = sizeof(flag); + if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) + err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) after " + "set"); + + if (flag[0] == 0) + errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) " + "after set had flag of %d\n", flag[0]); + +#define HISTORICAL_INP_HDRINCL 8 + if (flag[0] != HISTORICAL_INP_HDRINCL) + warnx("test_ip_hdrincl(): WARNING: getsockopt(IP_H" + "DRINCL) after set had non-historical value of %d\n", + flag[0]); + + /* + * Reset the IP_HDRINCL flag to 0. + */ + flag[0] = 0; + if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, sizeof(flag[0])) + < 0) + err(-1, "test_ip_hdrincl(): setsockopt(IP_HDRINCL, 0)"); + + /* + * Check that the IP_HDRINCL flag was reset to 0. + */ + flag[0] = -1; + flag[1] = -1; + len = sizeof(flag); + if (getsockopt(sock, IPPROTO_IP, IP_HDRINCL, flag, &len) < 0) + err(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) after " + "reset"); + + if (flag[0] != 0) + errx(-1, "test_ip_hdrincl(): getsockopt(IP_HDRINCL) " + "after set had flag of %d\n", flag[0]); + + close(sock); +} + +/* + * As with other non-int or larger sized socket options, the IP_TOS and + * IP_TTL fields in kernel is stored as an 8-bit value, reflecting the IP + * header fields, but useful I/O to the field occurs using 32-bit integers. + * The FreeBSD kernel will permit writes from variables at least an int in + * size (and ignore additional bytes), and will permit a read to buffers 1 + * byte or larger (but depending on endianness, may truncate out useful + * values if the caller provides less room). + * + * Given the limitations of the API, use a UDP socket to confirm that the + * following are true: + * + * - We can read the IP_TOS/IP_TTL options. + * - The initial value of the TOS option is 0, TTL is 64. + * - That if we provide more than 32 bits of storage, we get back only 32 + * bits of data. + * - When we set it to a non-zero value expressible with a u_char, we can + * read that value back. + * - When we reset it back to zero, we can read it as 0. + * - When we set it to a value >255, the value is truncated to something less + * than 255. + */ +static void +test_ip_uchar(int sock, const char *socktypename, int option, + const char *optionname, int initial) +{ + int val[2]; + socklen_t len; + + /* + * Check that the initial value is 0, and that the size is one + * u_char; + */ + val[0] = -1; + val[1] = -1; + len = sizeof(val); + if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) + err(-1, "test_ip_uchar(%s, %s): initial getsockopt()", + socktypename, optionname); + + if (len != sizeof(val[0])) + errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() " + "returned %d bytes", socktypename, optionname, len); + + if (val[0] == -1) + errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() didn't " + "return data", socktypename, optionname); + + if (val[0] != initial) + errx(-1, "test_ip_uchar(%s, %s): initial getsockopt() " + "returned value of %d, not %d", socktypename, optionname, + val[0], initial); + + /* + * Set the field to a valid value. + */ + val[0] = 128; + val[1] = -1; + if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) + err(-1, "test_ip_uchar(%s, %s): setsockopt(128)", + socktypename, optionname); + + /* + * Check that when we read back the field, we get the same value. + */ + val[0] = -1; + val[1] = -1; + len = sizeof(val); + if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) + err(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " + "128", socktypename, optionname); + + if (len != sizeof(val[0])) + errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " + "128 returned %d bytes", socktypename, optionname, len); + + if (val[0] == -1) + errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " + "128 didn't return data", socktypename, optionname); + + if (val[0] != 128) + errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " + "128 returned %d", socktypename, optionname, val[0]); + + /* + * Reset the value to 0, check that it was reset. + */ + val[0] = 0; + val[1] = 0; + if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) + err(-1, "test_ip_uchar(%s, %s): setsockopt() to reset from " + "128", socktypename, optionname); + + if (len != sizeof(val[0])) + errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " + "from 128 returned %d bytes", socktypename, optionname, + len); + + if (val[0] == -1) + errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " + "from 128 didn't return data", socktypename, optionname); + + if (val[0] != 0) + errx(-1, "test_ip_uchar(%s, %s): getsockopt() after reset " + "from 128 returned %d", socktypename, optionname, + val[0]); + + /* + * Set the value to something out of range and check that it comes + * back truncated, or that we get EINVAL back. Traditional u_char + * IP socket options truncate, but newer ones (such as multicast + * socket options) will return EINVAL. + */ + val[0] = 32000; + val[1] = -1; + if (setsockopt(sock, IPPROTO_IP, option, val, sizeof(val[0])) < 0) { + /* + * EINVAL is a fine outcome, no need to run the truncation + * tests. + */ + if (errno == EINVAL) + return; + err(-1, "test_ip_uchar(%s, %s): getsockopt(32000)", + socktypename, optionname); + } + + val[0] = -1; + val[1] = -1; + len = sizeof(val); + if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) + err(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " + "32000", socktypename, optionname); + + if (len != sizeof(val[0])) + errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " + "32000 returned %d bytes", socktypename, optionname, + len); + + if (val[0] == -1) + errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " + "32000 didn't return data", socktypename, optionname); + + if (val[0] == 32000) + errx(-1, "test_ip_uchar(%s, %s): getsockopt() after set to " + "32000 returned 32000: failed to truncate", socktypename, + optionname); +} + +/* + * Generic test for a boolean socket option. Caller provides the option + * number, string name, expected default (initial) value, and whether or not + * the option is root-only. For each option, test: + * + * - That we can read the option. + * - That the initial value is as expected. + * - That we can modify the value. + * - That on modification, the new value can be read back. + * - That we can reset the value. + * - that on reset, the new value can be read back. + */ +#define BOOLEAN_ANYONE 1 +#define BOOLEAN_ROOTONLY 1 +static void +test_ip_boolean(int sock, const char *socktypename, int option, + char *optionname, int initial, int rootonly) +{ + int newvalue, val[2]; + socklen_t len; + + /* + * The default for a boolean might be true or false. If it's false, + * we will try setting it to true (but using a non-1 value of true). + * If it's true, we'll set it to false. + */ + if (initial == 0) + newvalue = 0xff; + else + newvalue = 0; + + val[0] = -1; + val[1] = -1; + len = sizeof(val); + if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) + err(-1, "test_ip_boolean: initial getsockopt()"); + + if (len != sizeof(val[0])) + errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " + "returned %d bytes", socktypename, optionname, len); + + if (val[0] == -1) + errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " + "didn't return data", socktypename, optionname); + + if (val[0] != initial) + errx(-1, "test_ip_boolean(%s, %s): initial getsockopt() " + "returned %d (expected %d)", socktypename, optionname, + val[0], initial); + + /* + * Set the socket option to a new non-default value. + */ + if (setsockopt(sock, IPPROTO_IP, option, &newvalue, sizeof(newvalue)) + < 0) + err(-1, "test_ip_boolean(%s, %s): setsockopt() to %d", + socktypename, optionname, newvalue); + + /* + * Read the value back and see if it is not the default (note: will + * not be what we set it to, as we set it to 0xff above). + */ + val[0] = -1; + val[1] = -1; + len = sizeof(val); + if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) + err(-1, "test_ip_boolean(%s, %s): getsockopt() after set to " + "%d", socktypename, optionname, newvalue); + + if (len != sizeof(val[0])) + errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " + "to %d returned %d bytes", socktypename, optionname, + newvalue, len); + + if (val[0] == -1) + errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " + "to %d didn't return data", socktypename, optionname, + newvalue); + + /* + * If we set it to true, check for '1', otherwise '0. + */ + if (val[0] != (newvalue ? 1 : 0)) + errx(-1, "test_ip_boolean(%s, %s): getsockopt() after set " + "to %d returned %d", socktypename, optionname, newvalue, + val[0]); + + /* + * Reset to initial value. + */ + newvalue = initial; + if (setsockopt(sock, IPPROTO_IP, option, &newvalue, sizeof(newvalue)) + < 0) + err(-1, "test_ip_boolean(%s, %s): setsockopt() to reset", + socktypename, optionname); + + /* + * Check reset version. + */ + val[0] = -1; + val[1] = -1; + len = sizeof(val); + if (getsockopt(sock, IPPROTO_IP, option, val, &len) < 0) + err(-1, "test_ip_boolean(%s, %s): getsockopt() after reset", + socktypename, optionname); + + if (len != sizeof(val[0])) + errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " + "returned %d bytes", socktypename, optionname, len); + + if (val[0] == -1) + errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " + "didn't return data", socktypename, optionname); + + if (val[0] != newvalue) + errx(-1, "test_ip_boolean(%s, %s): getsockopt() after reset " + "returned %d", socktypename, optionname, newvalue); +} + +/* + * Test the IP_ADD_MEMBERSHIP socket option, and the dynamic allocator + * for the imo_membership vector which now hangs off struct ip_moptions. + * We then call IP_DROP_MEMBERSHIP for each group so joined. + */ +static void +test_ip_multicast_membership(int sock, const char *socktypename) +{ + char addrbuf[16]; + struct ip_mreq mreq; + uint32_t basegroup; + uint16_t i; + int sotype; + socklen_t sotypelen; + + sotypelen = sizeof(sotype); + if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &sotype, &sotypelen) < 0) + err(-1, "test_ip_multicast_membership(%s): so_type getsockopt()", + socktypename); + /* + * Do not perform the test for SOCK_STREAM sockets, as this makes + * no sense. + */ + if (sotype == SOCK_STREAM) + return; + /* + * The 224/8 range is administratively scoped and has special meaning, + * therefore it is not used for this test. + * If we were not told to be non-deterministic: + * Join multicast groups from 238.1.1.0 up to nmcastgroups. + * Otherwise, pick a multicast group ID in subnet 238/5 with 11 random + * bits in the middle, and join groups in linear order up to nmcastgroups. + */ + if (dorandom) { + /* be non-deterministic (for interactive operation; a fuller test) */ + srandomdev(); + basegroup = 0xEE000000; /* 238.0.0.0 */ + basegroup |= ((random() % ((1 << 11) - 1)) << 16); /* 11 bits */ + } else { + /* be deterministic (for automated operation) */ + basegroup = 0xEE010100; /* 238.1.1.0 */ + } + /* + * Join the multicast group(s) on the default multicast interface; + * this usually maps to the interface to which the default + * route is pointing. + */ + for (i = 1; i < nmcastgroups+1; i++) { + mreq.imr_multiaddr.s_addr = htonl((basegroup + i)); + mreq.imr_interface.s_addr = INADDR_ANY; + inet_ntop(AF_INET, &mreq.imr_multiaddr, addrbuf, sizeof(addrbuf)); + if (verbose) + fprintf(stderr, "IP_ADD_MEMBERSHIP %s INADDR_ANY\n", addrbuf); + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) < 0) { + err(-1, +"test_ip_multicast_membership(%d, %s): failed IP_ADD_MEMBERSHIP (%s, %s)", + sock, socktypename, addrbuf, "INADDR_ANY"); + } + } + for (i = 1; i < nmcastgroups+1; i++) { + mreq.imr_multiaddr.s_addr = htonl((basegroup + i)); + mreq.imr_interface.s_addr = INADDR_ANY; + inet_ntop(AF_INET, &mreq.imr_multiaddr, addrbuf, sizeof(addrbuf)); + if (verbose) + fprintf(stderr, "IP_DROP_MEMBERSHIP %s INADDR_ANY\n", addrbuf); + if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, + sizeof(mreq)) < 0) { + err(-1, +"test_ip_multicast_membership(%d, %s): failed IP_DROP_MEMBERSHIP (%s, %s)", + sock, socktypename, addrbuf, "INADDR_ANY"); + } + } +} + +/* + * XXX: For now, nothing here. + */ +static void +test_ip_multicast_if(int sock, const char *socktypename) +{ + + /* + * It's probably worth trying INADDR_ANY and INADDR_LOOPBACK here + * to see what happens. + */ +} + +/* + * XXX: For now, nothing here. + */ +static void +test_ip_multicast_vif(int sock, const char *socktypename) +{ + + /* + * This requires some knowledge of the number of virtual interfaces, + * and what is valid. + */ +} + +static void +testsuite(int priv) +{ + const char *socktypenameset[] = {"SOCK_DGRAM", "SOCK_STREAM", + "SOCK_RAW"}; + int socktypeset[] = {SOCK_DGRAM, SOCK_STREAM, SOCK_RAW}; + const char *socktypename; + int i, sock, socktype; + + test_ip_hdrincl(); + + for (i = 0; i < sizeof(socktypeset)/sizeof(int); i++) { + socktype = socktypeset[i]; + socktypename = socktypenameset[i]; + + /* + * If we can't acquire root privilege, we can't open raw + * sockets, so don't actually try. + */ + if (getuid() != 0 && socktype == SOCK_RAW) + continue; + if (geteuid() != 0 && !priv && socktype == SOCK_RAW) + continue; + + /* + * XXXRW: On 5.3, this seems not to work for SOCK_RAW. + */ + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for test_ip_uchar(IP_TOS)", + socktypename, priv); + test_ip_uchar(sock, socktypename, IP_TOS, "IP_TOS", 0); + close(sock); + + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s %d) for test_ip_uchar(IP_TTL)", + socktypename, priv); + test_ip_uchar(sock, socktypename, IP_TTL, "IP_TTL", 64); + close(sock); + + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for test_ip_boolean" + "(IP_RECVOPTS)", socktypename, priv); + test_ip_boolean(sock, socktypename, IP_RECVOPTS, + "IP_RECVOPTS", 0, BOOLEAN_ANYONE); + close(sock); + + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for test_ip_boolean" + "(IP_RECVRETOPTS)", socktypename, priv); + test_ip_boolean(sock, socktypename, IP_RECVRETOPTS, + "IP_RECVRETOPTS", 0, BOOLEAN_ANYONE); + close(sock); + + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for test_ip_boolean" + "(IP_RECVDSTADDR)", socktypename, priv); + test_ip_boolean(sock, socktypename, IP_RECVDSTADDR, + "IP_RECVDSTADDR", 0, BOOLEAN_ANYONE); + close(sock); + + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for test_ip_boolean" + "(IP_RECVTTL)", socktypename, priv); + test_ip_boolean(sock, socktypename, IP_RECVTTL, "IP_RECVTTL", + 0, BOOLEAN_ANYONE); + close(sock); + + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for test_ip_boolean" + "(IP_RECVIF)", socktypename, priv); + test_ip_boolean(sock, socktypename, IP_RECVIF, "IP_RECVIF", + 0, BOOLEAN_ANYONE); + close(sock); + + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for test_ip_boolean" + "(IP_FAITH)", socktypename, priv); + test_ip_boolean(sock, socktypename, IP_FAITH, "IP_FAITH", 0, + BOOLEAN_ANYONE); + close(sock); + + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for test_ip_boolean" + "(IP_ONESBCAST)", socktypename, priv); + test_ip_boolean(sock, socktypename, IP_ONESBCAST, + "IP_ONESBCAST", 0, BOOLEAN_ANYONE); + close(sock); + + /* + * Test the multicast TTL exactly as we would the regular + * TTL, only expect a different default. + */ + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for IP_MULTICAST_TTL", + socktypename, priv); + test_ip_uchar(sock, socktypename, IP_MULTICAST_TTL, + "IP_MULTICAST_TTL", 1); + close(sock); + + /* + * The multicast loopback flag can be tested using our + * boolean tester, but only because the FreeBSD API is a bit + * more flexible than earlir APIs and will accept an int as + * well as a u_char. Loopback is enabled by default. + */ + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for IP_MULTICAST_LOOP", + socktypename, priv); + test_ip_boolean(sock, socktypename, IP_MULTICAST_LOOP, + "IP_MULTICAST_LOOP", 1, BOOLEAN_ANYONE); + close(sock); + + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for test_ip_options", + socktypename, priv); + //test_ip_options(sock, socktypename); + close(sock); + + sock = get_socket(socktype, priv); + if (sock == -1) + err(-1, "get_socket(%s, %d) for test_ip_options", + socktypename, priv); + test_ip_multicast_membership(sock, socktypename); + close(sock); + + test_ip_multicast_if(0, NULL); + test_ip_multicast_vif(0, NULL); + /* + * XXX: Still need to test: + * IP_PORTRANGE + * IP_IPSEC_POLICY? + */ + } +} + +static void +usage() +{ + + fprintf(stderr, "usage: ipsockopt [-M ngroups] [-r] [-v]\n"); + exit(EXIT_FAILURE); +} + +/* + * Very simply exercise that we can get and set each option. If we're running + * as root, run it also as nobody. If not as root, complain about that. + */ +int +main(int argc, char *argv[]) +{ + int ch; + + while ((ch = getopt(argc, argv, "M:rv")) != -1) { + switch (ch) { + case 'M': + nmcastgroups = atoi(optarg); + break; + case 'r': + dorandom = 1; /* introduce non-determinism */ + break; + case 'v': + verbose = 1; + break; + default: + usage(); + } + } + + printf("1..1\n"); + + if (geteuid() != 0) { + warnx("Not running as root, can't run tests as root"); + fprintf(stderr, "\n"); + fprintf(stderr, + "Running tests with uid %d sock uid %d\n", geteuid(), + geteuid()); + testsuite(PRIV_ASIS); + } else { + fprintf(stderr, + "Running tests with ruid %d euid %d sock uid 0\n", + getuid(), geteuid()); + testsuite(PRIV_ASIS); + if (seteuid(65534) != 0) + err(-1, "seteuid(65534)"); + fprintf(stderr, + "Running tests with ruid %d euid %d sock uid 65534\n", + getuid(), geteuid()); + testsuite(PRIV_ASIS); + fprintf(stderr, + "Running tests with ruid %d euid %d sock uid 0\n", + getuid(), geteuid()); + testsuite(PRIV_GETROOT); + } + printf("ok 1 - ipsockopt\n"); + exit(0); +} diff --git a/tools/regression/netinet/ipsockopt/ipsockopt.t b/tools/regression/netinet/ipsockopt/ipsockopt.t new file mode 100644 index 000000000000..74eb8c9c581d --- /dev/null +++ b/tools/regression/netinet/ipsockopt/ipsockopt.t @@ -0,0 +1,9 @@ +#!/bin/sh + +cd `dirname $0` + +executable=`basename $0 .t` + +make $executable 2>&1 > /dev/null + +exec ./$executable diff --git a/tools/regression/netinet/msocket/Makefile b/tools/regression/netinet/msocket/Makefile new file mode 100644 index 000000000000..221b27d0dbf4 --- /dev/null +++ b/tools/regression/netinet/msocket/Makefile @@ -0,0 +1,5 @@ +PROG= msocket +MAN= +CFLAGS+= -Wall + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/msocket/msocket.c b/tools/regression/netinet/msocket/msocket.c new file mode 100644 index 000000000000..4c1033491637 --- /dev/null +++ b/tools/regression/netinet/msocket/msocket.c @@ -0,0 +1,462 @@ +/*- + * Copyright (c) 2005 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +/* + * Regression test for multicast sockets and options: + * + * - Check the defaults for ttl, if, and loopback. Make sure they can be set + * and then read. + * + * - Check that adding and removing multicast addresses seems to work. + * + * - Send a test message over loop back multicast and make sure it arrives. + * + * NB: + * + * Would be nice to use BPF or if_tap to actually check packet contents and + * layout, make sure that the ttl is set right, etc. + * + * Would be nice if attempts to use multicast options on TCP sockets returned + * an error, as the docs suggest it might. + */ + +#ifdef WARN_TCP +#define WARN_SUCCESS 0x00000001 /* Set for TCP to warn on success. */ +#else +#define WARN_SUCCESS 0x00000000 +#endif + +/* + * Multicast test address, picked arbitrarily. Will be used with the + * loopback interface. + */ +#define TEST_MADDR "224.100.100.100" + +/* + * Test that a given IP socket option (optname) has a default value of + * 'defaultv', that we can set it to 'modifiedv', and use 'fakev' as a dummy + * value that shouldn't be returned at any point during the tests. Perform + * the tests on the raw socket, tcp socket, and upd socket passed. + * 'optstring' is used in printing warnings and errors as needed. + */ +static void +test_u_char(int optname, const char *optstring, u_char defaultv, + u_char modifiedv, u_char fakev, const char *socktype, int sock, + int flags) +{ + socklen_t socklen; + u_char uc; + int ret; + + /* + * Check that we read back the expected default. + */ + uc = fakev; + socklen = sizeof(uc); + + ret = getsockopt(sock, IPPROTO_IP, optname, &uc, &socklen); + if (ret < 0) + err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)", + socktype, optstring); + if (ret == 0 && (flags & WARN_SUCCESS)) + warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0", + socktype, optstring); + if (uc != defaultv) + errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) default is " + "%d not %d", socktype, optstring, uc, defaultv); + + /* + * Set to a modifiedv value, read it back and make sure it got there. + */ + uc = modifiedv; + ret = setsockopt(sock, IPPROTO_IP, optname, &uc, sizeof(uc)); + if (ret == -1) + err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, %s)", + socktype, optstring); + if (ret == 0 && (flags & WARN_SUCCESS)) + warnx("WARN: setsockopt(%s, IPPROTO_IP, %s) returned 0", + socktype, optstring); + + uc = fakev; + socklen = sizeof(uc); + ret = getsockopt(sock, IPPROTO_IP, optname, &uc, &socklen); + if (ret < 0) + err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)", + socktype, optstring); + if (ret == 0 && (flags & WARN_SUCCESS)) + warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0", + socktype, optstring); + if (uc != modifiedv) + errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) set value is " + "%d not %d", socktype, optstring, uc, modifiedv); +} + +/* + * test_in_addr() is like test_u_char(), only it runs on a struct in_addr + * (surprise). + */ +static void +test_in_addr(int optname, const char *optstring, struct in_addr defaultv, + struct in_addr modifiedv, struct in_addr fakev, const char *socktype, + int sock, int flags) +{ + socklen_t socklen; + struct in_addr ia; + int ret; + + /* + * Check that we read back the expected default. + */ + ia = fakev; + socklen = sizeof(ia); + + ret = getsockopt(sock, IPPROTO_IP, optname, &ia, &socklen); + if (ret < 0) + err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)", + socktype, optstring); + if (ret == 0 && (flags & WARN_SUCCESS)) + warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0", + socktype, optstring); + if (memcmp(&ia, &defaultv, sizeof(struct in_addr))) + errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) default is " + "%s not %s", socktype, optstring, inet_ntoa(ia), + inet_ntoa(defaultv)); + + /* + * Set to a modifiedv value, read it back and make sure it got there. + */ + ia = modifiedv; + ret = setsockopt(sock, IPPROTO_IP, optname, &ia, sizeof(ia)); + if (ret == -1) + err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, %s)", + socktype, optstring); + if (ret == 0 && (flags & WARN_SUCCESS)) + warnx("WARN: setsockopt(%s, IPPROTO_IP, %s) returned 0", + socktype, optstring); + + ia = fakev; + socklen = sizeof(ia); + ret = getsockopt(sock, IPPROTO_IP, optname, &ia, &socklen); + if (ret < 0) + err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)", + socktype, optstring); + if (ret == 0 && (flags & WARN_SUCCESS)) + warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0", + socktype, optstring); + if (memcmp(&ia, &modifiedv, sizeof(struct in_addr))) + errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) set value is " + "%s not %s", socktype, optstring, inet_ntoa(ia), + inet_ntoa(modifiedv)); +} + +static void +test_ttl(int raw_sock, int tcp_sock, int udp_sock) +{ + + test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243, + "raw_sock", raw_sock, 0); + test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243, + "tcp_sock", tcp_sock, WARN_SUCCESS); + test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243, + "udp_sock", udp_sock, 0); +} + +static void +test_loop(int raw_sock, int tcp_sock, int udp_sock) +{ + + test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243, + "raw_sock", raw_sock, 0); + test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243, + "tcp_sock", tcp_sock, WARN_SUCCESS); + test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243, + "udp_sock", udp_sock, 0); +} + +static void +test_if(int raw_sock, int tcp_sock, int udp_sock) +{ + struct in_addr defaultv, modifiedv, fakev; + + defaultv.s_addr = inet_addr("0.0.0.0"); + + /* Should be valid on all hosts. */ + modifiedv.s_addr = inet_addr("127.0.0.1"); + + /* Should not happen. */ + fakev.s_addr = inet_addr("255.255.255.255"); + + test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv, + fakev, "raw_sock", raw_sock, 0); + test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv, + fakev, "tcp_sock", tcp_sock, WARN_SUCCESS); + test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv, + fakev, "udp_sock", udp_sock, 0); +} + +/* + * Add a multicast address to an interface. Warn if appropriate. No query + * interface so can't check if it's there directly; instead we have to try + * to add it a second time and make sure we get back EADDRINUSE. + */ +static void +test_add_multi(int sock, const char *socktype, struct ip_mreq imr, + int flags) +{ + char buf[128]; + int ret; + + ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, + sizeof(imr)); + if (ret < 0) { + strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128); + err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP " + "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface)); + } + if (ret == 0 && (flags & WARN_SUCCESS)) { + strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128); + warnx("WARN: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP " + "%s, %s) returned 0", socktype, buf, + inet_ntoa(imr.imr_interface)); + } + + /* Try to add a second time to make sure it got there. */ + ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, + sizeof(imr)); + if (ret == 0) { + strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128); + err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP " + "%s, %s) dup returned 0", socktype, buf, + inet_ntoa(imr.imr_interface)); + } + if (ret < 0 && errno != EADDRINUSE) { + strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128); + err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP " + "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface)); + } +} + +/* + * Drop a multicast address from an interface. Warn if appropriate. No + * query interface so can't check if it's gone directly; instead we have to + * try to drop it a second time and make sure we get back EADDRNOTAVAIL. + */ +static void +test_drop_multi(int sock, const char *socktype, struct ip_mreq imr, + int flags) +{ + char buf[128]; + int ret; + + ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, + sizeof(imr)); + if (ret < 0) { + strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128); + err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP " + "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface)); + } + if (ret == 0 && (flags & WARN_SUCCESS)) { + strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128); + warnx("WARN: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP " + "%s, %s) returned 0", socktype, buf, + inet_ntoa(imr.imr_interface)); + } + + /* Try a second time to make sure it's gone. */ + ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, + sizeof(imr)); + if (ret == 0) { + strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128); + err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP " + "%s, %s) returned 0", socktype, buf, + inet_ntoa(imr.imr_interface)); + } + if (ret < 0 && errno != EADDRNOTAVAIL) { + strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128); + err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP " + "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface)); + } +} + +/* + * Should really also test trying to add an invalid address, delete one + * that's not there, etc. + */ +static void +test_addr(int raw_sock, int tcp_sock, int udp_sock) +{ + struct ip_mreq imr; + + /* Arbitrary. */ + imr.imr_multiaddr.s_addr = inet_addr(TEST_MADDR); + + /* Localhost should be OK. */ + imr.imr_interface.s_addr = inet_addr("127.0.0.1"); + + test_add_multi(raw_sock, "raw_sock", imr, 0); + test_drop_multi(raw_sock, "raw_sock", imr, 0); + + test_add_multi(tcp_sock, "raw_sock", imr, WARN_SUCCESS); + test_drop_multi(tcp_sock, "raw_sock", imr, WARN_SUCCESS); + + test_add_multi(udp_sock, "raw_sock", imr, 0); + test_drop_multi(udp_sock, "raw_sock", imr, 0); +} + +/* + * Test an actual simple UDP message - send a single byte to an address we're + * subscribed to, and hope to get it back. We create a new UDP socket for + * this purpose because we will need to bind it. + */ +#define UDP_PORT 5012 +static void +test_udp(void) +{ + struct sockaddr_in sin; + struct ip_mreq imr; + struct in_addr if_addr; + char message; + ssize_t len; + int sock; + + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock < 0) + err(-1, "FAIL: test_udp: socket(PF_INET, SOCK_DGRAM)"); + + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) + err(-1, "FAIL: test_udp: fcntl(F_SETFL, O_NONBLOCK)"); + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(UDP_PORT); + sin.sin_addr.s_addr = inet_addr(TEST_MADDR); + + if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) + err(-1, "FAIL: test_udp: bind(udp_sock, 127.0.0.1:%d", + UDP_PORT); + + /* Arbitrary. */ + imr.imr_multiaddr.s_addr = inet_addr(TEST_MADDR); + + /* Localhost should be OK. */ + imr.imr_interface.s_addr = inet_addr("127.0.0.1"); + + /* + * Tell socket what interface to send on -- use localhost. + */ + if_addr.s_addr = inet_addr("127.0.0.1"); + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &if_addr, + sizeof(if_addr)) < 0) + err(-1, "test_udp: setsockopt(IPPROTO_IP, IP_MULTICAST_IF)"); + + test_add_multi(sock, "udp_sock", imr, 0); + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(UDP_PORT); + sin.sin_addr.s_addr = inet_addr(TEST_MADDR); + + message = 'A'; + len = sizeof(message); + len = sendto(sock, &message, len, 0, (struct sockaddr *)&sin, + sizeof(sin)); + if (len < 0) + err(-1, "test_udp: sendto"); + + if (len != sizeof(message)) + errx(-1, "test_udp: sendto: expected to send %d, instead %d", + sizeof(message), len); + + message = 'B'; + len = sizeof(sin); + len = recvfrom(sock, &message, sizeof(message), 0, + (struct sockaddr *)&sin, &len); + if (len < 0) + err(-1, "test_udp: recvfrom"); + + if (len != sizeof(message)) + errx(-1, "test_udp: recvfrom: len %d != message len %d", + len, sizeof(message)); + + if (message != 'A') + errx(-1, "test_udp: recvfrom: expected 'A', got '%c'", + message); + + test_drop_multi(sock, "udp_sock", imr, 0); + + close(sock); +} +#undef UDP_PORT + +int +main(int argc, char *argv[]) +{ + int raw_sock, tcp_sock, udp_sock; + + if (geteuid() != 0) + errx(-1, "FAIL: root privilege required"); + + raw_sock = socket(PF_INET, SOCK_RAW, 0); + if (raw_sock == -1) + err(-1, "FAIL: socket(PF_INET, SOCK_RAW)"); + + tcp_sock = socket(PF_INET, SOCK_STREAM, 0); + if (raw_sock == -1) + err(-1, "FAIL: socket(PF_INET, SOCK_STREAM)"); + + udp_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (raw_sock == -1) + err(-1, "FAIL: socket(PF_INET, SOCK_DGRAM)"); + + test_ttl(raw_sock, tcp_sock, udp_sock); + test_loop(raw_sock, tcp_sock, udp_sock); + test_if(raw_sock, tcp_sock, udp_sock); + test_addr(raw_sock, tcp_sock, udp_sock); + + close(udp_sock); + close(tcp_sock); + close(raw_sock); + + test_udp(); + + return (0); +} diff --git a/tools/regression/netinet/msocket_ifnet_remove/Makefile b/tools/regression/netinet/msocket_ifnet_remove/Makefile new file mode 100644 index 000000000000..08640d862ce4 --- /dev/null +++ b/tools/regression/netinet/msocket_ifnet_remove/Makefile @@ -0,0 +1,5 @@ +PROG= msocket_ifnet_remove +WARNS?= 3 +MAN= + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/msocket_ifnet_remove/msocket_ifnet_remove.c b/tools/regression/netinet/msocket_ifnet_remove/msocket_ifnet_remove.c new file mode 100644 index 000000000000..f75ae3630569 --- /dev/null +++ b/tools/regression/netinet/msocket_ifnet_remove/msocket_ifnet_remove.c @@ -0,0 +1,242 @@ +/*- + * Copyright (c) 2005 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/linker.h> +#include <sys/socket.h> + +#include <net/if.h> + +#include <netinet/in.h> + +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +/* + * Regression test to reproduce problems associated with the removal of a + * network interface being used by an active multicast socket. This proves + * to be somewhat complicated, as we need a multicast-capable synthetic + * network device that can be torn down on demand, in order that the test + * program can open a multicast socket, join a group on the interface, tear + * down the interface, and then close the multicast socket. We use the + * if_disc ("discard") synthetic interface for this purpose. + * + * Because potential solutions to this problem require separate handling for + * different IP socket types, we actually run the test twice: once for UDP + * sockets, and once for raw IP sockets. + */ + +/* + * XXX: The following hopefully don't conflict with the local configuration. + */ +#define MULTICAST_IP "224.100.100.100" +#define DISC_IP "192.0.2.100" +#define DISC_MASK "255.255.255.0" +#define DISC_IFNAME "disc" +#define DISC_IFUNIT 100 + +static int +disc_setup(void) +{ + struct ifreq ifr; + int s; + + if (kldload("if_disc") < 0) { + switch (errno) { + case EEXIST: + break; + default: + warn("disc_setup: kldload(if_disc)"); + return (-1); + } + } + + s = socket(PF_INET, SOCK_RAW, 0); + if (s < 0) { + warn("disc_setup: socket(PF_INET, SOCK_RAW, 0)"); + return (-1); + } + + bzero(&ifr, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", DISC_IFNAME, + DISC_IFUNIT); + + if (ioctl(s, SIOCIFCREATE, &ifr) < 0) { + warn("disc_setup: ioctl(%s, SIOCIFCREATE)", ifr.ifr_name); + close(s); + return (-1); + } + + close(s); + return (0); +} + +static void +disc_done(void) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_RAW, 0); + if (s < 0) { + warn("disc_done: socket(PF_INET, SOCK_RAW, 0)"); + return; + } + + bzero(&ifr, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", DISC_IFNAME, + DISC_IFUNIT); + + if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) + warn("disc_done: ioctl(%s, SIOCIFDESTROY)", ifr.ifr_name); + close(s); +} + +/* + * Configure an IP address and netmask on a network interface. + */ +static int +ifconfig_inet(char *ifname, int ifunit, char *ip, char *netmask) +{ + struct sockaddr_in *sinp; + struct ifaliasreq ifra; + int s; + + s = socket(PF_INET, SOCK_RAW, 0); + if (s < 0) { + warn("ifconfig_inet: socket(PF_INET, SOCK_RAW, 0)"); + return (-1); + } + + bzero(&ifra, sizeof(ifra)); + snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "%s%d", ifname, + ifunit); + + sinp = (struct sockaddr_in *)&ifra.ifra_addr; + sinp->sin_family = AF_INET; + sinp->sin_len = sizeof(ifra.ifra_addr); + sinp->sin_addr.s_addr = inet_addr(ip); + + sinp = (struct sockaddr_in *)&ifra.ifra_mask; + sinp->sin_family = AF_INET; + sinp->sin_len = sizeof(ifra.ifra_addr); + sinp->sin_addr.s_addr = inet_addr(netmask); + + if (ioctl(s, SIOCAIFADDR, &ifra) < 0) { + warn("ifconfig_inet: ioctl(%s%d, SIOCAIFADDR, %s)", ifname, + ifunit, ip); + close(s); + return (-1); + } + + close(s); + return (0); +} + +static int +multicast_open(int *sockp, int type, const char *type_string) +{ + struct ip_mreq imr; + int sock; + + sock = socket(PF_INET, type, 0); + if (sock < 0) { + warn("multicast_test: socket(PF_INET, %s, 0)", type_string); + return (-1); + } + + bzero(&imr, sizeof(imr)); + imr.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP); + imr.imr_interface.s_addr = inet_addr(DISC_IP); + + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, + sizeof(imr)) < 0) { + warn("multicast_test: setsockopt(IPPROTO_IP, " + "IP_ADD_MEMBERSHIP, {%s, %s})", MULTICAST_IP, DISC_IP); + close(sock); + return (-1); + } + + *sockp = sock; + return (0); +} + +static void +multicast_close(int udp_socket) +{ + + close(udp_socket); +} + +static int +test_sock_type(int type, const char *type_string) +{ + int sock; + + if (disc_setup() < 0) + return (-1); + + if (ifconfig_inet(DISC_IFNAME, DISC_IFUNIT, DISC_IP, DISC_MASK) < 0) { + disc_done(); + return (-1); + } + + if (multicast_open(&sock, type, type_string) < 0) { + disc_done(); + return (-1); + } + + /* + * Tear down the interface first, then close the multicast socket and + * see if we make it to the end of the function. + */ + disc_done(); + multicast_close(sock); + + printf("test_sock_type(%s) passed\n", type_string); + + return (0); +} + +int +main(int argc, char *argv[]) +{ + + if (test_sock_type(SOCK_RAW, "SOCK_RAW") < 0) + return (-1); + + if (test_sock_type(SOCK_DGRAM, "SOCK_DGRAM") < 0) + return (-1); + + return (0); +} diff --git a/tools/regression/netinet/rawconnect/Makefile b/tools/regression/netinet/rawconnect/Makefile new file mode 100644 index 000000000000..bd19e1c3bfe7 --- /dev/null +++ b/tools/regression/netinet/rawconnect/Makefile @@ -0,0 +1,7 @@ +# +# + +PROG= rawconnect +MAN= + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/rawconnect/rawconnect.c b/tools/regression/netinet/rawconnect/rawconnect.c new file mode 100644 index 000000000000..fbdb80db92dc --- /dev/null +++ b/tools/regression/netinet/rawconnect/rawconnect.c @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2006 Maxim Konovalov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Bug in IP code panics the system at close(2) on + * connected SOCK_RAW, IPPROTO_IP socket. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> + +static void +usage(void) +{ + + fprintf(stderr, "rawconnect: no arguments\n"); + exit(1); +} + +int +main(int argc, __unused char *argv[]) +{ + struct sockaddr_in sin; + int sock; + + if (argc != 1) + usage(); + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_LOOPBACK; + sin.sin_port = htons(65534); + + sock = socket(PF_INET, SOCK_RAW, IPPROTO_IP); + if (sock == -1) + err(1, "socket"); + + if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) + err(1, "connect"); + + close(sock); + + return (0); +} diff --git a/tools/regression/netinet/rawconnect/rawconnect.t b/tools/regression/netinet/rawconnect/rawconnect.t new file mode 100644 index 000000000000..2c4f9de40dd1 --- /dev/null +++ b/tools/regression/netinet/rawconnect/rawconnect.t @@ -0,0 +1,21 @@ +#!/bin/sh + +cd `dirname $0` + +executable=`basename $0 .t` + +make $executable 2>&1 > /dev/null + +echo 1..1 + +comment="rawconnect # open raw ip socket, connect it and then close" + +uid=`id -u` + +if [ $uid -ne 0 ]; then + echo "ok 1 - rawconnect # skipped: you need to be root to run this test" +elif ./$executable; then + echo "ok 1 - $comment" +else + echo "not ok 1 - $comment" +fi diff --git a/tools/regression/netinet/tcpconnect/Makefile b/tools/regression/netinet/tcpconnect/Makefile new file mode 100644 index 000000000000..1326489ded89 --- /dev/null +++ b/tools/regression/netinet/tcpconnect/Makefile @@ -0,0 +1,7 @@ +# +# + +PROG= tcpconnect +MAN= + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/tcpconnect/README.tcp-md5 b/tools/regression/netinet/tcpconnect/README.tcp-md5 new file mode 100644 index 000000000000..a3f6fbfbbe00 --- /dev/null +++ b/tools/regression/netinet/tcpconnect/README.tcp-md5 @@ -0,0 +1,29 @@ + +To test tcp-md5 do: + +* compile and install kernel with TCP_SIGNATURE support + +* add this to /etc/ipsec.conf (the md5 'secret' is just a sample) + add 127.0.0.1 127.0.0.1 tcp 0x1000 -A tcp-md5 "0e3a9ac42ceca8260f1d6fbc46a9707c"; + +* enable it in /etc/rc.conf with + ipsec_enable="YES" + and apply it with sh /etc/rc.d/ipsec start + + [ off course you can also manually add it using setkey(8) ] + +* compile tcpconnect in here running: + make + +* start tcpdump (secret as above, port is just a sample): + tcpdump -l -n -i lo0 -s 0 -M "0e3a9ac42ceca8260f1d6fbc46a9707c" tcp and port 2345 + +* run the server (use same port as given to tcpdump): + ./tcpconnect server 2345 + +* run the client (use same port as given to tcpdump): + ./tcpconnect client 127.0.0.1 2345 1 tcpmd5 + +* check tcpdump output + +# end diff --git a/tools/regression/netinet/tcpconnect/tcpconnect.c b/tools/regression/netinet/tcpconnect/tcpconnect.c new file mode 100644 index 000000000000..6de76e803e50 --- /dev/null +++ b/tools/regression/netinet/tcpconnect/tcpconnect.c @@ -0,0 +1,169 @@ +/*- + * Copyright (c) 2004-2005 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <netinet/tcp.h> + +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +static void +usage(void) +{ + + fprintf(stderr, "tcpconnect server port\n"); + fprintf(stderr, "tcpconnect client ip port count [nonblock] [tcpmd5]\n"); + exit(-1); +} + +static void +tcpconnect_server(int argc, char *argv[]) +{ + int listen_sock, accept_sock; + struct sockaddr_in sin; + char *dummy; + long port; + + if (argc != 1) + usage(); + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + + port = strtoul(argv[0], &dummy, 10); + if (port < 1 || port > 65535 || *dummy != '\0') + usage(); + sin.sin_port = htons(port); + + listen_sock = socket(PF_INET, SOCK_STREAM, 0); + if (listen_sock == -1) + err(-1, "socket"); + + if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) + err(-1, "bind"); + + if (listen(listen_sock, -1) == -1) + err(-1, "listen"); + + while (1) { + accept_sock = accept(listen_sock, NULL, NULL); + close(accept_sock); + } +} + +static void +tcpconnect_client(int argc, char *argv[]) +{ + struct sockaddr_in sin; + long count, i, port; + char *dummy; + int sock; + int nonblock = 0, md5enable = 0; + + if (argc < 3 || argc > 5) + usage(); + for (i=3; i < argc; i++) { + if (strcmp(argv[i], "nonblock") == 0) + nonblock = 1; + if (strcmp(argv[i], "tcpmd5") == 0) + md5enable = 1; + } + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + if (inet_aton(argv[0], &sin.sin_addr) == 0) + err(-1, "listen"); + + port = strtoul(argv[1], &dummy, 10); + if (port < 1 || port > 65535 || *dummy != '\0') + usage(); + sin.sin_port = htons(port); + + count = strtoul(argv[2], &dummy, 10); + if (count < 1 || count > 100000 || *dummy != '\0') + usage(); + + for (i = 0; i < count; i++) { + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock == -1) + err(-1, "socket"); + + /* No warning in default case on ENOPROTOOPT. */ + if (setsockopt(sock, IPPROTO_TCP, TCP_MD5SIG, + &md5enable, sizeof(md5enable)) != 0) { + if (errno == ENOPROTOOPT && md5enable > 0) + err(-1, "setsockopt(TCP_MD5SIG)"); + else if (errno != ENOPROTOOPT) + warn("setsockopt(TCP_MD5SIG)"); + } + + if (nonblock) { + if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) + err(-1, "fcntl(F_SETFL)"); + + if (connect(sock, (struct sockaddr *)&sin, + sizeof(sin)) == -1 && errno != EINPROGRESS) + err(-1, "connect"); + } else { + if (connect(sock, (struct sockaddr *)&sin, + sizeof(sin)) == -1) + err(-1, "connect"); + } + + close(sock); + } +} + +int +main(int argc, char *argv[]) +{ + + if (argc < 2) + usage(); + + if (strcmp(argv[1], "server") == 0) + tcpconnect_server(argc - 2, argv + 2); + else if (strcmp(argv[1], "client") == 0) + tcpconnect_client(argc - 2, argv + 2); + else + usage(); + + exit(0); +} diff --git a/tools/regression/netinet/tcpdrop/Makefile b/tools/regression/netinet/tcpdrop/Makefile new file mode 100644 index 000000000000..30c51c21909f --- /dev/null +++ b/tools/regression/netinet/tcpdrop/Makefile @@ -0,0 +1,8 @@ +# +# + +PROG= tcpdrop +MAN= +WARNS?= 3 + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/tcpdrop/tcpdrop.c b/tools/regression/netinet/tcpdrop/tcpdrop.c new file mode 100644 index 000000000000..95052f909fd4 --- /dev/null +++ b/tools/regression/netinet/tcpdrop/tcpdrop.c @@ -0,0 +1,254 @@ +/*- + * Copyright (c) 2006 Robert N. M. Watson + * Copyright (c) 2011 Juniper Networks, Inc. + * All rights reserved. + * + * Portions of this software were developed by Robert N. M. Watson under + * contract to Juniper Networks, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * TCP regression test for the tcpdrop sysctl; build a loopback TCP + * connection, drop it, and make sure both endpoints return that the + * connection has been reset. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> + +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static int +tcp_drop(struct sockaddr_in *sin_local, struct sockaddr_in *sin_remote) +{ + struct sockaddr_storage addrs[2]; + + /* + * Sysctl accepts an array of two sockaddr's, the first being the + * 'foreign' sockaddr, the second being the 'local' sockaddr. + */ + + bcopy(sin_remote, &addrs[0], sizeof(*sin_remote)); + bcopy(sin_local, &addrs[1], sizeof(*sin_local)); + + return (sysctlbyname("net.inet.tcp.drop", NULL, 0, addrs, + sizeof(addrs))); +} + +static void +tcp_server(pid_t partner, int listen_fd) +{ + int error, accept_fd; + ssize_t len; + char ch; + + accept_fd = accept(listen_fd, NULL, NULL); + if (accept_fd < 0) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "tcp_server: accept"); + } + + /* + * Send one byte, make sure that worked, wait for the drop, and try + * sending another. By sending small amounts, we avoid blocking + * waiting on the remote buffer to be drained. + */ + ch = 'A'; + len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL); + if (len < 0) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "tcp_server: send (1)"); + } + if (len != sizeof(ch)) { + (void)kill(partner, SIGTERM); + errx(-1, "tcp_server: send (1) len"); + } + + sleep (10); + + ch = 'A'; + len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL); + if (len >= 0) { + (void)kill(partner, SIGTERM); + errx(-1, "tcp_server: send (2): success"); + } else if (errno != EPIPE) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "tcp_server: send (2)"); + } + + close(accept_fd); + close(listen_fd); +} + +static void +tcp_client(pid_t partner, u_short port) +{ + struct sockaddr_in sin, sin_local; + int error, sock; + socklen_t slen; + ssize_t len; + char ch; + + sleep(1); + + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock < 0) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "socket"); + } + + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK); + sin.sin_port = port; + + if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "connect"); + } + + slen = sizeof(sin_local); + if (getsockname(sock, (struct sockaddr *)&sin_local, &slen) < 0) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "getsockname"); + } + + /* + * Send one byte, make sure that worked, wait for the drop, and try + * sending another. By sending small amounts, we avoid blocking + * waiting on the remote buffer to be drained. + */ + ch = 'A'; + len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL); + if (len < 0) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "tcp_client: send (1)"); + } + if (len != sizeof(ch)) { + (void)kill(partner, SIGTERM); + errx(-1, "tcp_client: send (1) len"); + } + + sleep(5); + if (tcp_drop(&sin_local, &sin) < 0) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "tcp_client: tcp_drop"); + } + sleep(5); + + ch = 'A'; + len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL); + if (len >= 0) { + (void)kill(partner, SIGTERM); + errx(-1, "tcp_client: send (2): success"); + } else if (errno != EPIPE) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "tcp_client: send (2)"); + } + close(sock); +} + +int +main(int argc, char *argv[]) +{ + pid_t child_pid, parent_pid; + struct sockaddr_in sin; + int listen_fd; + u_short port; + socklen_t len; + + listen_fd = socket(PF_INET, SOCK_STREAM, 0); + if (listen_fd < 0) + err(-1, "socket"); + + /* + * We use the loopback, but let the kernel select a port for the + * server socket. + */ + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) + err(-1, "bind"); + + if (listen(listen_fd, -1) < 0) + err(-1, "listen"); + + /* + * Query the port so that the client can use it. + */ + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + len = sizeof(sin); + if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0) + err(-1, "getsockname"); + port = sin.sin_port; + printf("Using port %d\n", ntohs(port)); + + if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) + err(-1, "signal"); + + parent_pid = getpid(); + child_pid = fork(); + if (child_pid < 0) + err(-1, "fork"); + if (child_pid == 0) { + child_pid = getpid(); + tcp_server(parent_pid, listen_fd); + } else + tcp_client(child_pid, port); + + return (0); +} diff --git a/tools/regression/netinet/tcpfullwindowrst/Makefile b/tools/regression/netinet/tcpfullwindowrst/Makefile new file mode 100644 index 000000000000..ad401e9df1cd --- /dev/null +++ b/tools/regression/netinet/tcpfullwindowrst/Makefile @@ -0,0 +1,4 @@ +PROG= tcpfullwindowrsttest +MAN= + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/tcpfullwindowrst/tcpfullwindowrst.t b/tools/regression/netinet/tcpfullwindowrst/tcpfullwindowrst.t new file mode 100644 index 000000000000..457255e2a76b --- /dev/null +++ b/tools/regression/netinet/tcpfullwindowrst/tcpfullwindowrst.t @@ -0,0 +1,6 @@ +#!/bin/sh +# + +make tcpfullwindowrsttest 2>&1 > /dev/null + +./tcpfullwindowrsttest diff --git a/tools/regression/netinet/tcpfullwindowrst/tcpfullwindowrsttest.c b/tools/regression/netinet/tcpfullwindowrst/tcpfullwindowrsttest.c new file mode 100644 index 000000000000..964892f5e380 --- /dev/null +++ b/tools/regression/netinet/tcpfullwindowrst/tcpfullwindowrsttest.c @@ -0,0 +1,130 @@ +/* +Copyright 2004 Michiel Boland. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <fcntl.h> +#include <poll.h> +#include <unistd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* + * The following code sets up two connected TCP sockets that send data to each + * other until the window is closed. Then one of the sockets is closed, which + * will generate a RST once the TCP at the other socket does a window probe. + * + * All versions of FreeBSD prior to 11/26/2004 will ignore this RST into a 0 + * window, causing the connection (and application) to hang indefinitely. + * On patched versions of FreeBSD (and other operating systems), the RST + * will be accepted and the program will exit in a few seconds. + */ + +/* + * If the alarm fired then we've hung and the test failed. + */ +void +do_alrm(int s) +{ + printf("not ok 1 - tcpfullwindowrst\n"); + exit(0); +} + +int +main(void) +{ + int o, s, t, u, do_t, do_u; + struct pollfd pfd[2]; + struct sockaddr_in sa; + char buf[4096]; + + printf("1..1\n"); + signal(SIGALRM, do_alrm); + alarm(20); + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s == -1) + return 1; + o = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &o, sizeof o); + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sa.sin_port = htons(3737); + if (bind(s, (struct sockaddr *) &sa, sizeof sa) == -1) + return 1; + if (listen(s, 1) == -1) + return 1; + t = socket(AF_INET, SOCK_STREAM, 0); + if (t == -1) + return 1; + if (connect(t, (struct sockaddr *) &sa, sizeof sa) == -1) + return 1; + u = accept(s, 0, 0); + if (u == -1) + return 1; + close(s); + fcntl(t, F_SETFL, fcntl(t, F_GETFL) | O_NONBLOCK); + fcntl(u, F_SETFL, fcntl(t, F_GETFL) | O_NONBLOCK); + do_t = 1; + do_u = 1; + pfd[0].fd = t; + pfd[0].events = POLLOUT; + pfd[1].fd = u; + pfd[1].events = POLLOUT; + while (do_t || do_u) { + if (poll(pfd, 2, 1000) == 0) { + if (do_t) { + close(t); + pfd[0].fd = -1; + do_t = 0; + } + continue; + } + if (pfd[0].revents & POLLOUT) { + if (write(t, buf, sizeof buf) == -1) { + close(t); + pfd[0].fd = -1; + do_t = 0; + } + } + if (pfd[1].revents & POLLOUT) { + if (write(u, buf, sizeof buf) == -1) { + close(u); + pfd[1].fd = -1; + do_u = 0; + } + } + } + + printf("ok 1 - tcpfullwindowrst\n"); + return 0; +} diff --git a/tools/regression/netinet/tcpsockclosebeforeaccept/Makefile b/tools/regression/netinet/tcpsockclosebeforeaccept/Makefile new file mode 100644 index 000000000000..ad26320b6529 --- /dev/null +++ b/tools/regression/netinet/tcpsockclosebeforeaccept/Makefile @@ -0,0 +1,8 @@ +# +# + +PROG= tcpsockclosebeforeaccept +MAN= +WARNS?= 3 + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/tcpsockclosebeforeaccept/tcpsockclosebeforeaccept.c b/tools/regression/netinet/tcpsockclosebeforeaccept/tcpsockclosebeforeaccept.c new file mode 100644 index 000000000000..b9180108a823 --- /dev/null +++ b/tools/regression/netinet/tcpsockclosebeforeaccept/tcpsockclosebeforeaccept.c @@ -0,0 +1,199 @@ +/*- + * Copyright (c) 2006 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * TCP regression test which opens a loopback TCP session, and closes it + * before the remote endpoint (server) can accept it. Run the test twice, + * once using an explicit close() from the client, a second using a tcp drop. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> + +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define TCP_PORT 9005 + +static int +tcp_drop(struct sockaddr_in *sin_local, struct sockaddr_in *sin_remote) +{ + struct sockaddr_storage addrs[2]; + + /* + * Sysctl accepts an array of two sockaddr's, the first being the + * 'foreign' sockaddr, the second being the 'local' sockaddr. + */ + + bcopy(sin_remote, &addrs[0], sizeof(*sin_remote)); + bcopy(sin_local, &addrs[1], sizeof(*sin_local)); + + return (sysctlbyname("net.inet.tcp.drop", NULL, 0, addrs, + sizeof(addrs))); +} + + +static void +tcp_server(pid_t partner) +{ + int error, listen_fd, accept_fd; + struct sockaddr_in sin; + + listen_fd = socket(PF_INET, SOCK_STREAM, 0); + if (listen_fd < 0) { + error = errno; + (void)kill(partner, SIGKILL); + errno = error; + err(-1, "tcp_server: socket"); + } + + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = htons(TCP_PORT); + + if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + error = errno; + (void)kill(partner, SIGKILL); + errno = error; + err(-1, "tcp_server: bind"); + } + + if (listen(listen_fd, -1) < 0) { + error = errno; + (void)kill(partner, SIGKILL); + errno = error; + err(-1, "tcp_server: listen"); + } + + sleep(10); + + accept_fd = accept(listen_fd, NULL, NULL); + if (accept_fd < 0) { + error = errno; + (void)kill(partner, SIGKILL); + errno = error; + err(-1, "tcp_server: accept"); + } + close(accept_fd); + close(listen_fd); +} + +static void +tcp_client(pid_t partner, int dropflag) +{ + struct sockaddr_in sin, sin_local; + int error, sock; + socklen_t slen; + + sleep(1); + + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock < 0) { + error = errno; + (void)kill(partner, SIGKILL); + errno = error; + err(-1, "socket"); + } + + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK); + sin.sin_port = htons(TCP_PORT); + + if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + error = errno; + (void)kill(partner, SIGKILL); + errno = error; + err(-1, "connect"); + } + + slen = sizeof(sin_local); + if (getsockname(sock, (struct sockaddr *)&sin_local, &slen) < 0) { + error = errno; + (void)kill(partner, SIGKILL); + errno = error; + err(-1, "getsockname"); + } + + if (dropflag) { + if (tcp_drop(&sin_local, &sin) < 0) { + error = errno; + (void)kill(partner, SIGKILL); + errno = error; + err(-1, "tcp_drop"); + } + sleep(2); + } + close(sock); +} + +int +main(int argc, char *argv[]) +{ + pid_t child_pid, parent_pid; + + if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) + err(-1, "signal"); + + parent_pid = getpid(); + child_pid = fork(); + if (child_pid < 0) + err(-1, "fork"); + if (child_pid == 0) { + child_pid = getpid(); + tcp_server(parent_pid); + return (0); + } else + tcp_client(child_pid, 0); + (void)kill(child_pid, SIGTERM); + + sleep(5); + + parent_pid = getpid(); + child_pid = fork(); + if (child_pid < 0) + err(-1, "fork"); + if (child_pid == 0) { + child_pid = getpid(); + tcp_server(parent_pid); + return (0); + } else + tcp_client(child_pid, 1); + (void)kill(child_pid, SIGTERM); + + return (0); +} diff --git a/tools/regression/netinet/tcpsocktimewait/Makefile b/tools/regression/netinet/tcpsocktimewait/Makefile new file mode 100644 index 000000000000..5ab67d506bf0 --- /dev/null +++ b/tools/regression/netinet/tcpsocktimewait/Makefile @@ -0,0 +1,8 @@ +# +# + +PROG= tcpsocktimewait +MAN= +WARNS?= 3 + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/tcpsocktimewait/tcpsocktimewait.c b/tools/regression/netinet/tcpsocktimewait/tcpsocktimewait.c new file mode 100644 index 000000000000..2b54aa925eb8 --- /dev/null +++ b/tools/regression/netinet/tcpsocktimewait/tcpsocktimewait.c @@ -0,0 +1,215 @@ +/*- + * Copyright (c) 2006 Robert N. M. Watson + * Copyright (c) 2011 Juniper Networks, Inc. + * All rights reserved. + * + * Portions of this software were developed by Robert N. M. Watson under + * contract to Juniper Networks, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * TCP regression test that opens a loopback TCP session, then closes one end + * while shutting down the other. This triggers an unusual TCP stack case in + * which an open file descriptor / socket is associated with a closed TCP + * connection. + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static void +tcp_server(pid_t partner, int listen_fd) +{ + int error, accept_fd; + + accept_fd = accept(listen_fd, NULL, NULL); + if (accept_fd < 0) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "tcp_server: accept"); + } + close(accept_fd); + close(listen_fd); +} + +static void +tcp_client(pid_t partner, u_short port, int secs) +{ + struct sockaddr_in sin; + int error, sock; + + sleep(1); + + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock < 0) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "socket"); + } + + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK); + sin.sin_port = port; + + if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "connect"); + } + + if (shutdown(sock, SHUT_RDWR) < 0) { + error = errno; + (void)kill(partner, SIGTERM); + errno = error; + err(-1, "shutdown"); + } + + sleep(secs); + close(sock); +} + +int +main(int argc, char *argv[]) +{ + struct sockaddr_in sin; + pid_t child_pid, parent_pid; + int listen_fd; + socklen_t len; + u_short port; + + if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) + err(-1, "signal"); + + /* + * Run the whole thing twice: once, with a short sleep in the client, + * so that we close before time wait runs out, and once with a long + * sleep so that the time wait terminates while the socket is open. + * We don't reuse listen sockets between runs. + */ + listen_fd = socket(PF_INET, SOCK_STREAM, 0); + if (listen_fd < 0) + err(-1, "socket"); + + /* + * We use the loopback, but let the kernel select a port for the + * server socket. + */ + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) + err(-1, "bind"); + + if (listen(listen_fd, -1) < 0) + err(-1, "listen"); + + /* + * Query the port so that the client can use it. + */ + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + len = sizeof(sin); + if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0) + err(-1, "getsockname"); + port = sin.sin_port; + printf("Using port %d\n", ntohs(port)); + + parent_pid = getpid(); + child_pid = fork(); + if (child_pid < 0) + err(-1, "fork"); + if (child_pid == 0) { + child_pid = getpid(); + tcp_server(child_pid, listen_fd); + exit(0); + } else + tcp_client(parent_pid, port, 1); + (void)kill(child_pid, SIGTERM); + close(listen_fd); + sleep(5); + + /* + * Start again, this time long sleep. + */ + listen_fd = socket(PF_INET, SOCK_STREAM, 0); + if (listen_fd < 0) + err(-1, "socket"); + + /* + * We use the loopback, but let the kernel select a port for the + * server socket. + */ + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) + err(-1, "bind"); + + if (listen(listen_fd, -1) < 0) + err(-1, "listen"); + + /* + * Query the port so that the client can use it. + */ + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + len = sizeof(sin); + if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0) + err(-1, "getsockname"); + port = sin.sin_port; + printf("Using port %d\n", ntohs(port)); + + parent_pid = getpid(); + child_pid = fork(); + if (child_pid < 0) + err(-1, "fork"); + if (child_pid == 0) { + child_pid = getpid(); + tcp_server(parent_pid, listen_fd); + } else + tcp_client(child_pid, port, 800); + + return (0); +} diff --git a/tools/regression/netinet/tcpstream/Makefile b/tools/regression/netinet/tcpstream/Makefile new file mode 100644 index 000000000000..e5bfa82b5ddf --- /dev/null +++ b/tools/regression/netinet/tcpstream/Makefile @@ -0,0 +1,8 @@ +# +# + +CFLAGS+= -Wall +PROG= tcpstream +MAN= + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/tcpstream/README b/tools/regression/netinet/tcpstream/README new file mode 100644 index 000000000000..4ac11ae0e70d --- /dev/null +++ b/tools/regression/netinet/tcpstream/README @@ -0,0 +1,18 @@ +tcpstream - a simple TCP streaming test tool +-------------------------------------------- + +tcpstream generates TCP connections between a 'client' and a 'server'. The +client writes a pseudo-random byte stream using varying write sizes. The +server then reads the stream and uses the same generator to confirm that the +data is correct. To run tcpstream, select a seed value (should be the same +for the client and server), and a port number (also the same for client and +server). Typical use might be: + +Run the server on port 8080 and use a seed of 100: + + tcpstream server 8080 100 + +Now run the client on a second machine with the server's IP, port 8080, and +seed of 100: + + tcpstream client 192.168.10.10 8080 100 diff --git a/tools/regression/netinet/tcpstream/tcpstream.c b/tools/regression/netinet/tcpstream/tcpstream.c new file mode 100644 index 000000000000..8bdd2bd2e305 --- /dev/null +++ b/tools/regression/netinet/tcpstream/tcpstream.c @@ -0,0 +1,231 @@ +/*- + * Copyright (c) 2004 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * tcpstream sets up a simple TCP client and server, and then streams a + * predictable pseudo-random byte sequence through it using variable block + * sizes. The intent is to detect corruption of data in the TCP stream. + */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define MAX_LOOPS 10240 +#define MAX_LONGS 1024 + +static void +usage(void) +{ + + fprintf(stderr, "tcpstream client [ip] [port] [seed]\n"); + fprintf(stderr, "tcpstream server [port] [seed]\n"); + exit(-1); +} + +static void +fill_buffer(long *buffer, int len) +{ + int i; + + for (i = 0; i < len; i++) + buffer[i] = htonl(random()); +} + +static int +check_buffer(long *buffer, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (buffer[i] != htonl(random())) + return (0); + } + return (1); +} + +static void +tcpstream_client(struct sockaddr_in sin, long seed) +{ + long buffer[MAX_LONGS]; + ssize_t len; + int i, j, sock; + + srandom(seed); + + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock == -1) + errx(-1, "socket: %s", strerror(errno)); + + if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) == -1) + errx(-1, "connect: %s", strerror(errno)); + + for (j = 0; j < MAX_LOOPS; j++) { + for (i = 0; i < MAX_LONGS; i++) { + fill_buffer(buffer, i); + len = send(sock, buffer, i * sizeof(long), 0); + if (len == -1) { + printf("%d bytes written of %d expected\n", + len, i * sizeof(long)); + fflush(stdout); + perror("send"); + goto done; + } + } + } + +done: + close(sock); +} + +static void +tcpstream_server(struct sockaddr_in sin, long seed) +{ + int i, j, listen_sock, accept_sock; + struct sockaddr_in other_sin; + long buffer[MAX_LONGS]; + socklen_t addrlen; + ssize_t len; + + int input_byte_counter; + + listen_sock = socket(PF_INET, SOCK_STREAM, 0); + if (listen_sock == -1) + errx(-1, "socket: %s", strerror(errno)); + + if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) + errx(-1, "bind: %s", strerror(errno)); + + if (listen(listen_sock, -1) == -1) + errx(-1, "listen: %s", strerror(errno)); + + while (1) { + bzero(&other_sin, sizeof(other_sin)); + addrlen = sizeof(other_sin); + + accept_sock = accept(listen_sock, (struct sockaddr *) + &other_sin, &addrlen); + if (accept_sock == -1) { + perror("accept"); + continue; + } + printf("connection opened from %s:%d\n", + inet_ntoa(other_sin.sin_addr), ntohs(other_sin.sin_port)); + input_byte_counter = 0; + + srandom(seed); + + for (j = 0; j < MAX_LOOPS; j++) { + for (i = 0; i < MAX_LONGS; i++) { + len = recv(accept_sock, buffer, + i * sizeof(long), MSG_WAITALL); + if (len != i * sizeof(long)) { + perror("recv"); + goto done; + } + if (check_buffer(buffer, i) == 0) { + fprintf(stderr, + "Corruption in block beginning %d and ending %d\n", input_byte_counter, + input_byte_counter + len); + fprintf(stderr, + "Block size %d\n", i * sizeof(long)); + goto done; + } + input_byte_counter += len; + } + } +done: + printf("connection closed\n"); + close(accept_sock); + } +} + +int +main(int argc, char *argv[]) +{ + struct sockaddr_in sin; + long port, seed; + char *dummy; + + if (argc < 2) + usage(); + if (strcmp(argv[1], "client") == 0) { + if (argc != 5) + usage(); + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + + if (inet_aton(argv[2], &sin.sin_addr) != 1) + errx(-1, "%s: %s", argv[2], strerror(EINVAL)); + + port = strtoul(argv[3], &dummy, 10); + if (port < 1 || port > 65535 || *dummy != '\0') + usage(); + sin.sin_port = htons(port); + + seed = strtoul(argv[4], &dummy, 10); + if (*dummy != '\0') + usage(); + + tcpstream_client(sin, seed); + + } else if (strcmp(argv[1], "server") == 0) { + if (argc != 4) + usage(); + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + + port = strtoul(argv[2], &dummy, 10); + if (port < 1 || port > 65535 || *dummy != '\0') + usage(); + sin.sin_port = htons(port); + + seed = strtoul(argv[3], &dummy, 10); + if (*dummy != '\0') + usage(); + + tcpstream_server(sin, seed); + } else + usage(); + + return (0); +} diff --git a/tools/regression/netinet/udpconnectjail/Makefile b/tools/regression/netinet/udpconnectjail/Makefile new file mode 100644 index 000000000000..88a14afb91d8 --- /dev/null +++ b/tools/regression/netinet/udpconnectjail/Makefile @@ -0,0 +1,5 @@ +PROG= udpconnectjail +MAN= +WARNS?= 2 + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/udpconnectjail/udpconnectjail.c b/tools/regression/netinet/udpconnectjail/udpconnectjail.c new file mode 100644 index 000000000000..28d81aa1b1db --- /dev/null +++ b/tools/regression/netinet/udpconnectjail/udpconnectjail.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 2005 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/jail.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* + * A bug in the jail(8) code prevented processes in jail from properly + * connecting UDP sockets. This test program attempts to exercise that bug. + */ + +static void +usage(void) +{ + + fprintf(stderr, "udpconnectjail: no arguments\n"); + exit(-1); +} + +static void +test(const char *context, struct sockaddr_in *sin) +{ + int sock; + + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock == -1) + errx(-1, "%s: socket(PF_INET, SOCK_DGRAM, 0): %s", context, + strerror(errno)); + + if (connect(sock, (struct sockaddr *)sin, sizeof(*sin)) < 0) + errx(-1, "%s: connect(%s): %s", context, + inet_ntoa(sin->sin_addr), strerror(errno)); + + if (close(sock) < 0) + errx(-1, "%s: close(): %s", context, strerror(errno)); +} + +int +main(int argc, __unused char *argv[]) +{ + struct sockaddr_in sin; + struct jail thejail; + struct in_addr ia4; + + if (argc != 1) + usage(); + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = htons(8080); /* Arbitrary */ + + /* + * First run the system call test outside of a jail. + */ + test("not in jail", &sin); + + /* + * Now re-run in a jail. + * XXX-BZ should switch to jail_set(2). + */ + ia4.s_addr = htonl(INADDR_LOOPBACK); + + bzero(&thejail, sizeof(thejail)); + thejail.version = JAIL_API_VERSION; + thejail.path = "/"; + thejail.hostname = "jail"; + thejail.jailname = "udpconnectjail"; + thejail.ip4s = 1; + thejail.ip4 = &ia4; + + if (jail(&thejail) < 0) + errx(-1, "jail: %s", strerror(errno)); + test("in jail", &sin); + + fprintf(stdout, "PASS\n"); + + return (0); +} diff --git a/tools/regression/netinet/udpzerobyte/Makefile b/tools/regression/netinet/udpzerobyte/Makefile new file mode 100644 index 000000000000..3cba327d2bd3 --- /dev/null +++ b/tools/regression/netinet/udpzerobyte/Makefile @@ -0,0 +1,5 @@ +PROG= udpzerobyte +MAN= +WARNS?= 2 + +.include <bsd.prog.mk> diff --git a/tools/regression/netinet/udpzerobyte/udpzerobyte.c b/tools/regression/netinet/udpzerobyte/udpzerobyte.c new file mode 100644 index 000000000000..98f31598ea35 --- /dev/null +++ b/tools/regression/netinet/udpzerobyte/udpzerobyte.c @@ -0,0 +1,143 @@ +/*- + * Copyright (c) 2008 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <netinet/in.h> + +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +/* + * The UDP code allows transmitting zero-byte datagrams, but are they + * received? + */ + +#define THEPORT 9543 /* Arbitrary. */ + +static void +usage(void) +{ + + errx(-1, "no arguments allowed\n"); +} + +static void +test(int domain, const char *domainstr, struct sockaddr *sa, socklen_t salen) +{ + int sock_send, sock_receive; + ssize_t size; + + sock_send = socket(domain, SOCK_DGRAM, 0); + if (sock_send < 0) + err(-1, "socket(%s, SOCK_DGRAM, 0)", domainstr); + + sock_receive = socket(domain, SOCK_DGRAM, 0); + if (sock_receive < 0) + err(-1, "socket(%s, SOCK_DGRAM, 0)", domainstr); + + if (bind(sock_receive, sa, salen) < 0) + err(-1, "Protocol %s bind(sock_receive)", domainstr); + if (fcntl(sock_receive, F_SETFL, O_NONBLOCK, 1) < 0) + err(-1, "Protocll %s fcntl(sock_receive, FL_SETFL, " + "O_NONBLOCK)", domainstr); + + if (connect(sock_send, sa, salen) < 0) + err(-1, "Protocol %s connect(sock_send)", domainstr); + + size = recv(sock_receive, NULL, 0, 0); + if (size > 0) + errx(-1, "Protocol %s recv(sock_receive, NULL, 0) before: %zd", + domainstr, size); + else if (size < 0) + err(-1, "Protocol %s recv(sock_receive, NULL, 0) before", + domainstr); + + size = send(sock_send, NULL, 0, 0); + if (size < 0) + err(-1, "Protocol %s send(sock_send, NULL, 0)", domainstr); + + (void)sleep(1); + size = recv(sock_receive, NULL, 0, 0); + if (size < 0) + err(-1, "Protocol %s recv(sock_receive, NULL, 0) test", + domainstr); + + size = recv(sock_receive, NULL, 0, 0); + if (size > 0) + errx(-1, "Protocol %s recv(sock_receive, NULL, 0) after: %zd", + domainstr, size); + else if (size < 0) + err(-1, "Protocol %s recv(sock_receive, NULL, 0) after", + domainstr); +} + +int +main(int argc, __unused char *argv[]) +{ + struct sockaddr_un sun; + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + struct in6_addr loopback6addr = IN6ADDR_LOOPBACK_INIT; + + if (argc != 1) + usage(); + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = htons(THEPORT); + + test(PF_INET, "PF_INET", (struct sockaddr *)&sin, sizeof(sin)); + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = loopback6addr; + sin6.sin6_port = htons(THEPORT); + + test(PF_INET6, "PF_INET6", (struct sockaddr *)&sin6, sizeof(sin6)); + + bzero(&sun, sizeof(sun)); + sun.sun_len = sizeof(sun); + sun.sun_family = AF_LOCAL; + strlcpy(sun.sun_path, "/tmp/udpzerosize-socket", sizeof(sun.sun_path)); + if (unlink(sun.sun_path) < 0 && errno != ENOENT) + err(-1, "unlink: %s", sun.sun_path); + + test(PF_LOCAL, "PF_LOCAL", (struct sockaddr *)&sun, sizeof(sun)); + + return (0); +} |
