svn commit: r353692 - in head: share/man/man4 sys/net sys/netinet/netdump

Conrad Meyer cem at FreeBSD.org
Thu Oct 17 19:49:21 UTC 2019


Author: cem
Date: Thu Oct 17 19:49:20 2019
New Revision: 353692
URL: https://svnweb.freebsd.org/changeset/base/353692

Log:
  Add ddb(4) 'netdump' command to netdump a core without preconfiguration
  
  Add a 'X -s <server> -c <client> [-g <gateway>] -i <interface>' subroutine
  to the generic debugnet code.  The imagined use is both netdump, shown here,
  and NetGDB (vaporware).  It uses the ddb(4) lexer, with some new extensions,
  to parse out IPv4 addresses.
  
  'Netdump' uses the generic debugnet routine to load a configuration and
  start a dump, without any netdump configuration prior to panic.
  
  Loosely derived from work by:	John Reimer <john.reimer AT emc.com>
  Reviewed by:	markj
  Differential Revision:	https://reviews.freebsd.org/D21460

Modified:
  head/share/man/man4/ddb.4
  head/share/man/man4/netdump.4
  head/sys/net/debugnet.c
  head/sys/net/debugnet.h
  head/sys/netinet/netdump/netdump_client.c

Modified: head/share/man/man4/ddb.4
==============================================================================
--- head/share/man/man4/ddb.4	Thu Oct 17 18:45:11 2019	(r353691)
+++ head/share/man/man4/ddb.4	Thu Oct 17 19:49:20 2019	(r353692)
@@ -24,43 +24,9 @@
 .\" any improvements or extensions that they make and grant Carnegie Mellon
 .\" the rights to redistribute these changes.
 .\"
-.\" changed a \# to #, since groff choked on it.
-.\"
-.\" HISTORY
-.\" ddb.4,v
-.\" Revision 1.1  1993/07/15  18:41:02  brezak
-.\" Man page for DDB
-.\"
-.\" Revision 2.6  92/04/08  08:52:57  rpd
-.\" 	Changes from OSF.
-.\" 	[92/01/17  14:19:22  jsb]
-.\" 	Changes for OSF debugger modifications.
-.\" 	[91/12/12            tak]
-.\"
-.\" Revision 2.5  91/06/25  13:50:22  rpd
-.\" 	Added some watchpoint explanation.
-.\" 	[91/06/25            rpd]
-.\"
-.\" Revision 2.4  91/06/17  15:47:31  jsb
-.\" 	Added documentation for continue/c, match, search, and watchpoints.
-.\" 	I've not actually explained what a watchpoint is; maybe Rich can
-.\" 	do that (hint, hint).
-.\" 	[91/06/17  10:58:08  jsb]
-.\"
-.\" Revision 2.3  91/05/14  17:04:23  mrt
-.\" 	Correcting copyright
-.\"
-.\" Revision 2.2  91/02/14  14:10:06  mrt
-.\" 	Changed to new Mach copyright
-.\" 	[91/02/12  18:10:12  mrt]
-.\"
-.\" Revision 2.2  90/08/30  14:23:15  dbg
-.\" 	Created.
-.\" 	[90/08/30            dbg]
-.\"
 .\" $FreeBSD$
 .\"
-.Dd October 10, 2019
+.Dd October 17, 2019
 .Dt DDB 4
 .Os
 .Sh NAME
@@ -212,7 +178,12 @@ arrow keys may be used to
 browse through the history buffer, and move the cursor within the
 current line.
 .Sh COMMANDS
+.Ss COMMON DEBUGGER COMMANDS
 .Bl -tag -width indent -compact
+.It Ic help
+Print a short summary of the available commands and command
+abbreviations.
+.Pp
 .It Xo
 .Ic examine Ns Op Li / Ns Cm AISabcdghilmorsuxz ...
 .Oo Ar addr Oc Ns Op , Ns Ar count
@@ -396,6 +367,9 @@ command, or by omitting
 to get the default address of
 .Va dot .
 .Pp
+.It Ic halt
+Halt the system.
+.Pp
 .It Ic watch Oo Ar addr Oc Ns Op , Ns Ar size
 Set a watchpoint for a region.
 Execution stops when an attempt to modify the region occurs.
@@ -429,6 +403,20 @@ its use on user mode address spaces.
 .It Ic dhwatch Oo Ar addr Oc Ns Op , Ns Ar size
 Delete specified hardware watchpoint.
 .Pp
+.It Ic kill Ar sig pid
+Send signal
+.Ar sig
+to process
+.Ar pid .
+The signal is acted on upon returning from the debugger.
+This command can be used to kill a process causing resource contention
+in the case of a hung system.
+See
+.Xr signal 3
+for a list of signals.
+Note that the arguments are reversed relative to
+.Xr kill 2 .
+.Pp
 .It Ic step Ns Oo Li / Ns Cm p Oc Ns Op , Ns Ar count
 .It Ic s    Ns Oo Li / Ns Cm p Oc Ns Op , Ns Ar count
 Single step
@@ -529,6 +517,25 @@ The optional
 argument limits the search.
 .\"
 .Pp
+.It Ic reboot Op Ar seconds
+.It Ic reset Op Ar seconds
+Hard reset the system.
+If the optional argument
+.Ar seconds
+is given, the debugger will wait for this long, at most a week,
+before rebooting.
+.Pp
+.It Ic thread Ar addr | tid
+Switch the debugger to the thread with ID
+.Ar tid ,
+if the argument is a decimal number, or address
+.Ar addr ,
+otherwise.
+.El
+.Pp
+.Ss SPECIALIZED HELPER COMMANDS
+.Pp
+.Bl -tag -width indent -compact
 .It Xo
 .Ic findstack
 .Ar addr
@@ -1174,47 +1181,36 @@ Shows watchpoints set with "watch" command.
 Shows information about lock acquisition coming from the
 .Xr witness 4
 subsystem.
-.\"
+.El
 .Pp
+.Ss OFFLINE DEBUGGING COMMANDS
+.Bl -tag -width indent -compact
 .It Ic gdb
-Toggles between remote GDB and DDB mode.
+Switches to remote GDB mode.
 In remote GDB mode, another machine is required that runs
 .Xr gdb 1
 using the remote debug feature, with a connection to the serial
 console port on the target machine.
-Currently only available on the
-i386
-architecture.
 .Pp
-.It Ic halt
-Halt the system.
+.It Ic netdump Fl s Ar server Oo Fl g Ar gateway Oc Fl c Ar client Fl i Ar iface
+Configure
+.Xr netdump 4
+with the provided parameters, and immediately perform a netdump.
 .Pp
-.It Ic kill Ar sig pid
-Send signal
-.Ar sig
-to process
-.Ar pid .
-The signal is acted on upon returning from the debugger.
-This command can be used to kill a process causing resource contention
-in the case of a hung system.
-See
-.Xr signal 3
-for a list of signals.
-Note that the arguments are reversed relative to
-.Xr kill 2 .
+There are some known limitations.
+Principally,
+.Xr netdump 4
+only supports IPv4 at this time.
+The address arguments to the
+.Ic netdump
+command must be dotted decimal IPv4 addresses.
+(Hostnames are not supported.)
+At present, the command only works if the machine is in a panic state.
+Finally, the
+.Nm
+.Ic netdump
+command does not provide any way to configure compression or encryption.
 .Pp
-.It Ic reboot Op Ar seconds
-.It Ic reset Op Ar seconds
-Hard reset the system.
-If the optional argument
-.Ar seconds
-is given, the debugger will wait for this long, at most a week,
-before rebooting.
-.Pp
-.It Ic help
-Print a short summary of the available commands and command
-abbreviations.
-.Pp
 .It Ic capture on
 .It Ic capture off
 .It Ic capture reset
@@ -1286,13 +1282,6 @@ rather than a traditional memory dump or minidump.
 reports whether a textdump has been scheduled.
 .Ic textdump unset
 cancels a request to perform a textdump as the next kernel core dump.
-.Pp
-.It Ic thread Ar addr | tid
-Switch the debugger to the thread with ID
-.Ar tid ,
-if the argument is a decimal number, or address
-.Ar addr ,
-otherwise.
 .El
 .Sh VARIABLES
 The debugger accesses registers and variables as

Modified: head/share/man/man4/netdump.4
==============================================================================
--- head/share/man/man4/netdump.4	Thu Oct 17 18:45:11 2019	(r353691)
+++ head/share/man/man4/netdump.4	Thu Oct 17 19:49:20 2019	(r353692)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd December 5, 2018
+.Dd October 17, 2019
 .Dt NETDUMP 4
 .Os
 .Sh NAME
@@ -46,7 +46,10 @@ daemon, available in ports as
 .Nm
 clients are configured using the
 .Xr dumpon 8
-utility.
+utility or the
+.Ic netdump
+command in
+.Xr ddb 4 .
 .Pp
 .Nm
 client messages consist of a fixed-size header followed by a variable-sized

Modified: head/sys/net/debugnet.c
==============================================================================
--- head/sys/net/debugnet.c	Thu Oct 17 18:45:11 2019	(r353691)
+++ head/sys/net/debugnet.c	Thu Oct 17 19:49:20 2019	(r353692)
@@ -31,6 +31,7 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_ddb.h"
 #include "opt_inet.h"
 
 #include <sys/param.h>
@@ -40,6 +41,11 @@ __FBSDID("$FreeBSD$");
 #include <sys/socket.h>
 #include <sys/sysctl.h>
 
+#ifdef DDB
+#include <ddb/ddb.h>
+#include <ddb/db_lex.h>
+#endif
+
 #include <net/ethernet.h>
 #include <net/if.h>
 #include <net/if_arp.h>
@@ -654,3 +660,223 @@ dn_evh_init(void *ctx __unused)
 	    dn_ifnet_event, NULL, EVENTHANDLER_PRI_ANY);
 }
 SYSINIT(dn_evh_init, SI_SUB_EVENTHANDLER + 1, SI_ORDER_ANY, dn_evh_init, NULL);
+
+/*
+ * DDB parsing helpers for debugnet(4) consumers.
+ */
+#ifdef DDB
+struct my_inet_opt {
+	bool has_opt;
+	const char *printname;
+	in_addr_t *result;
+};
+
+static int
+dn_parse_optarg_ipv4(struct my_inet_opt *opt)
+{
+	in_addr_t tmp;
+	unsigned octet;
+	int t;
+
+	tmp = 0;
+	for (octet = 0; octet < 4; octet++) {
+		t = db_read_token_flags(DRT_WSPACE | DRT_DECIMAL);
+		if (t != tNUMBER) {
+			db_printf("%s:%s: octet %u expected number; found %d\n",
+			    __func__, opt->printname, octet, t);
+			return (EINVAL);
+		}
+		/*
+		 * db_lex lexes '-' distinctly from the number itself, but
+		 * let's document that invariant.
+		 */
+		MPASS(db_tok_number >= 0);
+
+		if (db_tok_number > UINT8_MAX) {
+			db_printf("%s:%s: octet %u out of range: %jd\n", __func__,
+			    opt->printname, octet, (intmax_t)db_tok_number);
+			return (EDOM);
+		}
+
+		/* Constructed host-endian and converted to network later. */
+		tmp = (tmp << 8) | db_tok_number;
+
+		if (octet < 3) {
+			t = db_read_token_flags(DRT_WSPACE);
+			if (t != tDOT) {
+				db_printf("%s:%s: octet %u expected '.'; found"
+				    " %d\n", __func__, opt->printname, octet,
+				    t);
+				return (EINVAL);
+			}
+		}
+	}
+
+	*opt->result = htonl(tmp);
+	opt->has_opt = true;
+	return (0);
+}
+
+int
+debugnet_parse_ddb_cmd(const char *cmd, struct debugnet_ddb_config *result)
+{
+	struct ifnet *ifp;
+	int t, error;
+	bool want_ifp;
+	char ch;
+
+	struct my_inet_opt opt_client = {
+		.printname = "client",
+		.result = &result->dd_client,
+	},
+	opt_server = {
+		.printname = "server",
+		.result = &result->dd_server,
+	},
+	opt_gateway = {
+		.printname = "gateway",
+		.result = &result->dd_gateway,
+	},
+	*cur_inet_opt;
+
+	ifp = NULL;
+	memset(result, 0, sizeof(*result));
+
+	/*
+	 * command [space] [-] [opt] [[space] [optarg]] ...
+	 *
+	 * db_command has already lexed 'command' for us.
+	 */
+	t = db_read_token_flags(DRT_WSPACE);
+	if (t == tWSPACE)
+		t = db_read_token_flags(DRT_WSPACE);
+
+	while (t != tEOL) {
+		if (t != tMINUS) {
+			db_printf("%s: Bad syntax; expected '-', got %d\n",
+			    cmd, t);
+			goto usage;
+		}
+
+		t = db_read_token_flags(DRT_WSPACE);
+		if (t != tIDENT) {
+			db_printf("%s: Bad syntax; expected tIDENT, got %d\n",
+			    cmd, t);
+			goto usage;
+		}
+
+		if (strlen(db_tok_string) > 1) {
+			db_printf("%s: Bad syntax; expected single option "
+			    "flag, got '%s'\n", cmd, db_tok_string);
+			goto usage;
+		}
+
+		want_ifp = false;
+		cur_inet_opt = NULL;
+		switch ((ch = db_tok_string[0])) {
+		default:
+			DNETDEBUG("Unexpected: '%c'\n", ch);
+			/* FALLTHROUGH */
+		case 'h':
+			goto usage;
+		case 'c':
+			cur_inet_opt = &opt_client;
+			break;
+		case 'g':
+			cur_inet_opt = &opt_gateway;
+			break;
+		case 's':
+			cur_inet_opt = &opt_server;
+			break;
+		case 'i':
+			want_ifp = true;
+			break;
+		}
+
+		t = db_read_token_flags(DRT_WSPACE);
+		if (t != tWSPACE) {
+			db_printf("%s: Bad syntax; expected space after "
+			    "flag %c, got %d\n", cmd, ch, t);
+			goto usage;
+		}
+
+		if (want_ifp) {
+			t = db_read_token_flags(DRT_WSPACE);
+			if (t != tIDENT) {
+				db_printf("%s: Expected interface but got %d\n",
+				    cmd, t);
+				goto usage;
+			}
+
+			CURVNET_SET(vnet0);
+			/*
+			 * We *don't* take a ref here because the only current
+			 * consumer, db_netdump_cmd, does not need it.  It
+			 * (somewhat redundantly) extracts the if_name(),
+			 * re-lookups the ifp, and takes its own reference.
+			 */
+			ifp = ifunit(db_tok_string);
+			CURVNET_RESTORE();
+			if (ifp == NULL) {
+				db_printf("Could not locate interface %s\n",
+				    db_tok_string);
+				goto cleanup;
+			}
+		} else {
+			MPASS(cur_inet_opt != NULL);
+			/* Assume IPv4 for now. */
+			error = dn_parse_optarg_ipv4(cur_inet_opt);
+			if (error != 0)
+				goto cleanup;
+		}
+
+		/* Skip (mandatory) whitespace after option, if not EOL. */
+		t = db_read_token_flags(DRT_WSPACE);
+		if (t == tEOL)
+			break;
+		if (t != tWSPACE) {
+			db_printf("%s: Bad syntax; expected space after "
+			    "flag %c option; got %d\n", cmd, ch, t);
+			goto usage;
+		}
+		t = db_read_token_flags(DRT_WSPACE);
+	}
+
+	/* Currently, all three are required. */
+	if (!opt_client.has_opt || !opt_server.has_opt || ifp == NULL) {
+		db_printf("%s needs all of client, server, and interface "
+		    "specified.\n", cmd);
+		goto usage;
+	}
+
+	result->dd_has_gateway = opt_gateway.has_opt;
+
+	/* Iface validation stolen from netdump_configure. */
+	if (!DEBUGNET_SUPPORTED_NIC(ifp)) {
+		db_printf("%s: interface '%s' does not support debugnet\n",
+		    cmd, if_name(ifp));
+		error = ENODEV;
+		goto cleanup;
+	}
+	if ((if_getflags(ifp) & IFF_UP) == 0) {
+		db_printf("%s: interface '%s' link is down\n", cmd,
+		    if_name(ifp));
+		error = ENXIO;
+		goto cleanup;
+	}
+
+	result->dd_ifp = ifp;
+
+	/* We parsed the full line to tEOL already, or bailed with an error. */
+	return (0);
+
+usage:
+	db_printf("Usage: %s -s <server> [-g <gateway>] -c <localip> "
+	    "-i <interface>\n", cmd);
+	error = EINVAL;
+	/* FALLTHROUGH */
+cleanup:
+	db_skip_to_eol();
+	return (error);
+}
+#endif /* DDB */

Modified: head/sys/net/debugnet.h
==============================================================================
--- head/sys/net/debugnet.h	Thu Oct 17 18:45:11 2019	(r353691)
+++ head/sys/net/debugnet.h	Thu Oct 17 19:49:20 2019	(r353692)
@@ -173,6 +173,29 @@ const unsigned char *debugnet_get_gw_mac(const struct 
  */
 void debugnet_any_ifnet_update(struct ifnet *);
 
+/*
+ * DDB parsing helper for common debugnet options.
+ *
+ * -s <server> [-g <gateway] -c <localip> -i <interface>
+ *
+ * Order is not significant.  Interface is an online interface that supports
+ * debugnet and can route to the debugnet server.  The other parameters are all
+ * IP addresses.  For now, all parameters are mandatory, except gateway.
+ *
+ * Provides basic '-h' using provided 'cmd' string.
+ *
+ * Returns zero on success, or errno.
+ */
+struct debugnet_ddb_config {
+	struct ifnet	*dd_ifp;	/* not ref'd */
+	in_addr_t	dd_client;
+	in_addr_t	dd_server;
+	in_addr_t	dd_gateway;
+	bool		dd_has_gateway : 1;
+};
+int debugnet_parse_ddb_cmd(const char *cmd,
+    struct debugnet_ddb_config *result);
+
 /* Expose sysctl variables for netdump(4) to alias. */
 extern int debugnet_npolls;
 extern int debugnet_nretries;

Modified: head/sys/netinet/netdump/netdump_client.c
==============================================================================
--- head/sys/netinet/netdump/netdump_client.c	Thu Oct 17 18:45:11 2019	(r353691)
+++ head/sys/netinet/netdump/netdump_client.c	Thu Oct 17 19:49:20 2019	(r353692)
@@ -34,6 +34,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_ddb.h"
+
 #include <sys/param.h>
 #include <sys/conf.h>
 #include <sys/disk.h>
@@ -52,6 +54,11 @@ __FBSDID("$FreeBSD$");
 #include <sys/syslog.h>
 #include <sys/systm.h>
 
+#ifdef DDB
+#include <ddb/ddb.h>
+#include <ddb/db_lex.h>
+#endif
+
 #include <net/ethernet.h>
 #include <net/if.h>
 #include <net/if_arp.h>
@@ -387,15 +394,24 @@ netdump_ifdetach(void *arg __unused, struct ifnet *ifp
 	NETDUMP_WUNLOCK();
 }
 
+/*
+ * td of NULL is a sentinel value that indicates a kernel caller (ddb(4) or
+ * modload-based tunable parameters).
+ */
 static int
 netdump_configure(struct diocskerneldump_arg *conf, struct thread *td)
 {
 	struct ifnet *ifp;
+	struct vnet *vnet;
 
 	NETDUMP_ASSERT_WLOCKED();
 
-	CURVNET_SET(TD_TO_VNET(td));
-	if (!IS_DEFAULT_VNET(curvnet)) {
+	if (td != NULL)
+		vnet = TD_TO_VNET(td);
+	else
+		vnet = vnet0;
+	CURVNET_SET(vnet);
+	if (td != NULL && !IS_DEFAULT_VNET(curvnet)) {
 		CURVNET_RESTORE();
 		return (EINVAL);
 	}
@@ -699,7 +715,7 @@ netdump_modevent(module_t mod __unused, int what, void
 
 			/* Ignore errors; we print a message to the console. */
 			NETDUMP_WLOCK();
-			(void)netdump_configure(&conf, curthread);
+			(void)netdump_configure(&conf, NULL);
 			NETDUMP_WUNLOCK();
 		}
 		break;
@@ -729,3 +745,72 @@ static moduledata_t netdump_mod = {
 
 MODULE_VERSION(netdump, 1);
 DECLARE_MODULE(netdump, netdump_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
+
+#ifdef DDB
+/*
+ * Usage: netdump -s <server> [-g <gateway] -c <localip> -i <interface>
+ *
+ * Order is not significant.
+ *
+ * Currently, this command does not support configuring encryption or
+ * compression.
+ */
+DB_FUNC(netdump, db_netdump_cmd, db_cmd_table, CS_OWN, NULL)
+{
+	static struct diocskerneldump_arg conf;
+	static char blockbuf[NETDUMP_DATASIZE];
+	static union {
+		struct dumperinfo di;
+		/* For valid di_devname. */
+		char di_buf[sizeof(struct dumperinfo) + 1];
+	} u;
+
+	struct debugnet_ddb_config params;
+	int error;
+
+	error = debugnet_parse_ddb_cmd("netdump", &params);
+	if (error != 0) {
+		db_printf("Error configuring netdump: %d\n", error);
+		return;
+	}
+
+	/* Translate to a netdump dumper config. */
+	memset(&conf, 0, sizeof(conf));
+	strlcpy(conf.kda_iface, if_name(params.dd_ifp), sizeof(conf.kda_iface));
+
+	conf.kda_af = AF_INET;
+	conf.kda_server.in4 = (struct in_addr) { params.dd_server };
+	conf.kda_client.in4 = (struct in_addr) { params.dd_client };
+	if (params.dd_has_gateway)
+		conf.kda_gateway.in4 = (struct in_addr) { params.dd_gateway };
+	else
+		conf.kda_gateway.in4 = (struct in_addr) { INADDR_ANY };
+
+	/* Set the global netdump config to these options. */
+	error = netdump_configure(&conf, NULL);
+	if (error != 0) {
+		db_printf("Error enabling netdump: %d\n", error);
+		return;
+	}
+
+	/* Fake the generic dump configuration list entry to avoid malloc. */
+	memset(&u.di_buf, 0, sizeof(u.di_buf));
+	u.di.dumper_start = netdump_start;
+	u.di.dumper_hdr = netdump_write_headers;
+	u.di.dumper = netdump_dumper;
+	u.di.priv = NULL;
+	u.di.blocksize = NETDUMP_DATASIZE;
+	u.di.maxiosize = MAXDUMPPGS * PAGE_SIZE;
+	u.di.mediaoffset = 0;
+	u.di.mediasize = 0;
+	u.di.blockbuf = blockbuf;
+
+	dumper_ddb_insert(&u.di);
+
+	error = doadump(false);
+
+	dumper_ddb_remove(&u.di);
+	if (error != 0)
+		db_printf("Cannot dump: %d\n", error);
+}
+#endif /* DDB */


More information about the svn-src-head mailing list