Index: sys/net/debugnet.h =================================================================== --- sys/net/debugnet.h +++ sys/net/debugnet.h @@ -177,6 +177,31 @@ */ void debugnet_any_ifnet_update(struct ifnet *); +/* + * DDB parsing helper for common debugnet options. + * + * -s [-g -i + * + * 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. + * + * Caveat: 'line' is mutated during parsing. + * + * Returns zero on success, or errno. + */ +struct debugnet_ddb_config { + struct ifnet *dd_ifp; /* 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; Index: sys/net/debugnet.c =================================================================== --- sys/net/debugnet.c +++ sys/net/debugnet.c @@ -31,6 +31,7 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_ddb.h" #include "opt_inet.h" #include @@ -40,6 +41,11 @@ #include #include +#ifdef DDB +#include +#include +#endif + #include #include #include @@ -656,3 +662,222 @@ 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(); + 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: %ld\n", __func__, + opt->printname, octet, 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(); + 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; + + /* Assume v4, dotted decimal encoding for now. */ + db_cmd_radix = 10; + + 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(); + if (t == tWSPACE) + t = db_read_token(); + + while (t != tEOL) { + if (t != tMINUS) { + db_printf("%s: Bad syntax; expected '-', got %d\n", + cmd, t); + goto usage; + } + + t = db_read_token(); + 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(); + 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(); + if (t != tIDENT) { + db_printf("%s: Expected interface but got %d\n", + cmd, t); + goto usage; + } + + CURVNET_SET(vnet0); + ifp = ifunit_ref(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(); + 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(); + } + + /* 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 ((if_getflags(ifp) & IFF_UP) == 0) { + db_printf("%s: interface '%s' link is down\n", cmd, + if_name(ifp)); + error = ENXIO; + goto cleanup; + } + if (!DEBUGNET_SUPPORTED_NIC(ifp)) { + db_printf("%s: interface '%s' does not support debugnet\n", + cmd, if_name(ifp)); + error = ENODEV; + 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 [-g ] -c " + "-i \n", cmd); + error = EINVAL; + /* FALLTHROUGH */ +cleanup: + if (ifp != NULL) + if_rele(ifp); + db_skip_to_eol(); + return (error); +} +#endif /* DDB */ Index: sys/netinet/netdump/netdump_client.c =================================================================== --- sys/netinet/netdump/netdump_client.c +++ sys/netinet/netdump/netdump_client.c @@ -34,6 +34,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_ddb.h" + #include #include #include @@ -52,6 +54,11 @@ #include #include +#ifdef DDB +#include +#include +#endif + #include #include #include @@ -730,3 +737,73 @@ MODULE_VERSION(netdump, 1); DECLARE_MODULE(netdump, netdump_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); + +#ifdef DDB +/* + * Usage: netdump -s [-g -i + * + * 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 | CS_LEX_SPACE, 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", ¶ms); + 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, curthread); + if_rele(params.dd_ifp); + if (error != 0) { + db_printf("Error enabling netdump: %d\n", error); + return; + } + + /* Fako 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_static(&u.di); + + error = doadump(false); + + dumper_ddb_remove_static(&u.di); + if (error != 0) + db_printf("Cannot dump: %d", error); +} +#endif /* DDB */