aboutsummaryrefslogtreecommitdiff
path: root/tools/regression/netinet
diff options
context:
space:
mode:
Diffstat (limited to 'tools/regression/netinet')
-rw-r--r--tools/regression/netinet/arphold/Makefile5
-rw-r--r--tools/regression/netinet/arphold/arphold.c161
-rw-r--r--tools/regression/netinet/arphold/arphold.t6
-rw-r--r--tools/regression/netinet/ip_id_period/ip_id_period.py76
-rw-r--r--tools/regression/netinet/ipbroadcast/Makefile7
-rw-r--r--tools/regression/netinet/ipbroadcast/ipbroadcast.c352
-rw-r--r--tools/regression/netinet/ipdivert/Makefile10
-rw-r--r--tools/regression/netinet/ipdivert/ipdivert.c164
-rw-r--r--tools/regression/netinet/ipmulticast/Makefile10
-rw-r--r--tools/regression/netinet/ipmulticast/ipmulticast.c784
-rw-r--r--tools/regression/netinet/ipsockopt/Makefile7
-rw-r--r--tools/regression/netinet/ipsockopt/ipsockopt.c941
-rw-r--r--tools/regression/netinet/ipsockopt/ipsockopt.t9
-rw-r--r--tools/regression/netinet/msocket/Makefile5
-rw-r--r--tools/regression/netinet/msocket/msocket.c462
-rw-r--r--tools/regression/netinet/msocket_ifnet_remove/Makefile5
-rw-r--r--tools/regression/netinet/msocket_ifnet_remove/msocket_ifnet_remove.c242
-rw-r--r--tools/regression/netinet/rawconnect/Makefile7
-rw-r--r--tools/regression/netinet/rawconnect/rawconnect.c75
-rw-r--r--tools/regression/netinet/rawconnect/rawconnect.t21
-rw-r--r--tools/regression/netinet/tcpconnect/Makefile7
-rw-r--r--tools/regression/netinet/tcpconnect/README.tcp-md529
-rw-r--r--tools/regression/netinet/tcpconnect/tcpconnect.c169
-rw-r--r--tools/regression/netinet/tcpdrop/Makefile8
-rw-r--r--tools/regression/netinet/tcpdrop/tcpdrop.c254
-rw-r--r--tools/regression/netinet/tcpfullwindowrst/Makefile4
-rw-r--r--tools/regression/netinet/tcpfullwindowrst/tcpfullwindowrst.t6
-rw-r--r--tools/regression/netinet/tcpfullwindowrst/tcpfullwindowrsttest.c130
-rw-r--r--tools/regression/netinet/tcpsockclosebeforeaccept/Makefile8
-rw-r--r--tools/regression/netinet/tcpsockclosebeforeaccept/tcpsockclosebeforeaccept.c199
-rw-r--r--tools/regression/netinet/tcpsocktimewait/Makefile8
-rw-r--r--tools/regression/netinet/tcpsocktimewait/tcpsocktimewait.c215
-rw-r--r--tools/regression/netinet/tcpstream/Makefile8
-rw-r--r--tools/regression/netinet/tcpstream/README18
-rw-r--r--tools/regression/netinet/tcpstream/tcpstream.c231
-rw-r--r--tools/regression/netinet/udpconnectjail/Makefile5
-rw-r--r--tools/regression/netinet/udpconnectjail/udpconnectjail.c115
-rw-r--r--tools/regression/netinet/udpzerobyte/Makefile5
-rw-r--r--tools/regression/netinet/udpzerobyte/udpzerobyte.c143
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);
+}