summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--manifest4
-rw-r--r--usr/src/cmd/Makefile3
-rw-r--r--usr/src/cmd/devfsadm/misc_link.c4
-rw-r--r--usr/src/cmd/ipdadm/Makefile60
-rw-r--r--usr/src/cmd/ipdadm/ipdadm.c431
-rw-r--r--usr/src/lib/brand/joyent/zone/platform.xml3
-rw-r--r--usr/src/man/man1m/Makefile3
-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/uts/common/Makefile.files4
-rw-r--r--usr/src/uts/common/Makefile.rules9
-rw-r--r--usr/src/uts/common/inet/ipd/ipd.c1226
-rw-r--r--usr/src/uts/common/inet/ipd/ipd.conf27
-rw-r--r--usr/src/uts/common/sys/ipd.h82
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared3
-rw-r--r--usr/src/uts/intel/ipd/Makefile61
21 files changed, 2188 insertions, 22 deletions
diff --git a/manifest b/manifest
index 47771376bc..d84d07ebe0 100644
--- a/manifest
+++ b/manifest
@@ -585,6 +585,7 @@ f kernel/drv/amd64/intel_nhm 0755 root sys
f kernel/drv/amd64/intel_nhmex 0755 root sys
f kernel/drv/amd64/ip 0755 root sys
f kernel/drv/amd64/ip6 0755 root sys
+f kernel/drv/amd64/ipd 0755 root sys
f kernel/drv/amd64/ipnet 0755 root sys
f kernel/drv/amd64/ippctl 0755 root sys
f kernel/drv/amd64/iprb 0755 root sys
@@ -726,6 +727,7 @@ f kernel/drv/intel_nhm.conf 0644 root sys
f kernel/drv/intel_nhmex.conf 0644 root sys
f kernel/drv/ip.conf 0644 root sys
f kernel/drv/ip6.conf 0644 root sys
+f kernel/drv/ipd.conf 0644 root sys
f kernel/drv/ipnet.conf 0644 root sys
f kernel/drv/ippctl.conf 0644 root sys
f kernel/drv/ipsecah.conf 0644 root sys
@@ -10326,6 +10328,7 @@ s usr/sbin/installgrub=../../sbin/installgrub
h usr/sbin/intrstat=usr/lib/isaexec
f usr/sbin/ipaddrsel 0555 root bin
s usr/sbin/ipadm=../../sbin/ipadm
+f usr/sbin/ipdadm 0555 root bin
h usr/sbin/ipf=usr/lib/isaexec
h usr/sbin/ipfs=usr/lib/isaexec
h usr/sbin/ipfstat=usr/lib/isaexec
@@ -11825,6 +11828,7 @@ f usr/share/man/man1m/installgrub.1m 0444 root bin
f usr/share/man/man1m/intrstat.1m 0444 root bin
f usr/share/man/man1m/iostat.1m 0444 root bin
f usr/share/man/man1m/ipaddrsel.1m 0444 root bin
+f usr/share/man/man1m/ipdadm.1m 0444 root bin
f usr/share/man/man1m/ipf.1m 0444 root bin
f usr/share/man/man1m/ipmpstat.1m 0444 root bin
f usr/share/man/man1m/ipsecalgs.1m 0444 root bin
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 6c903a3f0a..076e46238a 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.
include ../Makefile.master
@@ -204,6 +204,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..c278da2241
--- /dev/null
+++ b/usr/src/cmd/ipdadm/Makefile
@@ -0,0 +1,60 @@
+#
+# 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.
+#
+
+PROG= ipdadm
+OBJS = ipdadm.o
+SRCS = $(OBJS:%.o=../%.c)
+
+
+include ../Makefile.cmd
+include ../Makefile.ctf
+
+#
+# The ipd.h interface is currently private and not shipped with the system. As
+# such it is only useful for building this command and so we manually set
+# cpppath to include it, just as we do for cpuid_drv.h.
+#
+CPPFLAGS += -I../../uts/common
+
+CLEANFILES += $(OBJS)
+CFLAGS += $(CCVERBOSE)
+
+.KEEP_STATE:
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(CTFCONVERT_O)
+
+clean:
+ -$(RM) $(CLEANFILES)
+
+lint: lint_PROG
+
+%.o: ../%.c
+ $(COMPILE.c) $<
+
+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..f87b8087e0
--- /dev/null
+++ b/usr/src/cmd/ipdadm/ipdadm.c
@@ -0,0 +1,431 @@
+/*
+ * 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.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <zone.h>
+#include <libgen.h>
+#include <assert.h>
+
+#include <sys/ipd.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" },
+ { "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 int
+ipdadm_open(void)
+{
+ int fd;
+ fd = open(IPD_DEV_PATH, O_RDWR);
+ if (fd < 0) {
+ (void) fprintf(stderr, "%s: failed to open %s: %s\n", g_pname,
+ IPD_DEV_PATH, strerror(errno));
+ exit(E_ERROR);
+ }
+ return (fd);
+}
+
+/*ARGSUSED*/
+static int
+ipdadm_list(int argc, char *argv[])
+{
+ int fd, rval;
+ unsigned int ii;
+ ipd_ioc_list_t ipil;
+ char zonename[ZONENAME_MAX];
+
+ if (argc != 0)
+ return (usage(stderr));
+
+ fd = ipdadm_open();
+ (void) memset(&ipil, '\0', sizeof (ipd_ioc_list_t));
+
+ rval = ioctl(fd, IPDIOC_LIST, &ipil);
+ if (rval != 0) {
+ (void) fprintf(stderr, "%s: failed to get list info: %s\n",
+ g_pname, strerror(errno));
+ return (E_ERROR);
+ }
+
+ ipil.ipil_list = malloc(sizeof (zoneid_t) * ipil.ipil_nzones);
+ if (ipil.ipil_list == NULL) {
+ (void) fprintf(stderr, "%s: failed to allocate memory: %s\n",
+ g_pname, strerror(errno));
+ return (E_ERROR);
+ }
+
+ rval = ioctl(fd, IPDIOC_LIST, &ipil);
+ if (rval != 0) {
+ free(ipil.ipil_list);
+ (void) fprintf(stderr, "%s: failed to get list info: %s\n",
+ g_pname, strerror(errno));
+ return (E_ERROR);
+ }
+
+ for (ii = 0; ii < ipil.ipil_nzones; ii++) {
+ if (getzonenamebyid(ipil.ipil_list[ii], zonename,
+ sizeof (zonename)) < 0) {
+ (void) fprintf(stderr, "%s: failed to get zonename: "
+ "%s\n", g_pname, strerror(errno));
+ return (E_ERROR);
+ }
+ (void) printf("%s\n", zonename);
+ }
+
+ return (E_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+ipdadm_info(int argc, char *argv[])
+{
+ int rval, fd;
+ ipd_ioc_info_t ipii;
+
+ if (argc != 0)
+ return (usage(stderr));
+
+ ipii.ipii_zoneid = g_zid;
+ fd = ipdadm_open();
+ rval = ioctl(fd, IPDIOC_INFO, &ipii);
+ (void) close(fd);
+ if (rval != 0) {
+ (void) fprintf(stderr, "%s: failed to get info: %s\n",
+ g_pname, strerror(errno));
+ return (E_ERROR);
+ }
+
+ (void) printf("ipd information for zone %s:\n",
+ g_zonename);
+ (void) printf("\tcorrupt:\t%d%% chance of packet corruption\n",
+ ipii.ipii_corrupt);
+ (void) printf("\tdrop:\t\t%d%% chance of packet drop\n",
+ ipii.ipii_drop);
+ (void) printf("\tdelay:\t\t%d microsecond delay per packet\n",
+ ipii.ipii_delay);
+
+ 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_ioc_perturb_t ipip;
+
+ if (argc != 1) {
+ (void) fprintf(stderr, "%s: corrupt <percentage>\n",
+ g_pname);
+ return (usage(stderr));
+ }
+
+ val = ipdadm_parse_long(argv[0], "corrupt", 0, 100);
+ fd = ipdadm_open();
+ ipip.ipip_zoneid = g_zid;
+ ipip.ipip_arg = val;
+ rval = ioctl(fd, IPDIOC_CORRUPT, &ipip);
+ (void) close(fd);
+ if (rval == -1) {
+ (void) fprintf(stderr, "%s: failed to change corrupt "
+ "value: %s\n", g_pname, strerror(errno));
+ return (E_ERROR);
+ }
+
+ return (E_SUCCESS);
+}
+
+static int
+ipdadm_delay(int argc, char *argv[])
+{
+ long val;
+ int fd, rval;
+ ipd_ioc_perturb_t ipip;
+
+ if (argc != 1) {
+ (void) fprintf(stderr, "%s: delay <microseconds>\n",
+ g_pname);
+ return (usage(stderr));
+ }
+
+ val = ipdadm_parse_long(argv[0], "delay", 0, IPD_MAX_DELAY);
+ fd = ipdadm_open();
+ ipip.ipip_zoneid = g_zid;
+ ipip.ipip_arg = val;
+ rval = ioctl(fd, IPDIOC_DELAY, &ipip);
+ (void) close(fd);
+ if (rval == -1) {
+ (void) fprintf(stderr, "%s: failed to change delay value: %s\n",
+ g_pname, strerror(errno));
+ return (E_ERROR);
+ }
+
+ return (E_SUCCESS);
+}
+
+static int
+ipdadm_drop(int argc, char *argv[])
+{
+ long val;
+ int fd, rval;
+ ipd_ioc_perturb_t ipip;
+
+ if (argc != 1) {
+ (void) fprintf(stderr, "%s: drop <percentage>\n",
+ g_pname);
+ return (usage(stderr));
+ }
+
+ val = ipdadm_parse_long(argv[0], "drop", 0, 100);
+ fd = ipdadm_open();
+ ipip.ipip_zoneid = g_zid;
+ ipip.ipip_arg = val;
+ rval = ioctl(fd, IPDIOC_DROP, &ipip);
+ (void) close(fd);
+ if (rval == -1) {
+ (void) fprintf(stderr, "%s: failed to change drop value: %s\n",
+ g_pname, strerror(errno));
+ return (E_ERROR);
+ }
+
+ return (E_SUCCESS);
+}
+
+static int
+ipdadm_remove_valid(const char *str)
+{
+ if (strcmp(str, "corrupt") == 0) {
+ return (IPD_CORRUPT);
+ } else if (strcmp(str, "drop") == 0) {
+ return (IPD_DROP);
+ } else if (strcmp(str, "delay") == 0) {
+ return (IPD_DELAY);
+ }
+
+ return (0);
+}
+
+static int
+ipdadm_remove(int argc, char *argv[])
+{
+ ipd_ioc_perturb_t ipi;
+ 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);
+ }
+
+ ipi.ipip_zoneid = g_zid;
+ ipi.ipip_arg = 0;
+
+ 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);
+ }
+ ipi.ipip_arg |= 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);
+ }
+ ipi.ipip_arg |= rval;
+
+ fd = ipdadm_open();
+ rval = ioctl(fd, IPDIOC_REMOVE, &ipi);
+ (void) close(fd);
+ if (rval == -1) {
+ (void) fprintf(stderr, "%s: failed to remove instances: %s\n",
+ g_pname, strerror(errno));
+ 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/brand/joyent/zone/platform.xml b/usr/src/lib/brand/joyent/zone/platform.xml
index 1449de8973..7057623c1d 100644
--- a/usr/src/lib/brand/joyent/zone/platform.xml
+++ b/usr/src/lib/brand/joyent/zone/platform.xml
@@ -20,7 +20,7 @@
CDDL HEADER END
- Copyright 2010, 2011 Joyent, Inc. All rights reserved.
+ Copyright (c) 2012 Joyent, Inc. All rights reserved.
Use is subject to license terms.
DO NOT EDIT THIS FILE.
@@ -114,6 +114,7 @@
<device match="ip" ip-type="exclusive" />
<device match="ip6" ip-type="exclusive" />
<device match="ipauth" ip-type="exclusive" />
+ <device match="ipd" ip-type="exclusive" />
<device match="ipf" ip-type="exclusive" />
<device match="ipl" ip-type="exclusive" />
<device match="iplookup" ip-type="exclusive" />
diff --git a/usr/src/man/man1m/Makefile b/usr/src/man/man1m/Makefile
index 6af88a31a9..6413a5bc28 100644
--- a/usr/src/man/man1m/Makefile
+++ b/usr/src/man/man1m/Makefile
@@ -12,7 +12,7 @@
#
# Copyright 2011, Richard Lowe
# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
-# Copyright 2012 Joyent, Inc. All rights reserved.
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
#
include ../../Makefile.master
@@ -237,6 +237,7 @@ COMMON_MANFILES = 6to4relay.1m \
intrstat.1m \
iostat.1m \
ipaddrsel.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/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index 5b5abb27fc..34255038cf 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -25,7 +25,7 @@
#
# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
-# Copyright 2012 Joyent, Inc. All rights reserved.
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
#
#
@@ -1742,6 +1742,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 281388e9f0..da81ebc737 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -25,7 +25,7 @@
#
# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
-# Copyright 2012 Joyent, Inc. All rights reserved.
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
#
#
@@ -513,6 +513,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..a6a09b043e
--- /dev/null
+++ b/usr/src/uts/common/inet/ipd/ipd.c
@@ -0,0 +1,1226 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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/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);
+}
+
+static int
+ipd_ioctl_info(ipd_ioc_info_t *ipi, cred_t *cr)
+{
+ zoneid_t zid;
+ ipd_netstack_t *ins;
+
+ /*
+ * See ipd_ioctl_perturb for the rational here.
+ */
+ zid = crgetzoneid(cr);
+ if (zid != GLOBAL_ZONEID)
+ ipi->ipii_zoneid = zid;
+
+ if (zoneid_to_netstackid(ipi->ipii_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->ipii_zoneid)
+ break;
+ }
+
+ if (ins == NULL) {
+ mutex_exit(&ipd_nsl_lock);
+ return (EINVAL);
+ }
+
+ mutex_enter(&ins->ipdn_lock);
+ ipi->ipii_corrupt = ins->ipdn_corrupt;
+ ipi->ipii_delay = ins->ipdn_delay;
+ ipi->ipii_drop = ins->ipdn_drop;
+ mutex_exit(&ins->ipdn_lock);
+ mutex_exit(&ipd_nsl_lock);
+
+ return (0);
+}
+
+/*
+ * 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;
+ zoneid_t *zoneids;
+ ipd_netstack_t *ins;
+ uint_t nzoneids, rzids, 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);
+
+ rzids = STRUCT_FGET(h, ipil_nzones);
+ if (rzids == 0) {
+ if (zid == GLOBAL_ZONEID) {
+ mutex_enter(&ipd_nactive_lock);
+ rzids = ipd_nactive + ipd_nactive_fudge;
+ mutex_exit(&ipd_nactive_lock);
+ } else {
+ rzids = 1;
+ }
+ STRUCT_FSET(h, ipil_nzones, rzids);
+ 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) {
+ nzoneids = ipd_nactive;
+ } else {
+ nzoneids = 1;
+ }
+
+ zoneids = kmem_alloc(sizeof (zoneid_t) * nzoneids, KM_SLEEP);
+ cur = 0;
+ for (ins = list_head(&ipd_nsl); ins != NULL;
+ ins = list_next(&ipd_nsl, ins)) {
+ if (ins->ipdn_enabled == 0)
+ continue;
+
+ if (zid == GLOBAL_ZONEID || zid == ins->ipdn_zoneid) {
+ zoneids[cur++] = ins->ipdn_zoneid;
+ }
+
+ if (zid != GLOBAL_ZONEID && zid == ins->ipdn_zoneid)
+ break;
+ }
+ ASSERT(cur == nzoneids);
+ mutex_exit(&ipd_nsl_lock);
+
+ STRUCT_FSET(h, ipil_nzones, nzoneids);
+ if (nzoneids < rzids)
+ rzids = nzoneids;
+ if (ddi_copyout(zoneids, STRUCT_FGETP(h, ipil_list),
+ nzoneids * sizeof (zoneid_t), NULL) != 0)
+ rval = EFAULT;
+
+ kmem_free(zoneids, sizeof (zoneid_t) * nzoneids);
+ 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;
+ ipd_ioc_info_t ipii;
+
+ 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));
+ case IPDIOC_INFO:
+ if (ddi_copyin((void *)arg, &ipii, sizeof (ipd_ioc_info_t),
+ 0) != 0)
+ return (EFAULT);
+ rval = ipd_ioctl_info(&ipii, cr);
+ if (rval != 0)
+ return (rval);
+ if (ddi_copyout(&ipii, (void *)arg, sizeof (ipd_ioc_info_t),
+ 0) != 0)
+ return (EFAULT);
+ return (0);
+ 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);
+
+ 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..83b9b685f4
--- /dev/null
+++ b/usr/src/uts/common/inet/ipd/ipd.conf
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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.
+# Use is subject to license terms.
+#
+
+name="ipd" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/sys/ipd.h b/usr/src/uts/common/sys/ipd.h
new file mode 100644
index 0000000000..8f006b9054
--- /dev/null
+++ b/usr/src/uts/common/sys/ipd.h
@@ -0,0 +1,82 @@
+/*
+ * 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_list;
+} ipd_ioc_list32_t;
+
+#endif /* _KERNEL */
+
+typedef struct ipd_ioc_list {
+ uint_t ipil_nzones;
+ zoneid_t *ipil_list;
+} 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_INFO (IPDIOC | 5) /* disable ipd */
+#define IPDIOC_REMOVE (IPDIOC | 6) /* 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 92412499b6..4def75f7ee 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -22,7 +22,7 @@
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
-# Copyright 2012 Joyent, Inc. All rights reserved.
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
#
# This makefile contains the common definitions for all intel
@@ -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..1bb76d0728
--- /dev/null
+++ b/usr/src/uts/intel/ipd/Makefile
@@ -0,0 +1,61 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 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