netdump: Ref the interface we're attached to
Serialize netdump configuration / deconfiguration, and discard our configuration when the affiliated interface goes away by monitoring ifnet_departure_event. Reviewed by: markj, with input from vangyzen@ (earlier version) Sponsored by: Dell EMC Isilon Differential Revision: https://reviews.freebsd.org/D20206
This commit is contained in:
parent
12cd7ded68
commit
64e7d18f34
@ -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)
|
||||
{
|
||||
|
||||
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;
|
||||
|
||||
if (ifp != nd_ifp)
|
||||
return;
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user