summaryrefslogtreecommitdiff
path: root/sys/netinet/netdump
diff options
context:
space:
mode:
authorConrad Meyer <cem@FreeBSD.org>2019-05-10 23:12:59 +0000
committerConrad Meyer <cem@FreeBSD.org>2019-05-10 23:12:59 +0000
commit64e7d18f3438c74e228ba11b6d629696866a0091 (patch)
treeb1884c6bc5e0fff0ef2fdf2a919a0c42775a911b /sys/netinet/netdump
parent12cd7ded684e19d7b482c1bb4a7a76991cbbb1a6 (diff)
downloadsrc-test2-64e7d18f3438c74e228ba11b6d629696866a0091.tar.gz
src-test2-64e7d18f3438c74e228ba11b6d629696866a0091.zip
Notes
Diffstat (limited to 'sys/netinet/netdump')
-rw-r--r--sys/netinet/netdump/netdump_client.c187
1 files changed, 129 insertions, 58 deletions
diff --git a/sys/netinet/netdump/netdump_client.c b/sys/netinet/netdump/netdump_client.c
index 53ed511757d2..5787a036240b 100644
--- a/sys/netinet/netdump/netdump_client.c
+++ b/sys/netinet/netdump/netdump_client.c
@@ -93,6 +93,8 @@ static int netdump_configure(struct diocskerneldump_arg *,
struct thread *);
static int netdump_dumper(void *priv __unused, void *virtual,
vm_offset_t physical __unused, off_t offset, size_t length);
+static bool netdump_enabled(void);
+static int netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS);
static int netdump_ether_output(struct mbuf *m, struct ifnet *ifp,
struct ether_addr dst, u_short etype);
static void netdump_handle_arp(struct mbuf **mb);
@@ -102,11 +104,13 @@ static int netdump_ioctl(struct cdev *dev __unused, u_long cmd,
static int netdump_modevent(module_t mod, int type, void *priv);
static void netdump_network_poll(void);
static void netdump_pkt_in(struct ifnet *ifp, struct mbuf *m);
+static void netdump_reinit_internal(struct ifnet *ifp);
static int netdump_send(uint32_t type, off_t offset, unsigned char *data,
uint32_t datalen);
static int netdump_send_arp(in_addr_t dst);
static int netdump_start(struct dumperinfo *di);
static int netdump_udp_output(struct mbuf *m);
+static void netdump_unconfigure(void);
/* Must be at least as big as the chunks dumpsys() gives us. */
static unsigned char nd_buf[MAXDUMPPGS * PAGE_SIZE];
@@ -131,8 +135,17 @@ static struct {
#define nd_gateway nd_conf.ndc_gateway.in4
/* General dynamic settings. */
+static struct sx nd_conf_lk;
+SX_SYSINIT(nd_conf, &nd_conf_lk, "netdump configuration lock");
+#define NETDUMP_WLOCK() sx_xlock(&nd_conf_lk)
+#define NETDUMP_WUNLOCK() sx_xunlock(&nd_conf_lk)
+#define NETDUMP_RLOCK() sx_slock(&nd_conf_lk)
+#define NETDUMP_RUNLOCK() sx_sunlock(&nd_conf_lk)
+#define NETDUMP_ASSERT_WLOCKED() sx_assert(&nd_conf_lk, SA_XLOCKED)
+#define NETDUMP_ASSERT_LOCKED() sx_assert(&nd_conf_lk, SA_LOCKED)
static struct ether_addr nd_gw_mac;
static struct ifnet *nd_ifp;
+static eventhandler_tag nd_detach_cookie;
static uint16_t nd_server_port = NETDUMP_PORT;
FEATURE(netdump, "Netdump client support");
@@ -144,10 +157,8 @@ static int nd_debug;
SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN,
&nd_debug, 0,
"Debug message verbosity");
-static int nd_enabled;
-SYSCTL_INT(_net_netdump, OID_AUTO, enabled, CTLFLAG_RD,
- &nd_enabled, 0,
- "netdump configuration status");
+SYSCTL_PROC(_net_netdump, OID_AUTO, enabled, CTLFLAG_RD | CTLTYPE_INT,
+ &nd_ifp, 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),
@@ -165,6 +176,29 @@ SYSCTL_INT(_net_netdump, OID_AUTO, arp_retries, CTLFLAG_RWTUN,
&nd_arp_retries, 0,
"Number of ARP attempts before giving up");
+static bool
+netdump_enabled(void)
+{
+
+ NETDUMP_ASSERT_LOCKED();
+ return (nd_ifp != NULL);
+}
+
+static int
+netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ int en, error;
+
+ NETDUMP_RLOCK();
+ en = netdump_enabled();
+ NETDUMP_RUNLOCK();
+
+ error = SYSCTL_OUT(req, &en, sizeof(en));
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ return (EPERM);
+}
+
/*
* Checks for netdump support on a network interface
*
@@ -248,7 +282,7 @@ netdump_udp_output(struct mbuf *m)
struct udpiphdr *ui;
struct ip *ip;
- MPASS(nd_ifp != NULL);
+ MPASS(netdump_enabled());
M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT);
if (m == NULL) {
@@ -306,7 +340,7 @@ netdump_send_arp(in_addr_t dst)
struct arphdr *ah;
int pktlen;
- MPASS(nd_ifp != NULL);
+ MPASS(netdump_enabled());
/* Fill-up a broadcast address. */
memset(&bcast, 0xFF, ETHER_ADDR_LEN);
@@ -409,7 +443,7 @@ netdump_send(uint32_t type, off_t offset, unsigned char *data, uint32_t datalen)
rcvd_acks = 0;
retries = 0;
- MPASS(nd_ifp != NULL);
+ MPASS(netdump_enabled());
retransmit:
/* Chunks can be too big to fit in packets. */
@@ -875,7 +909,7 @@ static void
netdump_network_poll(void)
{
- MPASS(nd_ifp != NULL);
+ MPASS(netdump_enabled());
nd_ifp->if_netdump_methods->nd_poll(nd_ifp, 1000);
}
@@ -945,7 +979,7 @@ netdump_start(struct dumperinfo *di)
error = 0;
/* Check if the dumping is allowed to continue. */
- if (nd_enabled == 0)
+ if (!netdump_enabled())
return (EINVAL);
if (panicstr == NULL) {
@@ -954,8 +988,6 @@ netdump_start(struct dumperinfo *di)
return (EINVAL);
}
- MPASS(nd_ifp != NULL);
-
if (nd_server.s_addr == INADDR_ANY) {
printf("netdump_start: can't netdump; no server IP given\n");
return (EINVAL);
@@ -1065,36 +1097,68 @@ static struct cdevsw netdump_cdevsw = {
static struct cdev *netdump_cdev;
+static void
+netdump_unconfigure(void)
+{
+ struct diocskerneldump_arg kda;
+
+ NETDUMP_ASSERT_WLOCKED();
+ KASSERT(netdump_enabled(), ("%s: nd_ifp NULL", __func__));
+
+ bzero(&kda, sizeof(kda));
+ kda.kda_index = KDA_REMOVE_DEV;
+ (void)dumper_remove(nd_conf.ndc_iface, &kda);
+
+ netdump_mbuf_drain();
+
+ if_rele(nd_ifp);
+ nd_ifp = NULL;
+
+ bzero(&nd_conf, sizeof(nd_conf));
+}
+
+static void
+netdump_ifdetach(void *arg __unused, struct ifnet *ifp)
+{
+
+ NETDUMP_WLOCK();
+ if (ifp == nd_ifp)
+ netdump_unconfigure();
+ NETDUMP_WUNLOCK();
+}
+
static int
netdump_configure(struct diocskerneldump_arg *conf, struct thread *td)
{
- struct epoch_tracker et;
struct ifnet *ifp;
+ NETDUMP_ASSERT_WLOCKED();
+
CURVNET_SET(TD_TO_VNET(td));
if (!IS_DEFAULT_VNET(curvnet)) {
CURVNET_RESTORE();
return (EINVAL);
}
- NET_EPOCH_ENTER(et);
- CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
- if (strcmp(ifp->if_xname, conf->kda_iface) == 0)
- break;
- }
- /* XXX ref */
- NET_EPOCH_EXIT(et);
+ ifp = ifunit_ref(conf->kda_iface);
CURVNET_RESTORE();
if (ifp == NULL)
return (ENOENT);
- if ((if_getflags(ifp) & IFF_UP) == 0)
+ if ((if_getflags(ifp) & IFF_UP) == 0) {
+ if_rele(ifp);
return (ENXIO);
- if (!netdump_supported_nic(ifp) || ifp->if_type != IFT_ETHER)
+ }
+ if (!netdump_supported_nic(ifp) || ifp->if_type != IFT_ETHER) {
+ if_rele(ifp);
return (ENODEV);
+ }
+ if (netdump_enabled())
+ if_rele(nd_ifp);
nd_ifp = ifp;
- netdump_reinit(ifp);
+ netdump_reinit_internal(ifp);
+
#define COPY_SIZED(elm) do { \
_Static_assert(sizeof(nd_conf.ndc_ ## elm) == \
sizeof(conf->kda_ ## elm), "elm " __XSTRING(elm) " mismatch"); \
@@ -1107,22 +1171,34 @@ netdump_configure(struct diocskerneldump_arg *conf, struct thread *td)
COPY_SIZED(gateway);
COPY_SIZED(af);
#undef COPY_SIZED
- nd_enabled = 1;
+
return (0);
}
/*
* Reinitialize the mbuf pool used by drivers while dumping. This is called
- * from the generic ioctl handler for SIOCSIFMTU after the driver has
- * reconfigured itself.
+ * from the generic ioctl handler for SIOCSIFMTU after any NIC driver has
+ * reconfigured itself. (I.e., it may not be a configured netdump interface.)
*/
void
netdump_reinit(struct ifnet *ifp)
{
- int clsize, nmbuf, ncl, nrxr;
- if (ifp != nd_ifp)
+ NETDUMP_WLOCK();
+ if (ifp != nd_ifp) {
+ NETDUMP_WUNLOCK();
return;
+ }
+ netdump_reinit_internal(ifp);
+ NETDUMP_WUNLOCK();
+}
+
+static void
+netdump_reinit_internal(struct ifnet *ifp)
+{
+ int clsize, nmbuf, ncl, nrxr;
+
+ NETDUMP_ASSERT_WLOCKED();
ifp->if_netdump_methods->nd_init(ifp, &nrxr, &ncl, &clsize);
KASSERT(nrxr > 0, ("invalid receive ring count %d", nrxr));
@@ -1168,6 +1244,8 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
conf = NULL;
error = 0;
+ NETDUMP_WLOCK();
+
switch (cmd) {
#ifdef COMPAT_FREEBSD11
case DIOCSKERNELDUMP_FREEBSD11:
@@ -1177,10 +1255,8 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
error = ENXIO;
break;
}
- if (nd_enabled) {
- nd_enabled = 0;
- netdump_mbuf_drain();
- }
+ if (netdump_enabled())
+ netdump_unconfigure();
break;
#endif
#ifdef COMPAT_FREEBSD12
@@ -1197,17 +1273,15 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
error = ENXIO;
break;
}
- if (nd_enabled) {
- nd_enabled = 0;
- netdump_mbuf_drain();
- }
+ if (netdump_enabled())
+ netdump_unconfigure();
break;
case NETDUMPGCONF_FREEBSD12:
gone_in(14, "FreeBSD 12.x ABI compat");
conf12 = (void *)addr;
- if (!nd_enabled) {
+ if (!netdump_enabled()) {
error = ENXIO;
break;
}
@@ -1232,7 +1306,7 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
* For now, index is ignored; netdump doesn't support multiple
* configurations (yet).
*/
- if (!nd_enabled) {
+ if (!netdump_enabled()) {
error = ENXIO;
conf = NULL;
break;
@@ -1293,13 +1367,10 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
if (conf->kda_index == KDA_REMOVE ||
conf->kda_index == KDA_REMOVE_DEV ||
conf->kda_index == KDA_REMOVE_ALL) {
- if (nd_enabled || conf->kda_index == KDA_REMOVE_ALL) {
- error = dumper_remove(conf->kda_iface, conf);
- if (error == 0) {
- nd_enabled = 0;
- netdump_mbuf_drain();
- }
- }
+ if (netdump_enabled())
+ netdump_unconfigure();
+ if (conf->kda_index == KDA_REMOVE_ALL)
+ error = dumper_remove(NULL, conf);
break;
}
@@ -1342,10 +1413,8 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
conf->kda_encryptedkeysize);
free(encryptedkey, M_TEMP);
}
- if (error != 0) {
- nd_enabled = 0;
- netdump_mbuf_drain();
- }
+ if (error != 0)
+ netdump_unconfigure();
break;
default:
error = ENOTTY;
@@ -1354,6 +1423,7 @@ netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
explicit_bzero(&kda_copy, sizeof(kda_copy));
if (conf != NULL)
explicit_bzero(conf, sizeof(*conf));
+ NETDUMP_WUNLOCK();
return (error);
}
@@ -1385,6 +1455,9 @@ netdump_modevent(module_t mod __unused, int what, void *priv __unused)
if (error != 0)
return (error);
+ nd_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event,
+ netdump_ifdetach, NULL, EVENTHANDLER_PRI_ANY);
+
if ((arg = kern_getenv("net.dump.iface")) != NULL) {
strlcpy(conf.kda_iface, arg, sizeof(conf.kda_iface));
freeenv(arg);
@@ -1404,23 +1477,21 @@ netdump_modevent(module_t mod __unused, int what, void *priv __unused)
conf.kda_af = AF_INET;
/* Ignore errors; we print a message to the console. */
+ NETDUMP_WLOCK();
(void)netdump_configure(&conf, curthread);
+ NETDUMP_WUNLOCK();
}
break;
case MOD_UNLOAD:
- if (nd_enabled) {
- struct diocskerneldump_arg kda;
-
+ NETDUMP_WLOCK();
+ if (netdump_enabled()) {
printf("netdump: disabling dump device for unload\n");
-
- bzero(&kda, sizeof(kda));
- kda.kda_index = KDA_REMOVE_DEV;
- (void)dumper_remove(nd_conf.ndc_iface, &kda);
-
- netdump_mbuf_drain();
- nd_enabled = 0;
+ netdump_unconfigure();
}
+ NETDUMP_WUNLOCK();
destroy_dev(netdump_cdev);
+ EVENTHANDLER_DEREGISTER(ifnet_departure_event,
+ nd_detach_cookie);
break;
default:
error = EOPNOTSUPP;