summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2013-09-29 14:27:52 -0700
committerRobert Mustacchi <rm@joyent.com>2013-11-06 08:09:36 -0800
commitfe77cc0407fb667ddc04e1a8f2e203bb7b9c80e1 (patch)
treeb5723fc87b94e5c73aa9c243dc40e6dac6ef5440
parentc6d054cbc999e5c8b9ad1aa01dbb4800b84f06bd (diff)
downloadillumos-joyent-fe77cc0407fb667ddc04e1a8f2e203bb7b9c80e1.tar.gz
4236 Internet Packet Disturber
4237 net_* and hook_* man pages are wrong Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> Reviewed by: Brendan Gregg <brendan.gregg@joyent.com> Reviewed by: Keith M Wesolowski <wesolows@foobazco.org> Reviewed by: Sebastien Roy <seb@delphix.com> Approved by: Dan McDonald <danmcd@nexenta.com>
-rw-r--r--usr/src/cmd/Makefile3
-rw-r--r--usr/src/cmd/devfsadm/misc_link.c4
-rw-r--r--usr/src/cmd/ipdadm/Makefile45
-rw-r--r--usr/src/cmd/ipdadm/ipdadm.c435
-rw-r--r--usr/src/lib/Makefile2
-rw-r--r--usr/src/lib/libipd/Makefile42
-rw-r--r--usr/src/lib/libipd/Makefile.com36
-rw-r--r--usr/src/lib/libipd/amd64/Makefile19
-rw-r--r--usr/src/lib/libipd/common/libipd.c303
-rw-r--r--usr/src/lib/libipd/common/libipd.h74
-rw-r--r--usr/src/lib/libipd/common/llib-lipd20
-rw-r--r--usr/src/lib/libipd/common/mapfile-vers46
-rw-r--r--usr/src/lib/libipd/i386/Makefile18
-rw-r--r--usr/src/lib/libipd/sparc/Makefile18
-rw-r--r--usr/src/lib/libipd/sparcv9/Makefile19
-rw-r--r--usr/src/man/man1m/Makefile2
-rw-r--r--usr/src/man/man1m/ipdadm.1m223
-rw-r--r--usr/src/man/man9f/net_hook_register.9f31
-rw-r--r--usr/src/man/man9f/net_instance_alloc.9f5
-rw-r--r--usr/src/man/man9f/net_protocol_lookup.9f5
-rw-r--r--usr/src/man/man9f/net_protocol_release.9f7
-rw-r--r--usr/src/man/man9s/hook_t.9s13
-rw-r--r--usr/src/man/man9s/net_instance_t.9s6
-rw-r--r--usr/src/pkg/manifests/network-ipd.mf46
-rw-r--r--usr/src/uts/common/Makefile.files2
-rw-r--r--usr/src/uts/common/Makefile.rules7
-rw-r--r--usr/src/uts/common/inet/ipd/ipd.c1193
-rw-r--r--usr/src/uts/common/inet/ipd/ipd.conf17
-rw-r--r--usr/src/uts/common/sys/Makefile1
-rw-r--r--usr/src/uts/common/sys/ipd.h81
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared1
-rw-r--r--usr/src/uts/intel/ipd/Makefile51
-rw-r--r--usr/src/uts/sparc/ipd/Makefile51
33 files changed, 2809 insertions, 17 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 1fa2a43e8f..d04b74d498 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -21,7 +21,7 @@
# Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2010 Nexenta Systems, Inc. All rights reserved.
-# Copyright 2011 Joyent, Inc. All rights reserved.
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
# Copyright (c) 2012 by Delphix. All rights reserved.
# Copyright (c) 2013 DEY Storage Systems, Inc. All rights reserved.
@@ -207,6 +207,7 @@ COMMON_SUBDIRS= \
intrstat \
ipcrm \
ipcs \
+ ipdadm \
ipf \
isainfo \
isalist \
diff --git a/usr/src/cmd/devfsadm/misc_link.c b/usr/src/cmd/devfsadm/misc_link.c
index 9465e2c886..b7aef8b00d 100644
--- a/usr/src/cmd/devfsadm/misc_link.c
+++ b/usr/src/cmd/devfsadm/misc_link.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#include <regex.h>
@@ -107,6 +108,9 @@ static devfsadm_create_t misc_cbt[] = {
"(^nca$)|(^rds$)|(^sdp$)|(^ipnet$)|(^dlpistub$)|(^bpf$)",
TYPE_EXACT | DRV_RE, ILEVEL_1, minor_name
},
+ { "pseudo", "ddi_pseudo", "ipd",
+ TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name
+ },
{ "pseudo", "ddi_pseudo",
"(^ipf$)|(^ipnat$)|(^ipstate$)|(^ipauth$)|"
"(^ipsync$)|(^ipscan$)|(^iplookup$)",
diff --git a/usr/src/cmd/ipdadm/Makefile b/usr/src/cmd/ipdadm/Makefile
new file mode 100644
index 0000000000..349899e5f6
--- /dev/null
+++ b/usr/src/cmd/ipdadm/Makefile
@@ -0,0 +1,45 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
+#
+
+PROG= ipdadm
+OBJS = ipdadm.o
+SRCS = $(OBJS:%.o=../%.c)
+
+
+include ../Makefile.cmd
+include ../Makefile.ctf
+
+CLEANFILES += $(OBJS)
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lipd
+
+.KEEP_STATE:
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+clean:
+ -$(RM) $(CLEANFILES)
+
+lint: lint_PROG
+
+%.o: ../%.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+install: $(PROG) $(ROOTUSRSBINPROG)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/ipdadm/ipdadm.c b/usr/src/cmd/ipdadm/ipdadm.c
new file mode 100644
index 0000000000..ca68541690
--- /dev/null
+++ b/usr/src/cmd/ipdadm/ipdadm.c
@@ -0,0 +1,435 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+
+/*
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <values.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <zone.h>
+#include <libgen.h>
+#include <assert.h>
+
+#include <libipd.h>
+
+static char *g_pname;
+static char g_zonename[ZONENAME_MAX];
+static zoneid_t g_zid;
+
+#define E_SUCCESS 0
+#define E_ERROR 1
+#define E_USAGE 2
+
+typedef int (*idc_cmd_func_t)(int, char *[]);
+typedef struct ipdadm_cmd {
+ const char *idc_name; /* subcommand name */
+ idc_cmd_func_t idc_func; /* subcommand function */
+ const char *idc_usage; /* subcommand help */
+} ipdadm_cmd_t;
+
+static int ipdadm_list(int, char *[]);
+static int ipdadm_info(int, char *[]);
+static int ipdadm_corrupt(int, char *[]);
+static int ipdadm_delay(int, char *[]);
+static int ipdadm_drop(int, char *[]);
+static int ipdadm_remove(int, char *[]);
+
+#define IPDADM_NCMDS 6
+static ipdadm_cmd_t ipdadm_cmds[] = {
+ { "list", ipdadm_list, "list [-v]" },
+ { "info", ipdadm_info, "info" },
+ { "corrupt", ipdadm_corrupt, "corrupt <percentage>" },
+ { "delay", ipdadm_delay, "delay <microseconds>" },
+ { "drop", ipdadm_drop, "drop <percentage>" },
+ { "remove", ipdadm_remove, "remove [corrupt|delay|drop]" }
+};
+
+static int
+usage(FILE *fp)
+{
+ int ii;
+ ipdadm_cmd_t *cmd;
+
+ (void) fprintf(fp, "Usage: %s [-z zonename] subcommand "
+ "[subcommand opts]\n\n", g_pname);
+ (void) fprintf(fp, "Subcommands:\n");
+ for (ii = 0; ii < IPDADM_NCMDS; ii++) {
+ cmd = &ipdadm_cmds[ii];
+ (void) fprintf(fp, "\t%s\n", cmd->idc_usage);
+ }
+
+ return (E_USAGE);
+}
+
+static void
+ipdadm_list_one(zoneid_t z, const ipd_config_t *icp, void *arg)
+{
+ char zonename[ZONENAME_MAX];
+ int opt_v = (int)(intptr_t)arg;
+
+ if (getzonenamebyid(z, zonename, sizeof (zonename)) < 0)
+ (void) printf("%ld", z);
+ else
+ (void) printf("%s", zonename);
+
+ if (!opt_v) {
+ (void) printf("\n");
+ return;
+ }
+
+ (void) printf("\t%u\t%u\t%u\n", icp->ic_corrupt, icp->ic_drop,
+ icp->ic_delay);
+}
+
+static int
+ipdadm_list(int argc, char *argv[])
+{
+ int opt_v = 0;
+ int fd, rval;
+ ipd_stathdl_t hdl;
+
+ if (argc > 1)
+ return (usage(stderr));
+
+ if (argc == 1) {
+ if (strcmp(argv[0], "-v") == 0)
+ ++opt_v;
+ else
+ return (usage(stderr));
+ }
+
+ fd = ipd_open(NULL);
+ rval = ipd_status_read(fd, &hdl);
+ (void) ipd_close(fd);
+
+ if (rval != 0) {
+ (void) fprintf(stderr, "%s: failed to get list info: %s\n",
+ g_pname, ipd_errmsg);
+ return (E_ERROR);
+ }
+
+ ipd_status_foreach_zone(hdl, ipdadm_list_one, (void *)(intptr_t)opt_v);
+ ipd_status_free(hdl);
+
+ return (E_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+ipdadm_info(int argc, char *argv[])
+{
+ int rval, fd;
+ ipd_stathdl_t hdl;
+ ipd_config_t *icp;
+
+ if (argc != 0)
+ return (usage(stderr));
+
+ fd = ipd_open(NULL);
+ rval = ipd_status_read(fd, &hdl);
+ (void) ipd_close(fd);
+ if (rval != 0) {
+ (void) fprintf(stderr, "%s: failed to get info: %s\n",
+ g_pname, ipd_errmsg);
+ return (E_ERROR);
+ }
+
+ if (ipd_status_get_config(hdl, g_zid, &icp) != 0) {
+ if (ipd_errno == EIPD_ZC_NOENT) {
+ (void) printf("zone %s does not exist or has no "
+ "ipd actions enabled\n", g_zonename);
+ return (E_SUCCESS);
+ }
+ (void) fprintf(stderr, "%s: failed to get info: %s\n",
+ g_pname, ipd_errmsg);
+ return (E_ERROR);
+ }
+
+ (void) printf("ipd information for zone %s:\n",
+ g_zonename);
+ (void) printf("\tcorrupt:\t%u%% chance of packet corruption\n",
+ icp->ic_corrupt);
+ (void) printf("\tdrop:\t\t%u%% chance of packet drop\n",
+ icp->ic_drop);
+ (void) printf("\tdelay:\t\t%u microsecond delay per packet\n",
+ icp->ic_delay);
+
+ ipd_status_free(hdl);
+
+ return (E_SUCCESS);
+}
+
+static long
+ipdadm_parse_long(const char *str, const char *name, long min, long max)
+{
+ long val;
+ char *end;
+
+ errno = 0;
+ val = strtol(str, &end, 10);
+ if (errno != 0) {
+ (void) fprintf(stderr, "%s: invalid value for %s: %s\n",
+ g_pname, name, str);
+ exit(E_ERROR);
+ }
+
+ /*
+ * We want to make sure that we got the whole string. If not that's an
+ * error. e.g. 23.42 should not be valid.
+ */
+ if (*end != '\0') {
+ (void) fprintf(stderr, "%s: %s value must be an integer\n",
+ g_pname, name);
+ exit(E_ERROR);
+ }
+
+ if (val < min || val > max) {
+ (void) fprintf(stderr, "%s: %s value must be between %ld and "
+ "%ld inclusive\n", g_pname, name, min, max);
+ exit(E_ERROR);
+ }
+
+ return (val);
+}
+
+static int
+ipdadm_corrupt(int argc, char *argv[])
+{
+ int rval, fd;
+ long val;
+ ipd_config_t ic;
+
+ if (argc != 1) {
+ (void) fprintf(stderr, "%s: corrupt <percentage>\n",
+ g_pname);
+ return (usage(stderr));
+ }
+
+ val = ipdadm_parse_long(argv[0], "corrupt", 0, 100);
+ bzero(&ic, sizeof (ic));
+ ic.ic_mask = IPDM_CORRUPT;
+ ic.ic_corrupt = val;
+
+ fd = ipd_open(NULL);
+ rval = ipd_ctl(fd, g_zid, &ic);
+ (void) ipd_close(fd);
+
+ if (rval != 0) {
+ (void) fprintf(stderr, "%s: failed to change corrupt "
+ "value: %s\n", g_pname, ipd_errmsg);
+ return (E_ERROR);
+ }
+
+ return (E_SUCCESS);
+}
+
+static int
+ipdadm_delay(int argc, char *argv[])
+{
+ long val;
+ int fd, rval;
+ ipd_config_t ic;
+
+ if (argc != 1) {
+ (void) fprintf(stderr, "%s: delay <microseconds>\n",
+ g_pname);
+ return (usage(stderr));
+ }
+
+ val = ipdadm_parse_long(argv[0], "delay", 0, MAXLONG);
+ bzero(&ic, sizeof (ic));
+ ic.ic_mask = IPDM_DELAY;
+ ic.ic_delay = val;
+
+ fd = ipd_open(NULL);
+ rval = ipd_ctl(fd, g_zid, &ic);
+ (void) ipd_close(fd);
+
+ if (rval != 0) {
+ (void) fprintf(stderr, "%s: failed to change delay value: %s\n",
+ g_pname, ipd_errmsg);
+ return (E_ERROR);
+ }
+
+ return (E_SUCCESS);
+}
+
+static int
+ipdadm_drop(int argc, char *argv[])
+{
+ long val;
+ int fd, rval;
+ ipd_config_t ic;
+
+ if (argc != 1) {
+ (void) fprintf(stderr, "%s: drop <percentage>\n",
+ g_pname);
+ return (usage(stderr));
+ }
+
+ val = ipdadm_parse_long(argv[0], "drop", 0, 100);
+ bzero(&ic, sizeof (ic));
+ ic.ic_mask = IPDM_DROP;
+ ic.ic_drop = val;
+
+ fd = ipd_open(NULL);
+ rval = ipd_ctl(fd, g_zid, &ic);
+ (void) ipd_close(fd);
+
+ if (rval != 0) {
+ (void) fprintf(stderr, "%s: failed to change drop value: %s\n",
+ g_pname, ipd_errmsg);
+ return (E_ERROR);
+ }
+
+ return (E_SUCCESS);
+}
+
+static int
+ipdadm_remove_valid(const char *str)
+{
+ if (strcmp(str, "corrupt") == 0) {
+ return (IPDM_CORRUPT);
+ } else if (strcmp(str, "drop") == 0) {
+ return (IPDM_DROP);
+ } else if (strcmp(str, "delay") == 0) {
+ return (IPDM_DELAY);
+ }
+
+ return (0);
+}
+
+static int
+ipdadm_remove(int argc, char *argv[])
+{
+ ipd_config_t ic;
+ char *cur, *res;
+ int rval, fd;
+
+ if (argc < 1) {
+ (void) fprintf(stderr, "%s: remove <arguments>\n",
+ g_pname);
+ return (usage(stderr));
+ }
+
+ if (argc > 1) {
+ (void) fprintf(stderr, "%s: remove's arguments must be "
+ "comma seperated\n", g_pname);
+ return (E_ERROR);
+ }
+
+ bzero(&ic, sizeof (ic));
+
+ cur = argv[0];
+ while ((res = strchr(cur, ',')) != NULL) {
+ *res = '\0';
+ if ((rval = ipdadm_remove_valid(cur)) == 0) {
+ (void) fprintf(stderr, "%s: unknown remove "
+ "argument: %s\n", g_pname, cur);
+ return (E_ERROR);
+ }
+ ic.ic_mask |= rval;
+ cur = res + 1;
+ }
+
+ if ((rval = ipdadm_remove_valid(cur)) == 0) {
+ (void) fprintf(stderr, "%s: unknown remove argument: %s\n",
+ g_pname, cur);
+ return (E_ERROR);
+ }
+ ic.ic_mask |= rval;
+
+ fd = ipd_open(NULL);
+ rval = ipd_ctl(fd, g_zid, &ic);
+ (void) ipd_close(fd);
+ if (rval == -1) {
+ (void) fprintf(stderr, "%s: failed to remove instances: %s\n",
+ g_pname, ipd_errmsg);
+ return (E_ERROR);
+ }
+
+ return (E_SUCCESS);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int ii;
+ ipdadm_cmd_t *cmd;
+
+ g_pname = basename(argv[0]);
+
+ if (argc < 2)
+ return (usage(stderr));
+ argc--;
+ argv++;
+
+ g_zid = getzoneid();
+ if (strcmp("-z", argv[0]) == 0) {
+ argc--;
+ argv++;
+ if (argc < 1) {
+ (void) fprintf(stderr, "%s: -z requires an argument\n",
+ g_pname);
+ return (usage(stderr));
+ }
+
+ if (g_zid != GLOBAL_ZONEID) {
+ (void) fprintf(stderr, "%s: -z option only permitted "
+ "in global zone\n", g_pname);
+ return (usage(stderr));
+ }
+
+ g_zid = getzoneidbyname(argv[0]);
+ if (g_zid == -1) {
+ (void) fprintf(stderr, "%s: %s: invalid zone\n",
+ g_pname, argv[0]);
+ return (E_ERROR);
+ }
+ argc--;
+ argv++;
+ }
+
+ if (getzonenamebyid(g_zid, g_zonename, sizeof (g_zonename)) < 0) {
+ (void) fprintf(stderr, "%s: failed to get zonename: %s\n",
+ g_pname, strerror(errno));
+ return (E_ERROR);
+ }
+
+ if (argc < 1)
+ return (usage(stderr));
+
+ for (ii = 0; ii < IPDADM_NCMDS; ii++) {
+ cmd = &ipdadm_cmds[ii];
+ if (strcmp(argv[0], cmd->idc_name) == 0) {
+ argv++;
+ argc--;
+ assert(cmd->idc_func != NULL);
+ return (cmd->idc_func(argc, argv));
+ }
+ }
+
+ (void) fprintf(stderr, "%s: %s: unknown command\n", g_pname, argv[0]);
+ return (usage(stderr));
+}
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index fe1035b59b..fe579b4c9d 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -105,6 +105,7 @@ SUBDIRS += \
libcryptoutil \
libinetutil \
libipadm \
+ libipd \
libipmp \
libiscsit \
libkmf \
@@ -407,6 +408,7 @@ HDRSUBDIRS= \
libfstyp \
libgen \
libipadm \
+ libipd \
libipsecutil \
libinetsvc \
libinetutil \
diff --git a/usr/src/lib/libipd/Makefile b/usr/src/lib/libipd/Makefile
new file mode 100644
index 0000000000..feab972218
--- /dev/null
+++ b/usr/src/lib/libipd/Makefile
@@ -0,0 +1,42 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012 Joyent, Inc. All rights reserved.
+#
+
+include ../Makefile.lib
+
+HDRS = libipd.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/libipd/Makefile.com b/usr/src/lib/libipd/Makefile.com
new file mode 100644
index 0000000000..f118aa6cc3
--- /dev/null
+++ b/usr/src/lib/libipd/Makefile.com
@@ -0,0 +1,36 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012 Joyent, Inc. All rights reserved.
+#
+
+include ../../Makefile.lib
+
+LIBRARY = libipd.a
+VERS = .1
+OBJECTS = libipd.o
+
+include ../../Makefile.lib
+
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lc
+CPPFLAGS += -I../common
+
+SRCDIR = ../common
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libipd/amd64/Makefile b/usr/src/lib/libipd/amd64/Makefile
new file mode 100644
index 0000000000..0ff7206916
--- /dev/null
+++ b/usr/src/lib/libipd/amd64/Makefile
@@ -0,0 +1,19 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012 Joyent, Inc. All rights reserved.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libipd/common/libipd.c b/usr/src/lib/libipd/common/libipd.c
new file mode 100644
index 0000000000..c580fdf61e
--- /dev/null
+++ b/usr/src/lib/libipd/common/libipd.c
@@ -0,0 +1,303 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include <libipd.h>
+#include <sys/ipd.h>
+
+__thread ipd_errno_t ipd_errno = 0;
+__thread char ipd_errmsg[512];
+
+struct ipd_stat {
+ uint_t is_nzones;
+ zoneid_t *is_zoneids;
+ struct ipd_config *is_configs;
+};
+
+static ipd_errno_t
+xlate_errno(int e)
+{
+ switch (e) {
+ case 0:
+ return (EIPD_NOERROR);
+ case ENOMEM:
+ case EAGAIN:
+ return (EIPD_NOMEM);
+ case ERANGE:
+ return (EIPD_RANGE);
+ case EPERM:
+ return (EIPD_PERM);
+ case EFAULT:
+ return (EIPD_FAULT);
+ case ENOTTY:
+ return (EIPD_INTERNAL);
+ default:
+ return (EIPD_UNKNOWN);
+ }
+}
+
+const char *
+ipd_strerror(ipd_errno_t e)
+{
+ switch (e) {
+ case EIPD_NOERROR:
+ return ("no error");
+ case EIPD_NOMEM:
+ return ("out of memory");
+ case EIPD_ZC_NOENT:
+ return ("zone does not exist or is not using ipd");
+ case EIPD_RANGE:
+ return ("argument out of range");
+ case EIPD_PERM:
+ return ("permission denied");
+ case EIPD_FAULT:
+ return ("bad pointer");
+ case EIPD_INTERNAL:
+ return ("internal library error");
+ case EIPD_UNKNOWN:
+ default:
+ return ("unknown error");
+ }
+}
+
+static int
+ipd_set_errno(ipd_errno_t e, const char *fmt, ...)
+{
+ va_list ap;
+
+ ipd_errno = e;
+ if (fmt != NULL) {
+ va_start(ap, fmt);
+ (void) vsnprintf(ipd_errmsg, sizeof (ipd_errmsg), fmt, ap);
+ va_end(ap);
+ } else {
+ (void) strlcpy(ipd_errmsg,
+ ipd_strerror(e), sizeof (ipd_errmsg));
+ }
+
+ return (-1);
+}
+
+int
+ipd_open(const char *path)
+{
+ int fd;
+
+ if (path == NULL)
+ path = IPD_DEV_PATH;
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ return (ipd_set_errno(xlate_errno(errno),
+ "unable to open %s: %s", path, strerror(errno)));
+ }
+
+ return (fd);
+}
+
+int
+ipd_close(int fd)
+{
+ (void) close(fd);
+ return (0);
+}
+
+int
+ipd_status_read(int fd, ipd_stathdl_t *ispp)
+{
+ struct ipd_stat *isp = NULL;
+ ipd_ioc_list_t ipil;
+ uint_t rzones;
+ uint_t i;
+
+ bzero(&ipil, sizeof (ipil));
+ if (ioctl(fd, IPDIOC_LIST, &ipil) != 0) {
+ return (ipd_set_errno(xlate_errno(errno),
+ "unable to retrieve ipd zone list: %s", strerror(errno)));
+ }
+
+ for (;;) {
+ if ((rzones = ipil.ipil_nzones) == 0)
+ break;
+
+ ipil.ipil_info =
+ malloc(sizeof (ipd_ioc_info_t) * ipil.ipil_nzones);
+ if (ipil.ipil_info == NULL)
+ return (ipd_set_errno(EIPD_NOMEM, NULL));
+
+ if (ioctl(fd, IPDIOC_LIST, &ipil) != 0) {
+ free(ipil.ipil_info);
+ return (ipd_set_errno(xlate_errno(errno),
+ "unable to retrieve ipd zone list: %s",
+ strerror(errno)));
+ }
+
+ if (ipil.ipil_nzones <= rzones)
+ break;
+
+ free(ipil.ipil_info);
+ }
+
+ if ((isp = malloc(sizeof (struct ipd_stat))) == NULL) {
+ free(ipil.ipil_info);
+ return (ipd_set_errno(EIPD_NOMEM, NULL));
+ }
+
+ isp->is_nzones = ipil.ipil_nzones;
+
+ if (isp->is_nzones == 0) {
+ isp->is_zoneids = NULL;
+ isp->is_configs = NULL;
+ *ispp = isp;
+ return (0);
+ }
+
+ isp->is_zoneids = malloc(sizeof (zoneid_t) * ipil.ipil_nzones);
+ if (isp->is_zoneids == NULL) {
+ free(ipil.ipil_info);
+ free(isp);
+ return (ipd_set_errno(EIPD_NOMEM, NULL));
+ }
+ isp->is_configs = malloc(sizeof (struct ipd_config) * ipil.ipil_nzones);
+ if (isp->is_configs == NULL) {
+ free(ipil.ipil_info);
+ free(isp->is_zoneids);
+ free(isp);
+ return (ipd_set_errno(EIPD_NOMEM, NULL));
+ }
+
+ for (i = 0; i < isp->is_nzones; i++) {
+ isp->is_zoneids[i] = ipil.ipil_info[i].ipii_zoneid;
+
+ isp->is_configs[i].ic_corrupt = ipil.ipil_info[i].ipii_corrupt;
+ isp->is_configs[i].ic_drop = ipil.ipil_info[i].ipii_drop;
+ isp->is_configs[i].ic_delay = ipil.ipil_info[i].ipii_delay;
+
+ isp->is_configs[i].ic_mask =
+ ((!!isp->is_configs[i].ic_corrupt) * IPDM_CORRUPT) |
+ ((!!isp->is_configs[i].ic_drop) * IPDM_DROP) |
+ ((!!isp->is_configs[i].ic_delay) * IPDM_DELAY);
+ }
+
+ *ispp = isp;
+ return (0);
+}
+
+void
+ipd_status_foreach_zone(const ipd_stathdl_t hdl, ipd_status_cb_f f, void *arg)
+{
+ const struct ipd_stat *isp = hdl;
+ uint_t i;
+
+ for (i = 0; i < isp->is_nzones; i++)
+ f(isp->is_zoneids[i], &isp->is_configs[i], arg);
+}
+
+int
+ipd_status_get_config(const ipd_stathdl_t hdl, zoneid_t z, ipd_config_t **icpp)
+{
+ const struct ipd_stat *isp = hdl;
+ uint_t i;
+
+ for (i = 0; i < isp->is_nzones; i++) {
+ if (isp->is_zoneids[i] == z) {
+ *icpp = &isp->is_configs[i];
+ return (0);
+ }
+ }
+
+ return (ipd_set_errno(EIPD_ZC_NOENT,
+ "zone %d does not exist or has no ipd configuration", z));
+}
+
+void
+ipd_status_free(ipd_stathdl_t hdl)
+{
+ struct ipd_stat *isp = hdl;
+
+ if (isp != NULL) {
+ free(isp->is_zoneids);
+ free(isp->is_configs);
+ }
+ free(isp);
+}
+
+int
+ipd_ctl(int fd, zoneid_t z, const ipd_config_t *icp)
+{
+ ipd_ioc_perturb_t ipip;
+
+ bzero(&ipip, sizeof (ipd_ioc_perturb_t));
+ ipip.ipip_zoneid = z;
+
+ if (icp->ic_mask & IPDM_CORRUPT) {
+ if (icp->ic_corrupt == 0)
+ ipip.ipip_arg |= IPD_CORRUPT;
+ }
+ if (icp->ic_mask & IPDM_DELAY) {
+ if (icp->ic_delay == 0)
+ ipip.ipip_arg |= IPD_DELAY;
+ }
+ if (icp->ic_mask & IPDM_DROP) {
+ if (icp->ic_drop == 0)
+ ipip.ipip_arg |= IPD_DROP;
+ }
+
+ if (ipip.ipip_arg != 0 && ioctl(fd, IPDIOC_REMOVE, &ipip) != 0) {
+ return (ipd_set_errno(xlate_errno(errno),
+ "unable to remove cleared ipd settings: %s",
+ strerror(errno)));
+ }
+
+ if ((icp->ic_mask & IPDM_CORRUPT) && icp->ic_corrupt != 0) {
+ ipip.ipip_zoneid = z;
+ ipip.ipip_arg = icp->ic_corrupt;
+ if (ioctl(fd, IPDIOC_CORRUPT, &ipip) != 0) {
+ return (ipd_set_errno(xlate_errno(errno),
+ "unable to set corruption rate to %d: %s",
+ ipip.ipip_arg, strerror(errno)));
+ }
+ }
+ if ((icp->ic_mask & IPDM_DELAY) && icp->ic_delay != 0) {
+ ipip.ipip_zoneid = z;
+ ipip.ipip_arg = icp->ic_delay;
+ if (ioctl(fd, IPDIOC_DELAY, &ipip) != 0) {
+ return (ipd_set_errno(xlate_errno(errno),
+ "unable to set delay time to %d: %s",
+ ipip.ipip_arg, strerror(errno)));
+ }
+ }
+ if ((icp->ic_mask & IPDM_DROP) && icp->ic_drop != 0) {
+ ipip.ipip_zoneid = z;
+ ipip.ipip_arg = icp->ic_drop;
+ if (ioctl(fd, IPDIOC_DROP, &ipip) != 0) {
+ return (ipd_set_errno(xlate_errno(errno),
+ "unable to set drop probability to %d: %s",
+ ipip.ipip_arg, strerror(errno)));
+ }
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/libipd/common/libipd.h b/usr/src/lib/libipd/common/libipd.h
new file mode 100644
index 0000000000..ebf56aea1e
--- /dev/null
+++ b/usr/src/lib/libipd/common/libipd.h
@@ -0,0 +1,74 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBIPD_H
+#define _LIBIPD_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Bitmask values for ic_mask.
+ */
+#define IPDM_CORRUPT 0x1000
+#define IPDM_DELAY 0x2000
+#define IPDM_DROP 0x4000
+
+typedef enum ipd_errno {
+ EIPD_NOERROR = 0,
+ EIPD_NOMEM,
+ EIPD_ZC_NOENT,
+ EIPD_RANGE,
+ EIPD_PERM,
+ EIPD_FAULT,
+ EIPD_INTERNAL,
+ EIPD_UNKNOWN
+} ipd_errno_t;
+
+typedef struct ipd_config {
+ uint32_t ic_mask;
+ uint32_t ic_corrupt;
+ uint32_t ic_drop;
+ uint32_t ic_delay;
+} ipd_config_t;
+
+struct ipd_stat;
+typedef struct ipd_stat *ipd_stathdl_t;
+
+typedef void (*ipd_status_cb_f)(zoneid_t, const ipd_config_t *, void *);
+
+extern __thread ipd_errno_t ipd_errno;
+extern __thread char ipd_errmsg[];
+
+extern const char *ipd_strerror(ipd_errno_t);
+extern int ipd_open(const char *);
+extern int ipd_close(int);
+extern int ipd_status_read(int, ipd_stathdl_t *);
+extern void ipd_status_foreach_zone(const ipd_stathdl_t,
+ ipd_status_cb_f, void *);
+extern int ipd_status_get_config(const ipd_stathdl_t,
+ zoneid_t, ipd_config_t **);
+extern void ipd_status_free(ipd_stathdl_t);
+extern int ipd_ctl(int, zoneid_t, const ipd_config_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBIPD_H */
diff --git a/usr/src/lib/libipd/common/llib-lipd b/usr/src/lib/libipd/common/llib-lipd
new file mode 100644
index 0000000000..79a3521301
--- /dev/null
+++ b/usr/src/lib/libipd/common/llib-lipd
@@ -0,0 +1,20 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <libipd.h>
diff --git a/usr/src/lib/libipd/common/mapfile-vers b/usr/src/lib/libipd/common/mapfile-vers
new file mode 100644
index 0000000000..91c4632dea
--- /dev/null
+++ b/usr/src/lib/libipd/common/mapfile-vers
@@ -0,0 +1,46 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate_1.1 {
+ global:
+ ipd_errno;
+ ipd_errmsg;
+ ipd_strerror;
+ ipd_open;
+ ipd_close;
+ ipd_status_read;
+ ipd_status_foreach_zone;
+ ipd_status_get_config;
+ ipd_status_free;
+ ipd_ctl;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libipd/i386/Makefile b/usr/src/lib/libipd/i386/Makefile
new file mode 100644
index 0000000000..6a668fb98f
--- /dev/null
+++ b/usr/src/lib/libipd/i386/Makefile
@@ -0,0 +1,18 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012 Joyent, Inc. All rights reserved.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libipd/sparc/Makefile b/usr/src/lib/libipd/sparc/Makefile
new file mode 100644
index 0000000000..6a668fb98f
--- /dev/null
+++ b/usr/src/lib/libipd/sparc/Makefile
@@ -0,0 +1,18 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012 Joyent, Inc. All rights reserved.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libipd/sparcv9/Makefile b/usr/src/lib/libipd/sparcv9/Makefile
new file mode 100644
index 0000000000..0ff7206916
--- /dev/null
+++ b/usr/src/lib/libipd/sparcv9/Makefile
@@ -0,0 +1,19 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012 Joyent, Inc. All rights reserved.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/man/man1m/Makefile b/usr/src/man/man1m/Makefile
index 7560550067..9510504781 100644
--- a/usr/src/man/man1m/Makefile
+++ b/usr/src/man/man1m/Makefile
@@ -11,6 +11,7 @@
#
# Copyright 2011, Richard Lowe
+# Copyright (c) 2012, Joyent, Inc. All rights reserved.
# Copyright 2013 Nexenta Systems, Inc. All rights reserved.
#
@@ -237,6 +238,7 @@ _MANFILES= 6to4relay.1m \
iostat.1m \
ipaddrsel.1m \
ipadm.1m \
+ ipdadm.1m \
ipf.1m \
ipfs.1m \
ipfstat.1m \
diff --git a/usr/src/man/man1m/ipdadm.1m b/usr/src/man/man1m/ipdadm.1m
new file mode 100644
index 0000000000..8d5eec4331
--- /dev/null
+++ b/usr/src/man/man1m/ipdadm.1m
@@ -0,0 +1,223 @@
+'\" te
+.\" Copyright (c) 2012, Joyent, Inc. All Rights Reserved.
+.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
+.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the
+.\" fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
+.TH IPDADM 1M "Mar 1, 2012"
+.SH NAME
+ipdadm \- administer the Internet packet disturber
+.SH SYNOPSIS
+.LP
+.nf
+\fBipdadm\fR [\fB-z\fR \fIzonename\fR] \fIsubcommand\fR [\fIsubcommand-options\fR]
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The ipdadm utility is used to administer the illumos facility for simulating
+pathological networks by induce packet drops, delays, and corruption.
+
+This functionality is only able to the global zone and zones with exclusive
+networking stacks. If this is enabled for the global zone, any zone with a
+shared networking stack will be affected.
+
+.SH OPTIONS
+.sp
+.LP
+The following options are supported:
+.sp
+.ne 2
+.na
+\fB-z\fR
+.ad
+.sp .6
+.RS 4n
+The string of the zonename to control. If this is not specified, the current
+zone is used. For the list subcommand, this option is not supported.
+.RE
+
+.SH SUBCOMMANDS
+.sp
+.LP
+The following subcommands are supported:
+
+.sp
+.ne 2
+.na
+\fBcorrupt\fR \fIpercent\fR
+.ad
+.sp .6
+.RS 4n
+Sets the chance for packets to be corrupted to \fIpercent\fR which must be an
+integer between 0 and 100. Setting \fIpercent\fR to 0 disables packet corruption
+and is equivalent to calling \fBremove\fR \fIcorrupt\fR. When enabled, a random
+byte will have a single bit flipped.
+.sp
+.RE
+
+.sp
+.ne 2
+.na
+\fBdelay\fR \fImicroseconds\fR
+.ad
+.sp .6
+.RS 4n
+Inserts \fImicroseconds\fR of delay between both the transmit and receive of
+each packet. Setting \fImicroseconds\fR to zero is equivalent to calling
+\fBremove\fR \fIdelay\fR.
+.sp
+.RE
+
+.sp
+.ne 2
+.na
+\fBdrop\fR \fIpercent\fR
+.ad
+.sp .6
+.RS 4n
+Sets the chance for packets to be dropped to \fIpercent\fR. \fIpercent\fR must
+be an integer between 0 and 100. Setting \fIpercent\fR to zero is equivalent to
+calling \fBremove\fR \fIdrop\fR.
+.sp
+.RE
+
+.sp
+.ne 2
+.na
+\fBinfo\fR
+.ad
+.sp .6
+.RS 4n
+Prints the current values for packet corruption, packet dropping, and packet
+delay for the zone.
+.sp
+.RE
+
+.sp
+.ne 2
+.na
+\fBlist\fR
+.ad
+.sp .6
+.RS 4n
+Prints out the zonename of each zone that has active (non-zero) packet
+corruption, packet delay, and packet drop.
+.sp
+.RE
+
+.sp
+.ne 2
+.na
+\fBremove\fR \fIcorrupt\fR,\fIdelay\fI,\fIdrop\fR
+.ad
+.sp .6
+.RS 4n
+Disables the requested forms of packet perturbation for the zone. This is
+functionally equivalent to setting the value to zero. One or more of
+\fIcorrupt\fR, \fIdelay\fR, and \fIdrop\fR can be specified as a comma separated
+list.
+.sp
+.RE
+
+.SH EXAMPLES
+.LP
+\fBExample 1 \fRViewing zone information
+.sp
+.LP
+The following command illustrates how to obtain information about a specific
+zone while in the global zone.
+
+.sp
+.in +2
+.nf
+# \fBipdadm -z 949860c5-52e2-4f32-965d-02e60e116b72 info\fR
+ipd information for zone 949860c5-52e2-4f32-965d-02e60e116b72:
+ corrupt: 0% chance of packet corruption
+ drop: 0% chance of packet drop
+ delay: 0 microsecond delay per packet
+.fi
+.in -2
+.sp
+
+.LP
+\fBExample 2 \fRRemoving multiple perturbations
+.sp
+.LP
+The following command illustrates the use of the \fBremove\fR subcommand from
+inside of a zone.
+
+.sp
+.in +2
+.nf
+# \fBipdadm remove corrupt,drop\fR
+.fi
+.in -2
+.sp
+
+.LP
+\fBExample 3 \fREnabling packet drops
+.sp
+.LP
+The following command illustrates the use of the \fBdrop\fR subcommand from the
+global zone.
+
+.sp
+.in +2
+.nf
+# \fBipdadm -z 949860c5-52e2-4f32-965d-02e60e116b72 drop 25\fR
+.fi
+.in -2
+.sp
+
+
+.SH EXIT STATUS
+.sp
+.LP
+The following exit values are returned:
+.sp
+.ne 2
+.na
+\fB0\fR
+.ad
+.RS 5n
+Successful completion.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB1\fR\fR
+.ad
+.RS 5n
+An error occurred.
+.sp
+.sp
+.ne 2
+.na
+\fB\fB2\fR\fR
+.ad
+.RS 5n
+Invalid command line options or arguments were specified.
+.RE
+
+.SH ATTRIBUTES
+.sp
+.LP
+See \fBattributes\fR(5) for descriptions of the following attributes:
+.sp
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Interface Stability Evolving
+.TE
+
+.SH SEE ALSO
+.sp
+.LP
+\fBzonename\fR(1),\fBzoneadm\fR(1M), \fBattributes\fR(5), \fBzones(5)\fR
diff --git a/usr/src/man/man9f/net_hook_register.9f b/usr/src/man/man9f/net_hook_register.9f
index 6cffdc1e64..3d10a10524 100644
--- a/usr/src/man/man9f/net_hook_register.9f
+++ b/usr/src/man/man9f/net_hook_register.9f
@@ -1,9 +1,10 @@
'\" te
.\" Copyright (C) 2008, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright (C) 2012, Joyent, Inc. All Rights Reserved.
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH NET_HOOK_REGISTER 9F "May 1, 2008"
+.TH NET_HOOK_REGISTER 9F "Mar 5, 2012"
.SH NAME
net_hook_register \- add a hook to be called in event processing
.SH SYNOPSIS
@@ -11,7 +12,7 @@ net_hook_register \- add a hook to be called in event processing
.nf
#include <sys/neti.h>
-\fBnet_hook_t\fR \fBnet_hook_register\fR(\fBconst\fR \fBnet_data_t\fR \fInet\fR, \fBhook_t\fR *\fIhook\fR);
+\fBint\fR \fBnet_hook_register\fR(\fBnet_handle_t\fR \fIinfo\fR, \fBchar\fR *\fIevent\fR, \fBhook_t\fR *\fIhook\fR);
.fi
.SH INTERFACE LEVEL
@@ -22,7 +23,7 @@ Solaris DDI specific (Solaris DDI).
.sp
.ne 2
.na
-\fB\fInet\fR\fR
+\fB\fIinfo\fR\fR
.ad
.RS 8n
value returned from a successful call to \fBnet_protocol_register()\fR.
@@ -31,6 +32,15 @@ value returned from a successful call to \fBnet_protocol_register()\fR.
.sp
.ne 2
.na
+\fB\fIname\fR\fR
+.ad
+.RS 8n
+unique name for this hook.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fIhook\fR\fR
.ad
.RS 8n
@@ -42,9 +52,10 @@ pointer to a \fBhook_t\fR structure.
.LP
The \fBnet_hook_register()\fR function uses hooks that allow callbacks to be
registered with events that belong to a network protocol. A successful call to
-\fBnet_hook_register()\fR requires that a valid handle for a network protocol
-be provided (the \fInet\fR parameter), along with a hook description that
-includes a reference to an available event.
+\fBnet_hook_register()\fR requires that a valid handle for a network protocol be
+provided (the \fIinfo\fR parameter), along with a unique name and a hook
+description that includes a reference to an available event. No two hooks on the
+same netstack may share a name.
.sp
.LP
While it is possible to use the same \fBhook_t\fR structure with multiple calls
@@ -154,6 +165,14 @@ because it conflicts with another hook. An example of this might be specifying
with this value.
.RE
+.sp
+.ne 2
+.na
+\fBESHUTDOWN\fR
+.ad
+.RS 10n
+The netstack corresponding to \fIinfo\fR is condemned or no longer exists.
+
.SH CONTEXT
.sp
.LP
diff --git a/usr/src/man/man9f/net_instance_alloc.9f b/usr/src/man/man9f/net_instance_alloc.9f
index 0f51ce67d6..146054f5d4 100644
--- a/usr/src/man/man9f/net_instance_alloc.9f
+++ b/usr/src/man/man9f/net_instance_alloc.9f
@@ -1,9 +1,10 @@
'\" te
.\" Copyright (C) 2008, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright (C) 2012, Joyent, Inc. All Rights Reserved.
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH NET_INSTANCE_ALLOC 9F "May 1, 2008"
+.TH NET_INSTANCE_ALLOC 9F "Mar 5, 2012"
.SH NAME
net_instance_alloc \- allocate a net_instance_t structure
.SH SYNOPSIS
@@ -25,7 +26,7 @@ Solaris DDI specific (Solaris DDI).
\fB\fIversion\fR\fR
.ad
.RS 11n
-must always be the symbol \fBNETI_VERSION\fR.
+must always be the symbol \fBNETINFO_VERSION\fR.
.RE
.SH DESCRIPTION
diff --git a/usr/src/man/man9f/net_protocol_lookup.9f b/usr/src/man/man9f/net_protocol_lookup.9f
index d6707d7c9e..cafa4c2c4f 100644
--- a/usr/src/man/man9f/net_protocol_lookup.9f
+++ b/usr/src/man/man9f/net_protocol_lookup.9f
@@ -1,9 +1,10 @@
'\" te
.\" Copyright (C) 2008, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright (C) 2012, Joyent, Inc. All Rights Reserved.
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH NET_PROTOCOL_LOOKUP 9F "May 1, 2008"
+.TH NET_PROTOCOL_LOOKUP 9F "Mar 5, 2012"
.SH NAME
net_protocol_lookup \- locate an implementation of a network layer protocol
.SH SYNOPSIS
@@ -11,7 +12,7 @@ net_protocol_lookup \- locate an implementation of a network layer protocol
.nf
#include <sys/neti.h>
-\fBnet_data_t\fR \fBnet_protocol_lookup\fR(\fBnetid_t\fR \fIid\fR, \fBconst\fR \fBchar\fR *\fIprotocol\fR);
+\fBnet_handle_t\fR \fBnet_protocol_lookup\fR(\fBnetid_t\fR \fIid\fR, \fBconst\fR \fBchar\fR *\fIprotocol\fR);
.fi
.SH INTERFACE LEVEL
diff --git a/usr/src/man/man9f/net_protocol_release.9f b/usr/src/man/man9f/net_protocol_release.9f
index 5f9d1c5be5..f1759d099d 100644
--- a/usr/src/man/man9f/net_protocol_release.9f
+++ b/usr/src/man/man9f/net_protocol_release.9f
@@ -1,9 +1,10 @@
'\" te
.\" Copyright (C) 2008, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright (C) 2012, Joyent, Inc. All Rights Reserved.
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH NET_PROTOCOL_RELEASE 9F "May 1, 2008"
+.TH NET_PROTOCOL_RELEASE 9F "Mar 5, 2012"
.SH NAME
net_protocol_release \- indicate that a reference to a network protocol is no
longer required
@@ -12,7 +13,7 @@ longer required
.nf
#include <sys/neti.h>
-\fBint\fR \fBnet_protocol_release\fR(\fBnet_data_t\fR *\fInet\fR);
+\fBint\fR \fBnet_protocol_release\fR(\fBnet_handle_t\fR \fIinfo\fR);
.fi
.SH INTERFACE LEVEL
@@ -23,7 +24,7 @@ Solaris DDI specific (Solaris DDI).
.sp
.ne 2
.na
-\fB\fInet\fR\fR
+\fB\fIinfo\fR\fR
.ad
.RS 7n
value returned from a successful call to \fBnet_protocol_lookup\fR(9F).
diff --git a/usr/src/man/man9s/hook_t.9s b/usr/src/man/man9s/hook_t.9s
index b8abef07a5..818603ba59 100644
--- a/usr/src/man/man9s/hook_t.9s
+++ b/usr/src/man/man9s/hook_t.9s
@@ -1,9 +1,10 @@
'\" te
.\" Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright (c) 2012 Joyent, Inc. All Rights Reserved
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH HOOK_T 9S "May 1, 2008"
+.TH HOOK_T 9S "Mar 5, 2012"
.SH NAME
hook_t \- callback structure for subscribing to netinfo events
.SH SYNOPSIS
@@ -38,6 +39,16 @@ typedef int (*hook_func_t)(net_event_t token, hook_data_t info,
.fi
.in -2
+.SS "CALLBACK RETURN VALUES"
+.sp
+.LP
+The value returned by the \fBhook_func_t\fR function indicates whether or not
+the packet should still be considered valid. If the callback function has
+modified the packet in such a way that it should no longer be processed or
+considered valid, e.g. called \fBfreemsg\fR(9F), then the callback should return
+\fB1\fR. Otherwise it should return \fB0\fR.
+.sp
+
.SS "HINT TYPES"
.sp
.LP
diff --git a/usr/src/man/man9s/net_instance_t.9s b/usr/src/man/man9s/net_instance_t.9s
index 956bbcdb3a..4f4b143276 100644
--- a/usr/src/man/man9s/net_instance_t.9s
+++ b/usr/src/man/man9s/net_instance_t.9s
@@ -1,9 +1,10 @@
'\" te
.\" Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright (c) 2012 Joyent, Inc. All Rights Reserved
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH NET_INSTANCE_T 9S "May 1, 2008"
+.TH NET_INSTANCE_T 9S "Mar 5, 2012"
.SH NAME
net_instance_t \- packet event structure passed through to hooks
.SH SYNOPSIS
@@ -51,7 +52,8 @@ Name of the owner of the instance.
\fB\fBnin_create\fR\fR
.ad
.RS 16n
-Function to be called when a new instance of \fBIP\fR is created.
+Function to be called when a new instance of \fBIP\fR is created. It may not
+return \fBNULL\fR.
.RE
.sp
diff --git a/usr/src/pkg/manifests/network-ipd.mf b/usr/src/pkg/manifests/network-ipd.mf
new file mode 100644
index 0000000000..01e2a41bec
--- /dev/null
+++ b/usr/src/pkg/manifests/network-ipd.mf
@@ -0,0 +1,46 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2013 by Delphix. All rights reserved.
+#
+
+set name=pkg.fmri value=pkg:/network/ipd@$(PKGVERS)
+set name=pkg.description value="Internet Packet Disturber"
+set name=pkg.summary value="Internet Packet Disturber"
+set name=info.classification \
+ value=org.opensolaris.category.2008:System/Hardware
+set name=variant.arch value=$(ARCH)
+dir path=kernel group=sys
+dir path=kernel/drv group=sys
+dir path=kernel/drv/$(ARCH64) group=sys
+dir path=usr group=sys
+dir path=usr/lib
+dir path=usr/lib/$(ARCH64)
+dir path=usr/sbin
+dir path=usr/share/man/man1m
+driver name=ipd perms="* 0600 root sys"
+file path=kernel/drv/$(ARCH64)/ipd group=sys
+$(i386_ONLY)file path=kernel/drv/ipd group=sys
+file path=kernel/drv/ipd.conf group=sys
+file path=usr/include/libipd.h
+file path=usr/include/sys/ipd.h
+file path=usr/lib/$(ARCH64)/libipd.so.1
+file path=usr/lib/$(ARCH64)/llib-lipd.ln
+file path=usr/lib/libipd.so.1
+file path=usr/lib/llib-lipd
+file path=usr/lib/llib-lipd.ln
+file path=usr/sbin/ipdadm mode=0555
+file path=usr/share/man/man1m/ipdadm.1m
+license cr_Sun license=cr_Sun
+license lic_CDDL license=lic_CDDL
+link path=usr/lib/$(ARCH64)/libipd.so target=libipd.so.1
+link path=usr/lib/libipd.so target=libipd.so.1
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index 075432f012..e49ffe089b 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -1751,6 +1751,8 @@ IPF_OBJS += ip_fil_solaris.o fil.o solaris.o ip_state.o ip_frag.o ip_nat.o \
ip_proxy.o ip_auth.o ip_pool.o ip_htable.o ip_lookup.o \
ip_log.o misc.o ip_compat.o ip_nat6.o drand48.o
+IPD_OBJS += ipd.o
+
IBD_OBJS += ibd.o ibd_cm.o
EIBNX_OBJS += enx_main.o enx_hdlrs.o enx_ibt.o enx_log.o enx_fip.o \
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index cc71c7ba85..407bf4e26c 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -506,6 +506,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/inet/ipf/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/inet/ipd/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(COMMONBASE)/net/patricia/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1894,6 +1898,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/inet/ipnet/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/inet/iptun/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/inet/ipd/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/inet/ipf/%.c
@($(LHEAD) $(LINT.c) $(IPFFLAGS) $< $(LTAIL))
diff --git a/usr/src/uts/common/inet/ipd/ipd.c b/usr/src/uts/common/inet/ipd/ipd.c
new file mode 100644
index 0000000000..5029a5bb10
--- /dev/null
+++ b/usr/src/uts/common/inet/ipd/ipd.c
@@ -0,0 +1,1193 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * ipd: Internet packet disturber
+ *
+ * The purpose of ipd is to simulate congested and lossy networks when they
+ * don't actually exist. The features of these congested and lossy networks are
+ * events that end up leading to retransmits and thus kicking us out of the
+ * TCP/IP fastpath. Since normally this would require us to have an actually
+ * congested network, which can be problematic, we instead simulate this
+ * behavior.
+ *
+ * 1. ipd's operations and restrictions
+ *
+ * ipd currently has facilities to cause IP traffic to be:
+ *
+ * - Corrupted with some probability.
+ * - Delayed for a set number of microseconds.
+ * - Dropped with some probability.
+ *
+ * Each of these features are enabled on a per-zone basic. The current
+ * implementation restricts this specifically to exclusive stack zones.
+ * Enabling ipd on a given zone causes pfhooks to be installed for that zone's
+ * netstack. Because of the nature of ipd, it currently only supports exclusive
+ * stack zones and as a further restriction, it only allows the global zone
+ * administrative access. ipd can be enabled for the global zone, but doing so
+ * will cause all shared-stack zones to also be affected.
+ *
+ * 2. General architecture and Locking
+ *
+ * ipd consists of a few components. There is a per netstack data structure that
+ * is created and destroyed with the creation and destruction of each exclusive
+ * stack zone. Each of these netstacks is stored in a global list which is
+ * accessed for control of ipd via ioctls. The following diagram touches on the
+ * data structures that are used throughout ipd.
+ *
+ * ADMINISTRATIVE DATA PATH
+ *
+ * +--------+ +------+ +------+
+ * | ipdadm | | ip | | nics |
+ * +--------+ +------+ +------+
+ * | ^ | |
+ * | | ioctl(2) | |
+ * V | V V
+ * +----------+ +-------------------------+
+ * | /dev/ipd | | pfhooks packet callback | == ipd_hook()
+ * +----------+ +-------------------------+
+ * | |
+ * | |
+ * V |
+ * +----------------+ |
+ * | list_t ipd_nsl |------+ |
+ * +----------------+ | |
+ * | |
+ * V per netstack V
+ * +----------------------------+
+ * | ipd_nestack_t |
+ * +----------------------------+
+ *
+ * ipd has two different entry points, one is administrative, the other is the
+ * data path. The administrative path is accessed by a userland component called
+ * ipdadm(1M). It communicates to the kernel component via ioctls to /dev/ipd.
+ * If the administrative path enables a specific zone, then the data path will
+ * become active for that zone. Any packet that leaves that zone's IP stack or
+ * is going to enter it, comes through the callback specified in the hook_t(9S)
+ * structure. This will cause each packet to go through ipd_hook().
+ *
+ * While the locking inside of ipd should be straightforward, unfortunately, the
+ * pfhooks subsystem necessarily complicates this a little bit. There are
+ * currently three different sets of locks in ipd.
+ *
+ * - Global lock N on the netstack list.
+ * - Global lock A on the active count.
+ * - Per-netstack data structure lock Z.
+ *
+ * # Locking rules
+ *
+ * L.1a N must always be acquired first and released last
+ *
+ * If you need to acquire the netstack list lock, either for reading or writing,
+ * then N must be acquired first and before any other locks. It may not be
+ * dropped before any other lock.
+ *
+ * L.1b N must only be acquired from the administrative path and zone creation,
+ * shutdown, and destruct callbacks.
+ *
+ * The data path, e.g. receiving the per-packet callbacks, should never be
+ * grabbing the list lock. If it is, then the architecture here needs to be
+ * reconsidered.
+ *
+ * L.2 Z cannot be held across calls to the pfhooks subsystem if packet hooks
+ * are active.
+ *
+ * The way the pfhooks subsystem is designed is that a reference count is
+ * present on the hook_t while it is active. As long as that reference count is
+ * non-zero, a call to net_hook_unregister will block until it is lowered.
+ * Because the callbacks want the same lock for the netstack that is held by the
+ * administrative path calling into net_hook_unregister, we deadlock.
+ *
+ * ioctl from ipdadm remove hook_t cb (from nic) hook_t cb (from IP)
+ * ----------------------- -------------------- -------------------
+ * | | |
+ * | bump hook_t refcount |
+ * mutex_enter(ipd_nsl_lock); enter ipd_hook() bump hook_t refcount
+ * mutex acquired mutex_enter(ins->ipdn_lock); |
+ * | mutex acquired enter ipd_hook()
+ * mutex_enter(ins->ipdn_lock); | mutex_enter(ins->ipdn_lock);
+ * | | |
+ * | | |
+ * | mutex_exit(ins->ipdn_lock); |
+ * | | |
+ * mutex acquired leave ipd_hook() |
+ * | decrement hook_t refcount |
+ * | | |
+ * ipd_teardown_hooks() | |
+ * net_hook_unregister() | |
+ * cv_wait() if recount | |
+ * | | |
+ * ---------------------------------------------------------------------------
+ *
+ * At this point, we can see that the second hook callback still doesn't have
+ * the mutex, but it has bumped the hook_t refcount. However, it will never
+ * acquire the mutex that it needs to finish its operation and decrement the
+ * refcount.
+ *
+ * Obviously, deadlocking is not acceptable, thus the following corollary to the
+ * second locking rule:
+ *
+ * L.2 Corollary: If Z is being released across a call to the pfhooks subsystem,
+ * N must be held.
+ *
+ * There is currently only one path where we have to worry about this. That is
+ * when we are removing a hook, but the zone is not being shutdown, then hooks
+ * are currently active. The only place that this currently happens is in
+ * ipd_check_hooks().
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/modctl.h>
+#include <sys/kstat.h>
+#include <sys/neti.h>
+#include <sys/list.h>
+#include <sys/ksynch.h>
+#include <sys/sysmacros.h>
+#include <sys/policy.h>
+#include <sys/atomic.h>
+#include <sys/model.h>
+#include <sys/strsun.h>
+
+#include <sys/netstack.h>
+#include <sys/hook.h>
+#include <sys/hook_event.h>
+
+#include <sys/ipd.h>
+
+#define IPDN_STATUS_DISABLED 0x1
+#define IPDN_STATUS_ENABLED 0x2
+#define IPDN_STATUS_CONDEMNED 0x4
+
+/*
+ * These flags are used to determine whether or not the hooks are registered.
+ */
+#define IPDN_HOOK_NONE 0x0
+#define IPDN_HOOK_V4IN 0x1
+#define IPDN_HOOK_V4OUT 0x2
+#define IPDN_HOOK_V6IN 0x4
+#define IPDN_HOOK_V6OUT 0x8
+#define IPDN_HOOK_ALL 0xf
+
+/*
+ * Per-netstack kstats.
+ */
+typedef struct ipd_nskstat {
+ kstat_named_t ink_ndrops;
+ kstat_named_t ink_ncorrupts;
+ kstat_named_t ink_ndelays;
+} ipd_nskstat_t;
+
+/*
+ * Different parts of this structure have different locking semantics. The list
+ * node is not normally referenced, if it is, one has to hold the ipd_nsl_lock.
+ * The following members are read only: ipdn_netid and ipdn_zoneid. The members
+ * of the kstat structure are always accessible in the data path, but the
+ * counters must be bumped with atomic operations. The ipdn_lock protects every
+ * other aspect of this structure. Please see the big theory statement on the
+ * requirements for lock ordering.
+ */
+typedef struct ipd_netstack {
+ list_node_t ipdn_link; /* link on ipd_nsl */
+ netid_t ipdn_netid; /* netstack id */
+ zoneid_t ipdn_zoneid; /* zone id */
+ kstat_t *ipdn_kstat; /* kstat_t ptr */
+ ipd_nskstat_t ipdn_ksdata; /* kstat data */
+ kmutex_t ipdn_lock; /* protects following members */
+ int ipdn_status; /* status flags */
+ net_handle_t ipdn_v4hdl; /* IPv4 net handle */
+ net_handle_t ipdn_v6hdl; /* IPv4 net handle */
+ int ipdn_hooked; /* are hooks registered */
+ hook_t *ipdn_v4in; /* IPv4 traffic in hook */
+ hook_t *ipdn_v4out; /* IPv4 traffice out hook */
+ hook_t *ipdn_v6in; /* IPv6 traffic in hook */
+ hook_t *ipdn_v6out; /* IPv6 traffic out hook */
+ int ipdn_enabled; /* which perturbs are on */
+ int ipdn_corrupt; /* corrupt percentage */
+ int ipdn_drop; /* drop percentage */
+ uint_t ipdn_delay; /* delay us */
+ long ipdn_rand; /* random seed */
+} ipd_netstack_t;
+
+/*
+ * ipd internal variables
+ */
+static dev_info_t *ipd_devi; /* device info */
+static net_instance_t *ipd_neti; /* net_instance for hooks */
+static unsigned int ipd_max_delay = IPD_MAX_DELAY; /* max delay in us */
+static kmutex_t ipd_nsl_lock; /* lock for the nestack list */
+static list_t ipd_nsl; /* list of netstacks */
+static kmutex_t ipd_nactive_lock; /* lock for nactive */
+static unsigned int ipd_nactive; /* number of active netstacks */
+static int ipd_nactive_fudge = 4; /* amount to fudge by in list */
+
+/*
+ * Note that this random number implementation is based upon the old BSD 4.1
+ * rand. It's good enough for us!
+ */
+static int
+ipd_nextrand(ipd_netstack_t *ins)
+{
+ ins->ipdn_rand = ins->ipdn_rand * 1103515245L + 12345;
+ return (ins->ipdn_rand & 0x7fffffff);
+}
+
+static void
+ipd_ksbump(kstat_named_t *nkp)
+{
+ atomic_inc_64(&nkp->value.ui64);
+}
+
+/*
+ * This is where all the magic actually happens. The way that this works is we
+ * grab the ins lock to basically get a copy of all the data that we need to do
+ * our job and then let it go to minimize contention. In terms of actual work on
+ * the packet we do them in the following order:
+ *
+ * - drop
+ * - delay
+ * - corrupt
+ */
+/*ARGSUSED*/
+static int
+ipd_hook(hook_event_token_t event, hook_data_t data, void *arg)
+{
+ unsigned char *crp;
+ int dwait, corrupt, drop, rand, off, status;
+ mblk_t *mbp;
+ ipd_netstack_t *ins = arg;
+ hook_pkt_event_t *pkt = (hook_pkt_event_t *)data;
+
+ mutex_enter(&ins->ipdn_lock);
+ status = ins->ipdn_status;
+ dwait = ins->ipdn_delay;
+ corrupt = ins->ipdn_corrupt;
+ drop = ins->ipdn_drop;
+ rand = ipd_nextrand(ins);
+ mutex_exit(&ins->ipdn_lock);
+
+ /*
+ * This probably cannot happen, but we'll do an extra guard just in
+ * case.
+ */
+ if (status & IPDN_STATUS_CONDEMNED)
+ return (0);
+
+ if (drop != 0 && rand % 100 < drop) {
+ freemsg(*pkt->hpe_mp);
+ *pkt->hpe_mp = NULL;
+ pkt->hpe_mb = NULL;
+ pkt->hpe_hdr = NULL;
+ ipd_ksbump(&ins->ipdn_ksdata.ink_ndrops);
+
+ return (1);
+ }
+
+ if (dwait != 0) {
+ if (dwait < TICK_TO_USEC(1))
+ drv_usecwait(dwait);
+ else
+ delay(drv_usectohz(dwait));
+ ipd_ksbump(&ins->ipdn_ksdata.ink_ndelays);
+ }
+
+ if (corrupt != 0 && rand % 100 < corrupt) {
+ /*
+ * Since we're corrupting the mblk, just corrupt everything in
+ * the chain. While we could corrupt the entire packet, that's a
+ * little strong. Instead we're going to just change one of the
+ * bytes in each mblock.
+ */
+ mbp = *pkt->hpe_mp;
+ while (mbp != NULL) {
+ if (mbp->b_wptr == mbp->b_rptr)
+ continue;
+
+ /*
+ * While pfhooks probably won't send us anything else,
+ * let's just be extra careful. The stack probably isn't
+ * as resiliant to corruption of control messages.
+ */
+ if (DB_TYPE(mbp) != M_DATA)
+ continue;
+
+ off = rand % ((uintptr_t)mbp->b_wptr -
+ (uintptr_t)mbp->b_rptr);
+ crp = mbp->b_rptr + off;
+ off = rand % 8;
+ *crp = *crp ^ (1 << off);
+
+ mbp = mbp->b_cont;
+ }
+ ipd_ksbump(&ins->ipdn_ksdata.ink_ncorrupts);
+ }
+
+ return (0);
+}
+
+/*
+ * Sets up and registers all the proper hooks needed for the netstack to capture
+ * packets. Callers are assumed to already be holding the ipd_netstack_t's lock.
+ * If there is a failure in setting something up, it is the responsibility of
+ * this function to clean it up. Once this function has been called, it should
+ * not be called until a corresponding call to tear down the hooks has been
+ * done.
+ */
+static int
+ipd_setup_hooks(ipd_netstack_t *ins)
+{
+ ASSERT(MUTEX_HELD(&ins->ipdn_lock));
+ ins->ipdn_v4hdl = net_protocol_lookup(ins->ipdn_netid, NHF_INET);
+ if (ins->ipdn_v4hdl == NULL)
+ goto cleanup;
+
+ ins->ipdn_v6hdl = net_protocol_lookup(ins->ipdn_netid, NHF_INET6);
+ if (ins->ipdn_v6hdl == NULL)
+ goto cleanup;
+
+ ins->ipdn_v4in = hook_alloc(HOOK_VERSION);
+ if (ins->ipdn_v4in == NULL)
+ goto cleanup;
+
+ ins->ipdn_v4in->h_flags = 0;
+ ins->ipdn_v4in->h_hint = HH_NONE;
+ ins->ipdn_v4in->h_hintvalue = 0;
+ ins->ipdn_v4in->h_func = ipd_hook;
+ ins->ipdn_v4in->h_arg = ins;
+ ins->ipdn_v4in->h_name = "ipd IPv4 in";
+
+ if (net_hook_register(ins->ipdn_v4hdl, NH_PHYSICAL_IN,
+ ins->ipdn_v4in) != 0)
+ goto cleanup;
+ ins->ipdn_hooked |= IPDN_HOOK_V4IN;
+
+ ins->ipdn_v4out = hook_alloc(HOOK_VERSION);
+ if (ins->ipdn_v4out == NULL)
+ goto cleanup;
+ ins->ipdn_v4out->h_flags = 0;
+ ins->ipdn_v4out->h_hint = HH_NONE;
+ ins->ipdn_v4out->h_hintvalue = 0;
+ ins->ipdn_v4out->h_func = ipd_hook;
+ ins->ipdn_v4out->h_arg = ins;
+ ins->ipdn_v4out->h_name = "ipd IPv4 out";
+
+ if (net_hook_register(ins->ipdn_v4hdl, NH_PHYSICAL_OUT,
+ ins->ipdn_v4out) != 0)
+ goto cleanup;
+ ins->ipdn_hooked |= IPDN_HOOK_V4OUT;
+
+ ins->ipdn_v6in = hook_alloc(HOOK_VERSION);
+ if (ins->ipdn_v6in == NULL)
+ goto cleanup;
+ ins->ipdn_v6in->h_flags = 0;
+ ins->ipdn_v6in->h_hint = HH_NONE;
+ ins->ipdn_v6in->h_hintvalue = 0;
+ ins->ipdn_v6in->h_func = ipd_hook;
+ ins->ipdn_v6in->h_arg = ins;
+ ins->ipdn_v6in->h_name = "ipd IPv6 in";
+
+ if (net_hook_register(ins->ipdn_v6hdl, NH_PHYSICAL_IN,
+ ins->ipdn_v6in) != 0)
+ goto cleanup;
+ ins->ipdn_hooked |= IPDN_HOOK_V6IN;
+
+ ins->ipdn_v6out = hook_alloc(HOOK_VERSION);
+ if (ins->ipdn_v6out == NULL)
+ goto cleanup;
+ ins->ipdn_v6out->h_flags = 0;
+ ins->ipdn_v6out->h_hint = HH_NONE;
+ ins->ipdn_v6out->h_hintvalue = 0;
+ ins->ipdn_v6out->h_func = ipd_hook;
+ ins->ipdn_v6out->h_arg = ins;
+ ins->ipdn_v6out->h_name = "ipd IPv6 out";
+
+ if (net_hook_register(ins->ipdn_v6hdl, NH_PHYSICAL_OUT,
+ ins->ipdn_v6out) != 0)
+ goto cleanup;
+ ins->ipdn_hooked |= IPDN_HOOK_V6OUT;
+ mutex_enter(&ipd_nactive_lock);
+ ipd_nactive++;
+ mutex_exit(&ipd_nactive_lock);
+
+ return (0);
+
+cleanup:
+ if (ins->ipdn_hooked & IPDN_HOOK_V6OUT)
+ (void) net_hook_unregister(ins->ipdn_v6hdl, NH_PHYSICAL_OUT,
+ ins->ipdn_v6out);
+
+ if (ins->ipdn_hooked & IPDN_HOOK_V6IN)
+ (void) net_hook_unregister(ins->ipdn_v6hdl, NH_PHYSICAL_IN,
+ ins->ipdn_v6in);
+
+ if (ins->ipdn_hooked & IPDN_HOOK_V4OUT)
+ (void) net_hook_unregister(ins->ipdn_v4hdl, NH_PHYSICAL_OUT,
+ ins->ipdn_v4out);
+
+ if (ins->ipdn_hooked & IPDN_HOOK_V4IN)
+ (void) net_hook_unregister(ins->ipdn_v4hdl, NH_PHYSICAL_IN,
+ ins->ipdn_v4in);
+
+ ins->ipdn_hooked = IPDN_HOOK_NONE;
+
+ if (ins->ipdn_v6out != NULL)
+ hook_free(ins->ipdn_v6out);
+
+ if (ins->ipdn_v6in != NULL)
+ hook_free(ins->ipdn_v6in);
+
+ if (ins->ipdn_v4out != NULL)
+ hook_free(ins->ipdn_v4out);
+
+ if (ins->ipdn_v4in != NULL)
+ hook_free(ins->ipdn_v4in);
+
+ if (ins->ipdn_v6hdl != NULL)
+ (void) net_protocol_release(ins->ipdn_v6hdl);
+
+ if (ins->ipdn_v4hdl != NULL)
+ (void) net_protocol_release(ins->ipdn_v4hdl);
+
+ return (1);
+}
+
+static void
+ipd_teardown_hooks(ipd_netstack_t *ins)
+{
+ ASSERT(ins->ipdn_hooked == IPDN_HOOK_ALL);
+ VERIFY(net_hook_unregister(ins->ipdn_v6hdl, NH_PHYSICAL_OUT,
+ ins->ipdn_v6out) == 0);
+ VERIFY(net_hook_unregister(ins->ipdn_v6hdl, NH_PHYSICAL_IN,
+ ins->ipdn_v6in) == 0);
+ VERIFY(net_hook_unregister(ins->ipdn_v4hdl, NH_PHYSICAL_OUT,
+ ins->ipdn_v4out) == 0);
+ VERIFY(net_hook_unregister(ins->ipdn_v4hdl, NH_PHYSICAL_IN,
+ ins->ipdn_v4in) == 0);
+
+ ins->ipdn_hooked = IPDN_HOOK_NONE;
+
+ hook_free(ins->ipdn_v6out);
+ hook_free(ins->ipdn_v6in);
+ hook_free(ins->ipdn_v4out);
+ hook_free(ins->ipdn_v4in);
+
+ VERIFY(net_protocol_release(ins->ipdn_v6hdl) == 0);
+ VERIFY(net_protocol_release(ins->ipdn_v4hdl) == 0);
+
+ mutex_enter(&ipd_nactive_lock);
+ ipd_nactive--;
+ mutex_exit(&ipd_nactive_lock);
+}
+
+static int
+ipd_check_hooks(ipd_netstack_t *ins, int type, boolean_t enable)
+{
+ int olden, rval;
+ olden = ins->ipdn_enabled;
+
+ if (enable)
+ ins->ipdn_enabled |= type;
+ else
+ ins->ipdn_enabled &= ~type;
+
+ /*
+ * If hooks were previously enabled.
+ */
+ if (olden == 0 && ins->ipdn_enabled != 0) {
+ rval = ipd_setup_hooks(ins);
+ if (rval != 0) {
+ ins->ipdn_enabled &= ~type;
+ ASSERT(ins->ipdn_enabled == 0);
+ return (rval);
+ }
+
+ return (0);
+ }
+
+ if (olden != 0 && ins->ipdn_enabled == 0) {
+ ASSERT(olden != 0);
+
+ /*
+ * We have to drop the lock here, lest we cause a deadlock.
+ * Unfortunately, there may be hooks that are running and are
+ * actively in flight and we have to call the unregister
+ * function. Due to the hooks framework, if there is an inflight
+ * hook (most likely right now), and we are holding the
+ * netstack's lock, those hooks will never return. This is
+ * unfortunate.
+ *
+ * Because we only come into this path holding the list lock, we
+ * know that only way that someone else can come in and get to
+ * this structure is via the hook callbacks which are going to
+ * only be doing reads. They'll also see that everything has
+ * been disabled and return. So while this is unfortunate, it
+ * should be relatively safe.
+ */
+ mutex_exit(&ins->ipdn_lock);
+ ipd_teardown_hooks(ins);
+ mutex_enter(&ins->ipdn_lock);
+ return (0);
+ }
+
+ /*
+ * Othwerise, nothing should have changed here.
+ */
+ ASSERT((olden == 0) == (ins->ipdn_enabled == 0));
+ return (0);
+}
+
+static int
+ipd_toggle_corrupt(ipd_netstack_t *ins, int percent)
+{
+ int rval;
+
+ ASSERT(MUTEX_HELD(&ins->ipdn_lock));
+
+ if (percent < 0 || percent > 100)
+ return (ERANGE);
+
+ /*
+ * If we've been asked to set the value to a value that we already have,
+ * great, then we're done.
+ */
+ if (percent == ins->ipdn_corrupt)
+ return (0);
+
+ ins->ipdn_corrupt = percent;
+ rval = ipd_check_hooks(ins, IPD_CORRUPT, percent != 0);
+
+ /*
+ * If ipd_check_hooks_failed, that must mean that we failed to set up
+ * the hooks, so we are going to effectively zero out and fail the
+ * request to enable corruption.
+ */
+ if (rval != 0)
+ ins->ipdn_corrupt = 0;
+
+ return (rval);
+}
+
+static int
+ipd_toggle_delay(ipd_netstack_t *ins, uint32_t delay)
+{
+ int rval;
+
+ ASSERT(MUTEX_HELD(&ins->ipdn_lock));
+
+ if (delay > ipd_max_delay)
+ return (ERANGE);
+
+ /*
+ * If we've been asked to set the value to a value that we already have,
+ * great, then we're done.
+ */
+ if (delay == ins->ipdn_delay)
+ return (0);
+
+ ins->ipdn_delay = delay;
+ rval = ipd_check_hooks(ins, IPD_DELAY, delay != 0);
+
+ /*
+ * If ipd_check_hooks_failed, that must mean that we failed to set up
+ * the hooks, so we are going to effectively zero out and fail the
+ * request to enable corruption.
+ */
+ if (rval != 0)
+ ins->ipdn_delay = 0;
+
+ return (rval);
+}
+static int
+ipd_toggle_drop(ipd_netstack_t *ins, int percent)
+{
+ int rval;
+
+ ASSERT(MUTEX_HELD(&ins->ipdn_lock));
+
+ if (percent < 0 || percent > 100)
+ return (ERANGE);
+
+ /*
+ * If we've been asked to set the value to a value that we already have,
+ * great, then we're done.
+ */
+ if (percent == ins->ipdn_drop)
+ return (0);
+
+ ins->ipdn_drop = percent;
+ rval = ipd_check_hooks(ins, IPD_DROP, percent != 0);
+
+ /*
+ * If ipd_check_hooks_failed, that must mean that we failed to set up
+ * the hooks, so we are going to effectively zero out and fail the
+ * request to enable corruption.
+ */
+ if (rval != 0)
+ ins->ipdn_drop = 0;
+
+ return (rval);
+}
+
+static int
+ipd_ioctl_perturb(ipd_ioc_perturb_t *ipi, cred_t *cr, intptr_t cmd)
+{
+ zoneid_t zid;
+ ipd_netstack_t *ins;
+ int rval = 0;
+
+ /*
+ * If the zone that we're coming from is not the GZ, then we ignore it
+ * completely and then instead just set the zoneid to be that of the
+ * caller. If the zoneid is that of the GZ, then we don't touch this
+ * value.
+ */
+ zid = crgetzoneid(cr);
+ if (zid != GLOBAL_ZONEID)
+ ipi->ipip_zoneid = zid;
+
+ if (zoneid_to_netstackid(ipi->ipip_zoneid) == GLOBAL_NETSTACKID &&
+ zid != GLOBAL_ZONEID)
+ return (EPERM);
+
+ /*
+ * We need to hold the ipd_nsl_lock throughout the entire operation,
+ * otherwise someone else could come in and remove us from the list and
+ * free us, e.g. the netstack destroy handler. By holding the lock, we
+ * stop it from being able to do anything wrong.
+ */
+ mutex_enter(&ipd_nsl_lock);
+ for (ins = list_head(&ipd_nsl); ins != NULL;
+ ins = list_next(&ipd_nsl, ins)) {
+ if (ins->ipdn_zoneid == ipi->ipip_zoneid)
+ break;
+ }
+
+ if (ins == NULL) {
+ mutex_exit(&ipd_nsl_lock);
+ return (EINVAL);
+ }
+
+ mutex_enter(&ins->ipdn_lock);
+
+ if (ins->ipdn_status & IPDN_STATUS_CONDEMNED) {
+ rval = ESHUTDOWN;
+ goto cleanup;
+ }
+
+ switch (cmd) {
+ case IPDIOC_CORRUPT:
+ rval = ipd_toggle_corrupt(ins, ipi->ipip_arg);
+ break;
+ case IPDIOC_DELAY:
+ rval = ipd_toggle_delay(ins, ipi->ipip_arg);
+ break;
+ case IPDIOC_DROP:
+ rval = ipd_toggle_drop(ins, ipi->ipip_arg);
+ break;
+ }
+
+cleanup:
+ mutex_exit(&ins->ipdn_lock);
+ mutex_exit(&ipd_nsl_lock);
+ return (rval);
+}
+
+static int
+ipd_ioctl_remove(ipd_ioc_perturb_t *ipi, cred_t *cr)
+{
+ zoneid_t zid;
+ ipd_netstack_t *ins;
+ int rval = 0;
+
+ /*
+ * See ipd_ioctl_perturb for the rational here.
+ */
+ zid = crgetzoneid(cr);
+ if (zid != GLOBAL_ZONEID)
+ ipi->ipip_zoneid = zid;
+
+ if (zoneid_to_netstackid(ipi->ipip_zoneid) == GLOBAL_NETSTACKID &&
+ zid != GLOBAL_ZONEID)
+ return (EPERM);
+
+ mutex_enter(&ipd_nsl_lock);
+ for (ins = list_head(&ipd_nsl); ins != NULL;
+ ins = list_next(&ipd_nsl, ins)) {
+ if (ins->ipdn_zoneid == ipi->ipip_zoneid)
+ break;
+ }
+
+ if (ins == NULL) {
+ mutex_exit(&ipd_nsl_lock);
+ return (EINVAL);
+ }
+
+ mutex_enter(&ins->ipdn_lock);
+
+ /*
+ * If this is condemned, that means it's very shortly going to be torn
+ * down. In that case, there's no reason to actually do anything here,
+ * as it will all be done rather shortly in the destroy function.
+ * Furthermore, because condemned corresponds with it having hit
+ * shutdown, we know that no more packets can be received by this
+ * netstack. All this translates to a no-op.
+ */
+ if (ins->ipdn_status & IPDN_STATUS_CONDEMNED) {
+ rval = 0;
+ goto cleanup;
+ }
+
+ rval = EINVAL;
+ /*
+ * Go through and disable the requested pieces. We can safely ignore the
+ * return value of ipd_check_hooks because the removal case should never
+ * fail, we verify that in the hook teardown case.
+ */
+ if (ipi->ipip_arg & IPD_CORRUPT) {
+ ins->ipdn_corrupt = 0;
+ (void) ipd_check_hooks(ins, IPD_CORRUPT, B_FALSE);
+ rval = 0;
+ }
+
+ if (ipi->ipip_arg & IPD_DELAY) {
+ ins->ipdn_delay = 0;
+ (void) ipd_check_hooks(ins, IPD_DELAY, B_FALSE);
+ rval = 0;
+ }
+
+ if (ipi->ipip_arg & IPD_DROP) {
+ ins->ipdn_drop = 0;
+ (void) ipd_check_hooks(ins, IPD_DROP, B_FALSE);
+ rval = 0;
+ }
+
+cleanup:
+ mutex_exit(&ins->ipdn_lock);
+ mutex_exit(&ipd_nsl_lock);
+ return (rval);
+}
+
+/*
+ * When this function is called, the value of the ipil_nzones argument controls
+ * how this function works. When called with a value of zero, then we treat that
+ * as the caller asking us what's a reasonable number of entries for me to
+ * allocate memory for. If the zone is the global zone, then we tell them how
+ * many folks are currently active and add a fudge factor. Otherwise the answer
+ * is always one.
+ *
+ * In the non-zero case, we give them that number of zone ids. While this isn't
+ * quite ideal as it might mean that someone misses something, this generally
+ * won't be an issue, as it involves a rather tight race condition in the
+ * current ipdadm implementation.
+ */
+static int
+ipd_ioctl_list(intptr_t arg, cred_t *cr)
+{
+ zoneid_t zid;
+ ipd_ioc_info_t *configs;
+ ipd_netstack_t *ins;
+ uint_t azones, rzones, nzones, cur;
+ int rval = 0;
+ STRUCT_DECL(ipd_ioc_list, h);
+
+ STRUCT_INIT(h, get_udatamodel());
+ if (ddi_copyin((void *)arg, STRUCT_BUF(h),
+ STRUCT_SIZE(h), 0) != 0)
+ return (EFAULT);
+
+ zid = crgetzoneid(cr);
+
+ rzones = STRUCT_FGET(h, ipil_nzones);
+ if (rzones == 0) {
+ if (zid == GLOBAL_ZONEID) {
+ mutex_enter(&ipd_nactive_lock);
+ rzones = ipd_nactive + ipd_nactive_fudge;
+ mutex_exit(&ipd_nactive_lock);
+ } else {
+ rzones = 1;
+ }
+ STRUCT_FSET(h, ipil_nzones, rzones);
+ if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
+ STRUCT_SIZE(h), 0) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ mutex_enter(&ipd_nsl_lock);
+ if (zid == GLOBAL_ZONEID) {
+ azones = ipd_nactive;
+ } else {
+ azones = 1;
+ }
+
+ configs = kmem_alloc(sizeof (ipd_ioc_info_t) * azones, KM_SLEEP);
+ cur = 0;
+ for (ins = list_head(&ipd_nsl); ins != NULL;
+ ins = list_next(&ipd_nsl, ins)) {
+ if (ins->ipdn_enabled == 0)
+ continue;
+
+ ASSERT(cur < azones);
+
+ if (zid == GLOBAL_ZONEID || zid == ins->ipdn_zoneid) {
+ configs[cur].ipii_zoneid = ins->ipdn_zoneid;
+
+ mutex_enter(&ins->ipdn_lock);
+ configs[cur].ipii_corrupt = ins->ipdn_corrupt;
+ configs[cur].ipii_delay = ins->ipdn_delay;
+ configs[cur].ipii_drop = ins->ipdn_drop;
+ mutex_exit(&ins->ipdn_lock);
+
+ ++cur;
+ }
+
+ if (zid != GLOBAL_ZONEID && zid == ins->ipdn_zoneid)
+ break;
+ }
+ mutex_exit(&ipd_nsl_lock);
+
+ ASSERT(zid != GLOBAL_ZONEID || cur == azones);
+
+ if (cur == 0)
+ STRUCT_FSET(h, ipil_nzones, 0);
+ else
+ STRUCT_FSET(h, ipil_nzones, cur);
+
+ nzones = MIN(cur, rzones);
+ if (nzones > 0) {
+ if (ddi_copyout(configs, STRUCT_FGETP(h, ipil_info),
+ nzones * sizeof (ipd_ioc_info_t), NULL) != 0)
+ rval = EFAULT;
+ }
+
+ kmem_free(configs, sizeof (ipd_ioc_info_t) * azones);
+ if (ddi_copyout(STRUCT_BUF(h), (void *)arg, STRUCT_SIZE(h), 0) != 0)
+ return (EFAULT);
+
+ return (rval);
+}
+
+static void *
+ipd_nin_create(const netid_t id)
+{
+ ipd_netstack_t *ins;
+ ipd_nskstat_t *ink;
+
+ ins = kmem_zalloc(sizeof (ipd_netstack_t), KM_SLEEP);
+ ins->ipdn_status = IPDN_STATUS_DISABLED;
+ ins->ipdn_netid = id;
+ ins->ipdn_zoneid = netstackid_to_zoneid(id);
+ ins->ipdn_rand = gethrtime();
+ mutex_init(&ins->ipdn_lock, NULL, MUTEX_DRIVER, NULL);
+
+ ins->ipdn_kstat = net_kstat_create(id, "ipd", ins->ipdn_zoneid,
+ "ipd", "net", KSTAT_TYPE_NAMED,
+ sizeof (ipd_nskstat_t) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL);
+
+ if (ins->ipdn_kstat != NULL) {
+ if (ins->ipdn_zoneid != GLOBAL_ZONEID)
+ kstat_zone_add(ins->ipdn_kstat, GLOBAL_ZONEID);
+
+ ink = &ins->ipdn_ksdata;
+ ins->ipdn_kstat->ks_data = ink;
+ kstat_named_init(&ink->ink_ncorrupts, "corrupts",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&ink->ink_ndrops, "drops", KSTAT_DATA_UINT64);
+ kstat_named_init(&ink->ink_ndelays, "delays",
+ KSTAT_DATA_UINT64);
+ kstat_install(ins->ipdn_kstat);
+ }
+
+ mutex_enter(&ipd_nsl_lock);
+ list_insert_tail(&ipd_nsl, ins);
+ mutex_exit(&ipd_nsl_lock);
+
+ return (ins);
+}
+
+static void
+ipd_nin_shutdown(const netid_t id, void *arg)
+{
+ ipd_netstack_t *ins = arg;
+
+ VERIFY(id == ins->ipdn_netid);
+ mutex_enter(&ins->ipdn_lock);
+ ASSERT(ins->ipdn_status == IPDN_STATUS_DISABLED ||
+ ins->ipdn_status == IPDN_STATUS_ENABLED);
+ ins->ipdn_status |= IPDN_STATUS_CONDEMNED;
+ if (ins->ipdn_kstat != NULL)
+ net_kstat_delete(id, ins->ipdn_kstat);
+ mutex_exit(&ins->ipdn_lock);
+}
+
+/*ARGSUSED*/
+static void
+ipd_nin_destroy(const netid_t id, void *arg)
+{
+ ipd_netstack_t *ins = arg;
+
+ /*
+ * At this point none of the hooks should be able to fire because the
+ * zone has been shutdown and we are in the process of destroying it.
+ * Thus it should not be possible for someone else to come in and grab
+ * our ipd_netstack_t for this zone. Because of that, we know that we
+ * are the only ones who could be running here.
+ */
+ mutex_enter(&ipd_nsl_lock);
+ list_remove(&ipd_nsl, ins);
+ mutex_exit(&ipd_nsl_lock);
+
+ if (ins->ipdn_hooked)
+ ipd_teardown_hooks(ins);
+ mutex_destroy(&ins->ipdn_lock);
+ kmem_free(ins, sizeof (ipd_netstack_t));
+}
+
+/*ARGSUSED*/
+static int
+ipd_open(dev_t *devp, int flag, int otype, cred_t *credp)
+{
+ if (flag & FEXCL || flag & FNDELAY)
+ return (EINVAL);
+
+ if (otype != OTYP_CHR)
+ return (EINVAL);
+
+ if (!(flag & FREAD && flag & FWRITE))
+ return (EINVAL);
+
+ if (secpolicy_ip_config(credp, B_FALSE) != 0)
+ return (EPERM);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+ipd_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
+{
+ int rval;
+ ipd_ioc_perturb_t ipip;
+
+ switch (cmd) {
+ case IPDIOC_CORRUPT:
+ case IPDIOC_DELAY:
+ case IPDIOC_DROP:
+ if (ddi_copyin((void *)arg, &ipip, sizeof (ipd_ioc_perturb_t),
+ 0) != 0)
+ return (EFAULT);
+ rval = ipd_ioctl_perturb(&ipip, cr, cmd);
+ return (rval);
+ case IPDIOC_REMOVE:
+ if (ddi_copyin((void *)arg, &ipip, sizeof (ipd_ioc_perturb_t),
+ 0) != 0)
+ return (EFAULT);
+ rval = ipd_ioctl_remove(&ipip, cr);
+ return (rval);
+ case IPDIOC_LIST:
+ /*
+ * Because the list ioctl doesn't have a fixed-size struct due
+ * to needing to pass around a pointer, we instead delegate the
+ * copyin logic to the list code.
+ */
+ return (ipd_ioctl_list(arg, cr));
+ default:
+ break;
+ }
+ return (ENOTTY);
+}
+
+/*ARGSUSED*/
+static int
+ipd_close(dev_t dev, int flag, int otype, cred_t *credp)
+{
+ return (0);
+}
+
+static int
+ipd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ minor_t instance;
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ if (ipd_devi != NULL)
+ return (DDI_FAILURE);
+
+ instance = ddi_get_instance(dip);
+ if (ddi_create_minor_node(dip, "ipd", S_IFCHR, instance,
+ DDI_PSEUDO, 0) == DDI_FAILURE)
+ return (DDI_FAILURE);
+
+ ipd_neti = net_instance_alloc(NETINFO_VERSION);
+ if (ipd_neti == NULL) {
+ ddi_remove_minor_node(dip, NULL);
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Note that these global structures MUST be initialized before we call
+ * net_instance_register, as that will instantly cause us to drive into
+ * the ipd_nin_create callbacks.
+ */
+ list_create(&ipd_nsl, sizeof (ipd_netstack_t),
+ offsetof(ipd_netstack_t, ipdn_link));
+ mutex_init(&ipd_nsl_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&ipd_nactive_lock, NULL, MUTEX_DRIVER, NULL);
+
+ /* Note, net_instance_alloc sets the version. */
+ ipd_neti->nin_name = "ipd";
+ ipd_neti->nin_create = ipd_nin_create;
+ ipd_neti->nin_destroy = ipd_nin_destroy;
+ ipd_neti->nin_shutdown = ipd_nin_shutdown;
+ if (net_instance_register(ipd_neti) == DDI_FAILURE) {
+ net_instance_free(ipd_neti);
+ ddi_remove_minor_node(dip, NULL);
+ }
+
+ ddi_report_dev(dip);
+ ipd_devi = dip;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+ipd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ int error;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = ipd_devi;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(uintptr_t)getminor((dev_t)arg);
+ error = DDI_SUCCESS;
+ default:
+ error = DDI_FAILURE;
+ break;
+ }
+
+ return (error);
+}
+
+static int
+ipd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ mutex_enter(&ipd_nactive_lock);
+ if (ipd_nactive > 0) {
+ mutex_exit(&ipd_nactive_lock);
+ return (EBUSY);
+ }
+ mutex_exit(&ipd_nactive_lock);
+ ASSERT(dip == ipd_devi);
+ ddi_remove_minor_node(dip, NULL);
+ ipd_devi = NULL;
+
+ if (ipd_neti != NULL) {
+ VERIFY(net_instance_unregister(ipd_neti) == 0);
+ net_instance_free(ipd_neti);
+ }
+
+ mutex_destroy(&ipd_nsl_lock);
+ mutex_destroy(&ipd_nactive_lock);
+ list_destroy(&ipd_nsl);
+
+ return (DDI_SUCCESS);
+}
+
+static struct cb_ops ipd_cb_ops = {
+ ipd_open, /* open */
+ ipd_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ ipd_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ NULL, /* streamtab */
+ D_NEW | D_MP, /* Driver compatibility flag */
+ CB_REV, /* rev */
+ nodev, /* aread */
+ nodev /* awrite */
+};
+
+static struct dev_ops ipd_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ ipd_getinfo, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ ipd_attach, /* attach */
+ ipd_detach, /* detach */
+ nodev, /* reset */
+ &ipd_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev, /* dev power */
+ ddi_quiesce_not_needed /* quiesce */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops,
+ "Internet packet disturber",
+ &ipd_ops
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ { (void *)&modldrv, NULL }
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
diff --git a/usr/src/uts/common/inet/ipd/ipd.conf b/usr/src/uts/common/inet/ipd/ipd.conf
new file mode 100644
index 0000000000..ddf16977cb
--- /dev/null
+++ b/usr/src/uts/common/inet/ipd/ipd.conf
@@ -0,0 +1,17 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+name="ipd" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 033dc2d703..656494c228 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -279,6 +279,7 @@ CHKHDRS= \
ipc.h \
ipc_impl.h \
ipc_rctl.h \
+ ipd.h \
ipmi.h \
isa_defs.h \
iscsi_authclient.h \
diff --git a/usr/src/uts/common/sys/ipd.h b/usr/src/uts/common/sys/ipd.h
new file mode 100644
index 0000000000..bad74f8b81
--- /dev/null
+++ b/usr/src/uts/common/sys/ipd.h
@@ -0,0 +1,81 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * These definitions are private to ipd and ipdadm.
+ */
+
+#ifndef _SYS_IPD_H
+#define _SYS_IPD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IPD_DEV_PATH "/dev/ipd"
+#define IPD_MAX_DELAY 10000 /* 10 ms in us */
+
+typedef struct ipd_ioc_perturb {
+ zoneid_t ipip_zoneid;
+ uint32_t ipip_arg;
+} ipd_ioc_perturb_t;
+
+typedef struct ipd_ioc_info {
+ zoneid_t ipii_zoneid;
+ uint32_t ipii_corrupt;
+ uint32_t ipii_drop;
+ uint32_t ipii_delay;
+} ipd_ioc_info_t;
+
+#ifdef _KERNEL
+
+typedef struct ipd_ioc_list32 {
+ uint_t ipil_nzones;
+ caddr32_t ipil_info;
+} ipd_ioc_list32_t;
+
+#endif /* _KERNEL */
+
+typedef struct ipd_ioc_list {
+ uint_t ipil_nzones;
+ ipd_ioc_info_t *ipil_info;
+} ipd_ioc_list_t;
+
+#define IPD_CORRUPT 0x1
+#define IPD_DELAY 0x2
+#define IPD_DROP 0x4
+
+#define IPDIOC (('i' << 24) | ('p' << 16) | ('d' << 8))
+#define IPDIOC_CORRUPT (IPDIOC | 1) /* disable ipd */
+#define IPDIOC_DELAY (IPDIOC | 2) /* disable ipd */
+#define IPDIOC_DROP (IPDIOC | 3) /* disable ipd */
+#define IPDIOC_LIST (IPDIOC | 4) /* enable ipd */
+#define IPDIOC_REMOVE (IPDIOC | 5) /* disable ipd */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_IPD_H */
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index a713e67480..dcacab9221 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -251,6 +251,7 @@ DRV_KMODS += intel_nb5000
DRV_KMODS += intel_nhm
DRV_KMODS += ip
DRV_KMODS += ip6
+DRV_KMODS += ipd
DRV_KMODS += ipf
DRV_KMODS += ipnet
DRV_KMODS += ippctl
diff --git a/usr/src/uts/intel/ipd/Makefile b/usr/src/uts/intel/ipd/Makefile
new file mode 100644
index 0000000000..27a0b6b79c
--- /dev/null
+++ b/usr/src/uts/intel/ipd/Makefile
@@ -0,0 +1,51 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+UTSBASE = ../..
+
+MODULE = ipd
+OBJECTS = $(IPD_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(IPD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/inet/ipd
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+LDFLAGS += -dy -Nmisc/neti -Nmisch/hook
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/sparc/ipd/Makefile b/usr/src/uts/sparc/ipd/Makefile
new file mode 100644
index 0000000000..27a0b6b79c
--- /dev/null
+++ b/usr/src/uts/sparc/ipd/Makefile
@@ -0,0 +1,51 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+UTSBASE = ../..
+
+MODULE = ipd
+OBJECTS = $(IPD_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(IPD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/inet/ipd
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+LDFLAGS += -dy -Nmisc/neti -Nmisch/hook
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ