Index: share/man/man4/ddb.4 =================================================================== --- share/man/man4/ddb.4 +++ share/man/man4/ddb.4 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 30, 2019 +.Dd September 1, 2019 .Dt DDB 4 .Os .Sh NAME @@ -1161,7 +1161,7 @@ using the remote debug feature, with a connection to the serial console port on the target machine. .Pp -.It Ic netdump Fl s Ar server Oo Fl g Ar gateway Oc Fl c Ar client Fl i Ar iface +.It Ic netdump Fl s Ar server Oo Fl g Ar gateway Fl c Ar client Fl i Ar iface Oc Configure .Xr netdump 4 with the provided parameters, and immediately perform a netdump. Index: sys/net/debugnet.h =================================================================== --- sys/net/debugnet.h +++ sys/net/debugnet.h @@ -180,11 +180,12 @@ /* * DDB parsing helper for common debugnet options. * - * -s [-g -i + * -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. + * IP addresses. Only the server parameter is required. The others are + * inferred automatically from the routing table, if not explicitly provided. * * Provides basic '-h' using provided 'cmd' string. * @@ -195,6 +196,7 @@ in_addr_t dd_client; in_addr_t dd_server; in_addr_t dd_gateway; + bool dd_has_client : 1; bool dd_has_gateway : 1; }; int debugnet_parse_ddb_cmd(const char *cmd, Index: sys/net/debugnet.c =================================================================== --- sys/net/debugnet.c +++ sys/net/debugnet.c @@ -493,8 +493,12 @@ MPASS(pcb == &g_dnet_pcb); ifp = pcb->dp_ifp; - ifp->if_input = pcb->dp_drv_input; - ifp->if_debugnet_methods->dn_event(ifp, DEBUGNET_END); + if (ifp != NULL) { + if (pcb->dp_drv_input != NULL) + ifp->if_input = pcb->dp_drv_input; + if (pcb->dp_event_started) + ifp->if_debugnet_methods->dn_event(ifp, DEBUGNET_END); + } debugnet_mbuf_finish(); g_debugnet_pcb_inuse = false; @@ -529,8 +533,87 @@ /* Switch to the debugnet mbuf zones. */ debugnet_mbuf_start(); + /* At least one needed parameter is missing; infer it. */ + if (pcb->dp_client == INADDR_ANY || pcb->dp_gateway == INADDR_ANY || + pcb->dp_ifp == NULL) { + struct sockaddr_in dest_sin, *gw_sin, *local_sin; + struct rtentry *dest_rt; + struct ifnet *rt_ifp; + + memset(&dest_sin, 0, sizeof(dest_sin)); + dest_sin = (struct sockaddr_in) { + .sin_len = sizeof(dest_sin), + .sin_family = AF_INET, + .sin_addr.s_addr = pcb->dp_server, + }; + + CURVNET_SET(vnet0); + dest_rt = rtalloc1((struct sockaddr *)&dest_sin, 0, + RTF_RNH_LOCKED); + CURVNET_RESTORE(); + + if (dest_rt == NULL) { + db_printf("%s: Could not get route for that server.\n", + __func__); + error = ENOENT; + goto cleanup; + } + + if (dest_rt->rt_gateway->sa_family == AF_INET) + gw_sin = (struct sockaddr_in *)dest_rt->rt_gateway; + else { + if (dest_rt->rt_gateway->sa_family == AF_LINK) + DNETDEBUG("Destination address is on link.\n"); + gw_sin = NULL; + } + + MPASS(dest_rt->rt_ifa->ifa_addr->sa_family == AF_INET); + local_sin = (struct sockaddr_in *)dest_rt->rt_ifa->ifa_addr; + + rt_ifp = dest_rt->rt_ifp; + + if (pcb->dp_client == INADDR_ANY) + pcb->dp_client = local_sin->sin_addr.s_addr; + if (pcb->dp_gateway == INADDR_ANY && gw_sin != NULL) + pcb->dp_gateway = gw_sin->sin_addr.s_addr; + if (pcb->dp_ifp == NULL) + pcb->dp_ifp = rt_ifp; + + RTFREE_LOCKED(dest_rt); + } + ifp = pcb->dp_ifp; + + if (debugnet_debug > 0) { + char serbuf[INET_ADDRSTRLEN], clibuf[INET_ADDRSTRLEN], + gwbuf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &pcb->dp_server, serbuf, sizeof(serbuf)); + inet_ntop(AF_INET, &pcb->dp_client, clibuf, sizeof(clibuf)); + if (pcb->dp_gateway != INADDR_ANY) + inet_ntop(AF_INET, &pcb->dp_gateway, gwbuf, sizeof(gwbuf)); + DNETDEBUG("Connecting to %s:%d%s%s from %s:%d on %s\n", + serbuf, pcb->dp_server_port, + (pcb->dp_gateway == INADDR_ANY) ? "" : " via ", + (pcb->dp_gateway == INADDR_ANY) ? "" : gwbuf, + clibuf, pcb->dp_client_ack_port, if_name(ifp)); + } + + /* Validate iface is online and supported. */ + if (!DEBUGNET_SUPPORTED_NIC(ifp)) { + printf("%s: interface '%s' does not support debugnet\n", + __func__, if_name(ifp)); + error = ENODEV; + goto cleanup; + } + if ((if_getflags(ifp) & IFF_UP) == 0) { + printf("%s: interface '%s' link is down\n", __func__, + if_name(ifp)); + error = ENXIO; + goto cleanup; + } + ifp->if_debugnet_methods->dn_event(ifp, DEBUGNET_START); + pcb->dp_event_started = true; /* * We maintain the invariant that g_debugnet_pcb_inuse is always true @@ -727,9 +810,6 @@ 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, @@ -744,6 +824,8 @@ }, *cur_inet_opt; + /* Assume v4, dotted decimal encoding for now. */ + db_cmd_radix = 10; ifp = NULL; memset(result, 0, sizeof(*result)); @@ -847,37 +929,21 @@ 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); + if (!opt_server.has_opt) { + db_printf("%s: need a destination server address\n", cmd); goto usage; } + result->dd_has_client = opt_client.has_opt; 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 [-g ] -c " - "-i \n", cmd); + db_printf("Usage: %s -s [-g -c " + "-i ]\n", cmd); error = EINVAL; /* FALLTHROUGH */ cleanup: Index: sys/net/debugnet_int.h =================================================================== --- sys/net/debugnet_int.h +++ sys/net/debugnet_int.h @@ -69,6 +69,7 @@ enum dnet_pcb_st dp_state; uint16_t dp_client_ack_port; + bool dp_event_started; }; /* TODO(CEM): Obviate this assertion by using a BITSET(9) for acks. */ Index: sys/netinet/netdump/netdump_client.c =================================================================== --- sys/netinet/netdump/netdump_client.c +++ sys/netinet/netdump/netdump_client.c @@ -138,8 +138,8 @@ SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN, &nd_debug, 0, "Debug message verbosity"); -SYSCTL_PROC(_net_netdump, OID_AUTO, enabled, CTLFLAG_RD | CTLTYPE_INT, - &nd_ifp, 0, netdump_enabled_sysctl, "I", "netdump configuration status"); +SYSCTL_PROC(_net_netdump, OID_AUTO, enabled, CTLFLAG_RD | CTLTYPE_INT, NULL, 0, + netdump_enabled_sysctl, "I", "netdump configuration status"); static char nd_path[MAXPATHLEN]; SYSCTL_STRING(_net_netdump, OID_AUTO, path, CTLFLAG_RW, nd_path, sizeof(nd_path), @@ -158,12 +158,21 @@ &debugnet_arp_nretries, 0, "Number of ARP attempts before giving up"); +static bool nd_is_enabled; static bool netdump_enabled(void) { NETDUMP_ASSERT_LOCKED(); - return (nd_ifp != NULL); + return (nd_is_enabled); +} + +static void +netdump_set_enabled(bool status) +{ + + NETDUMP_ASSERT_LOCKED(); + nd_is_enabled = status; } static int @@ -297,10 +306,6 @@ printf("netdump_start: can't netdump; no server IP given\n"); return (EINVAL); } - if (nd_client.s_addr == INADDR_ANY) { - printf("netdump_start: can't netdump; no client IP given\n"); - return (EINVAL); - } /* We start dumping at offset 0. */ di->dumpoff = 0; @@ -370,14 +375,16 @@ struct diocskerneldump_arg kda; NETDUMP_ASSERT_WLOCKED(); - KASSERT(netdump_enabled(), ("%s: nd_ifp NULL", __func__)); + KASSERT(netdump_enabled(), ("%s: not enabled", __func__)); bzero(&kda, sizeof(kda)); kda.kda_index = KDA_REMOVE_DEV; (void)dumper_remove(nd_conf.ndc_iface, &kda); - if_rele(nd_ifp); + if (nd_ifp != NULL) + if_rele(nd_ifp); nd_ifp = NULL; + netdump_set_enabled(false); log(LOG_WARNING, "netdump: Lost configured interface %s\n", nd_conf.ndc_iface); @@ -402,28 +409,21 @@ NETDUMP_ASSERT_WLOCKED(); - CURVNET_SET(TD_TO_VNET(td)); - if (!IS_DEFAULT_VNET(curvnet)) { + if (conf->kda_iface[0] != 0) { + CURVNET_SET(TD_TO_VNET(td)); + if (!IS_DEFAULT_VNET(curvnet)) { + CURVNET_RESTORE(); + return (EINVAL); + } + ifp = ifunit_ref(conf->kda_iface); CURVNET_RESTORE(); - return (EINVAL); - } - ifp = ifunit_ref(conf->kda_iface); - CURVNET_RESTORE(); - - if (ifp == NULL) - return (ENOENT); - if ((if_getflags(ifp) & IFF_UP) == 0) { - if_rele(ifp); - return (ENXIO); - } - if (!DEBUGNET_SUPPORTED_NIC(ifp)) { - if_rele(ifp); - return (ENODEV); - } + } else + ifp = NULL; - if (netdump_enabled()) + if (nd_ifp != NULL) if_rele(nd_ifp); nd_ifp = ifp; + netdump_set_enabled(true); #define COPY_SIZED(elm) do { \ _Static_assert(sizeof(nd_conf.ndc_ ## elm) == \ @@ -519,8 +519,9 @@ break; } - strlcpy(conf12->ndc12_iface, nd_ifp->if_xname, - sizeof(conf12->ndc12_iface)); + if (nd_ifp != NULL) + strlcpy(conf12->ndc12_iface, nd_ifp->if_xname, + sizeof(conf12->ndc12_iface)); memcpy(&conf12->ndc12_server, &nd_server, sizeof(conf12->ndc12_server)); memcpy(&conf12->ndc12_client, &nd_client, @@ -541,8 +542,9 @@ break; } - strlcpy(conf->kda_iface, nd_ifp->if_xname, - sizeof(conf->kda_iface)); + if (nd_ifp != NULL) + strlcpy(conf->kda_iface, nd_ifp->if_xname, + sizeof(conf->kda_iface)); memcpy(&conf->kda_server, &nd_server, sizeof(nd_server)); memcpy(&conf->kda_client, &nd_client, sizeof(nd_client)); memcpy(&conf->kda_gateway, &nd_gateway, sizeof(nd_gateway)); @@ -768,11 +770,17 @@ /* Translate to a netdump dumper config. */ memset(&conf, 0, sizeof(conf)); - strlcpy(conf.kda_iface, if_name(params.dd_ifp), sizeof(conf.kda_iface)); + + if (params.dd_ifp != NULL) + 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_client) + conf.kda_client.in4 = (struct in_addr) { params.dd_client }; + else + conf.kda_client.in4 = (struct in_addr) { INADDR_ANY }; if (params.dd_has_gateway) conf.kda_gateway.in4 = (struct in_addr) { params.dd_gateway }; else