1e5054602SMark Johnston /*- 2e5054602SMark Johnston * Copyright (c) 2005-2014 Sandvine Incorporated. All rights reserved. 3e5054602SMark Johnston * Copyright (c) 2000 Darrell Anderson 4e5054602SMark Johnston * All rights reserved. 5e5054602SMark Johnston * 6e5054602SMark Johnston * Redistribution and use in source and binary forms, with or without 7e5054602SMark Johnston * modification, are permitted provided that the following conditions 8e5054602SMark Johnston * are met: 9e5054602SMark Johnston * 1. Redistributions of source code must retain the above copyright 10e5054602SMark Johnston * notice, this list of conditions and the following disclaimer. 11e5054602SMark Johnston * 2. Redistributions in binary form must reproduce the above copyright 12e5054602SMark Johnston * notice, this list of conditions and the following disclaimer in the 13e5054602SMark Johnston * documentation and/or other materials provided with the distribution. 14e5054602SMark Johnston * 15e5054602SMark Johnston * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16e5054602SMark Johnston * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17e5054602SMark Johnston * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18e5054602SMark Johnston * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19e5054602SMark Johnston * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20e5054602SMark Johnston * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21e5054602SMark Johnston * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22e5054602SMark Johnston * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23e5054602SMark Johnston * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24e5054602SMark Johnston * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25e5054602SMark Johnston * SUCH DAMAGE. 26e5054602SMark Johnston */ 27e5054602SMark Johnston 28e5054602SMark Johnston /* 29e5054602SMark Johnston * netdump_client.c 30e5054602SMark Johnston * FreeBSD subsystem supporting netdump network dumps. 31e5054602SMark Johnston * A dedicated server must be running to accept client dumps. 32e5054602SMark Johnston */ 33e5054602SMark Johnston 34e5054602SMark Johnston #include <sys/cdefs.h> 35e5054602SMark Johnston __FBSDID("$FreeBSD$"); 36e5054602SMark Johnston 37e5054602SMark Johnston #include <sys/param.h> 38e5054602SMark Johnston #include <sys/conf.h> 39e5054602SMark Johnston #include <sys/disk.h> 40e5054602SMark Johnston #include <sys/endian.h> 4104e0c883SConrad Meyer #include <sys/eventhandler.h> 42b35822d9SMark Johnston #include <sys/jail.h> 43e5054602SMark Johnston #include <sys/kernel.h> 44e5054602SMark Johnston #include <sys/kerneldump.h> 45e5054602SMark Johnston #include <sys/mbuf.h> 46e5054602SMark Johnston #include <sys/module.h> 47e5054602SMark Johnston #include <sys/priv.h> 48e5054602SMark Johnston #include <sys/proc.h> 49e5054602SMark Johnston #include <sys/protosw.h> 50e5054602SMark Johnston #include <sys/socket.h> 51e5054602SMark Johnston #include <sys/sysctl.h> 52e5054602SMark Johnston #include <sys/systm.h> 53e5054602SMark Johnston 54e5054602SMark Johnston #include <net/ethernet.h> 55e5054602SMark Johnston #include <net/if.h> 56e5054602SMark Johnston #include <net/if_arp.h> 57e5054602SMark Johnston #include <net/if_dl.h> 58e5054602SMark Johnston #include <net/if_types.h> 59e5054602SMark Johnston #include <net/if_var.h> 60e5054602SMark Johnston 61e5054602SMark Johnston #include <netinet/in.h> 62e5054602SMark Johnston #include <netinet/in_systm.h> 63e5054602SMark Johnston #include <netinet/in_var.h> 64e5054602SMark Johnston #include <netinet/ip.h> 65e5054602SMark Johnston #include <netinet/ip_var.h> 66e5054602SMark Johnston #include <netinet/ip_options.h> 67e5054602SMark Johnston #include <netinet/udp.h> 68e5054602SMark Johnston #include <netinet/udp_var.h> 69e5054602SMark Johnston #include <netinet/netdump/netdump.h> 70e5054602SMark Johnston 71e5054602SMark Johnston #include <machine/in_cksum.h> 72e5054602SMark Johnston #include <machine/pcb.h> 73e5054602SMark Johnston 74e5054602SMark Johnston #define NETDDEBUG(f, ...) do { \ 75e5054602SMark Johnston if (nd_debug > 0) \ 76e5054602SMark Johnston printf(("%s: " f), __func__, ## __VA_ARGS__); \ 77e5054602SMark Johnston } while (0) 78e5054602SMark Johnston #define NETDDEBUG_IF(i, f, ...) do { \ 79e5054602SMark Johnston if (nd_debug > 0) \ 80e5054602SMark Johnston if_printf((i), ("%s: " f), __func__, ## __VA_ARGS__); \ 81e5054602SMark Johnston } while (0) 82e5054602SMark Johnston #define NETDDEBUGV(f, ...) do { \ 83e5054602SMark Johnston if (nd_debug > 1) \ 84e5054602SMark Johnston printf(("%s: " f), __func__, ## __VA_ARGS__); \ 85e5054602SMark Johnston } while (0) 86e5054602SMark Johnston #define NETDDEBUGV_IF(i, f, ...) do { \ 87e5054602SMark Johnston if (nd_debug > 1) \ 88e5054602SMark Johnston if_printf((i), ("%s: " f), __func__, ## __VA_ARGS__); \ 89e5054602SMark Johnston } while (0) 90e5054602SMark Johnston 91e5054602SMark Johnston static int netdump_arp_gw(void); 92e5054602SMark Johnston static void netdump_cleanup(void); 936b6e2954SConrad Meyer static int netdump_configure(struct diocskerneldump_arg *, 946b6e2954SConrad Meyer struct thread *); 95e5054602SMark Johnston static int netdump_dumper(void *priv __unused, void *virtual, 96e5054602SMark Johnston vm_offset_t physical __unused, off_t offset, size_t length); 9764e7d18fSConrad Meyer static bool netdump_enabled(void); 9864e7d18fSConrad Meyer static int netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS); 99e5054602SMark Johnston static int netdump_ether_output(struct mbuf *m, struct ifnet *ifp, 100e5054602SMark Johnston struct ether_addr dst, u_short etype); 101e5054602SMark Johnston static void netdump_handle_arp(struct mbuf **mb); 102e5054602SMark Johnston static void netdump_handle_ip(struct mbuf **mb); 103e5054602SMark Johnston static int netdump_ioctl(struct cdev *dev __unused, u_long cmd, 104e5054602SMark Johnston caddr_t addr, int flags __unused, struct thread *td); 105e5054602SMark Johnston static int netdump_modevent(module_t mod, int type, void *priv); 106e5054602SMark Johnston static void netdump_network_poll(void); 107e5054602SMark Johnston static void netdump_pkt_in(struct ifnet *ifp, struct mbuf *m); 10864e7d18fSConrad Meyer static void netdump_reinit_internal(struct ifnet *ifp); 109e5054602SMark Johnston static int netdump_send(uint32_t type, off_t offset, unsigned char *data, 110e5054602SMark Johnston uint32_t datalen); 111e5054602SMark Johnston static int netdump_send_arp(in_addr_t dst); 112e5054602SMark Johnston static int netdump_start(struct dumperinfo *di); 113e5054602SMark Johnston static int netdump_udp_output(struct mbuf *m); 11464e7d18fSConrad Meyer static void netdump_unconfigure(void); 115e5054602SMark Johnston 116e5054602SMark Johnston /* Must be at least as big as the chunks dumpsys() gives us. */ 117e5054602SMark Johnston static unsigned char nd_buf[MAXDUMPPGS * PAGE_SIZE]; 118e5054602SMark Johnston static uint32_t nd_seqno; 119e5054602SMark Johnston static int dump_failed, have_gw_mac; 120e5054602SMark Johnston static void (*drv_if_input)(struct ifnet *, struct mbuf *); 121e5054602SMark Johnston static int restore_gw_addr; 122e5054602SMark Johnston 123e5054602SMark Johnston static uint64_t rcvd_acks; 124e5054602SMark Johnston CTASSERT(sizeof(rcvd_acks) * NBBY == NETDUMP_MAX_IN_FLIGHT); 125e5054602SMark Johnston 126e5054602SMark Johnston /* Configuration parameters. */ 1276144b50fSConrad Meyer static struct { 1286144b50fSConrad Meyer char ndc_iface[IFNAMSIZ]; 1296144b50fSConrad Meyer union kd_ip ndc_server; 1306144b50fSConrad Meyer union kd_ip ndc_client; 1316144b50fSConrad Meyer union kd_ip ndc_gateway; 1326144b50fSConrad Meyer uint8_t ndc_af; 133*8726929dSMark Johnston /* Runtime State */ 134*8726929dSMark Johnston off_t nd_tx_off; 135*8726929dSMark Johnston size_t nd_buf_len; 1366144b50fSConrad Meyer } nd_conf; 1376144b50fSConrad Meyer #define nd_server nd_conf.ndc_server.in4 1386144b50fSConrad Meyer #define nd_client nd_conf.ndc_client.in4 1396144b50fSConrad Meyer #define nd_gateway nd_conf.ndc_gateway.in4 140e5054602SMark Johnston 141e5054602SMark Johnston /* General dynamic settings. */ 14264e7d18fSConrad Meyer static struct sx nd_conf_lk; 14364e7d18fSConrad Meyer SX_SYSINIT(nd_conf, &nd_conf_lk, "netdump configuration lock"); 14464e7d18fSConrad Meyer #define NETDUMP_WLOCK() sx_xlock(&nd_conf_lk) 14564e7d18fSConrad Meyer #define NETDUMP_WUNLOCK() sx_xunlock(&nd_conf_lk) 14664e7d18fSConrad Meyer #define NETDUMP_RLOCK() sx_slock(&nd_conf_lk) 14764e7d18fSConrad Meyer #define NETDUMP_RUNLOCK() sx_sunlock(&nd_conf_lk) 14864e7d18fSConrad Meyer #define NETDUMP_ASSERT_WLOCKED() sx_assert(&nd_conf_lk, SA_XLOCKED) 14964e7d18fSConrad Meyer #define NETDUMP_ASSERT_LOCKED() sx_assert(&nd_conf_lk, SA_LOCKED) 150e5054602SMark Johnston static struct ether_addr nd_gw_mac; 151e5054602SMark Johnston static struct ifnet *nd_ifp; 15264e7d18fSConrad Meyer static eventhandler_tag nd_detach_cookie; 153e5054602SMark Johnston static uint16_t nd_server_port = NETDUMP_PORT; 154e5054602SMark Johnston 155e5054602SMark Johnston FEATURE(netdump, "Netdump client support"); 156e5054602SMark Johnston 157e5054602SMark Johnston static SYSCTL_NODE(_net, OID_AUTO, netdump, CTLFLAG_RD, NULL, 158e5054602SMark Johnston "netdump parameters"); 159e5054602SMark Johnston 160e5054602SMark Johnston static int nd_debug; 161e5054602SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN, 162e5054602SMark Johnston &nd_debug, 0, 163e5054602SMark Johnston "Debug message verbosity"); 16464e7d18fSConrad Meyer SYSCTL_PROC(_net_netdump, OID_AUTO, enabled, CTLFLAG_RD | CTLTYPE_INT, 16564e7d18fSConrad Meyer &nd_ifp, 0, netdump_enabled_sysctl, "I", "netdump configuration status"); 166e5054602SMark Johnston static char nd_path[MAXPATHLEN]; 167e5054602SMark Johnston SYSCTL_STRING(_net_netdump, OID_AUTO, path, CTLFLAG_RW, 168e5054602SMark Johnston nd_path, sizeof(nd_path), 169e5054602SMark Johnston "Server path for output files"); 170da7d7778SMark Johnston static int nd_polls = 2000; 171da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, polls, CTLFLAG_RWTUN, 172da7d7778SMark Johnston &nd_polls, 0, 173da7d7778SMark Johnston "Number of times to poll before assuming packet loss (0.5ms per poll)"); 174da7d7778SMark Johnston static int nd_retries = 10; 175da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, retries, CTLFLAG_RWTUN, 176da7d7778SMark Johnston &nd_retries, 0, 177da7d7778SMark Johnston "Number of retransmit attempts before giving up"); 178da7d7778SMark Johnston static int nd_arp_retries = 3; 179da7d7778SMark Johnston SYSCTL_INT(_net_netdump, OID_AUTO, arp_retries, CTLFLAG_RWTUN, 180da7d7778SMark Johnston &nd_arp_retries, 0, 181da7d7778SMark Johnston "Number of ARP attempts before giving up"); 182e5054602SMark Johnston 18364e7d18fSConrad Meyer static bool 18464e7d18fSConrad Meyer netdump_enabled(void) 18564e7d18fSConrad Meyer { 18664e7d18fSConrad Meyer 18764e7d18fSConrad Meyer NETDUMP_ASSERT_LOCKED(); 18864e7d18fSConrad Meyer return (nd_ifp != NULL); 18964e7d18fSConrad Meyer } 19064e7d18fSConrad Meyer 19164e7d18fSConrad Meyer static int 19264e7d18fSConrad Meyer netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS) 19364e7d18fSConrad Meyer { 19464e7d18fSConrad Meyer int en, error; 19564e7d18fSConrad Meyer 19664e7d18fSConrad Meyer NETDUMP_RLOCK(); 19764e7d18fSConrad Meyer en = netdump_enabled(); 19864e7d18fSConrad Meyer NETDUMP_RUNLOCK(); 19964e7d18fSConrad Meyer 20064e7d18fSConrad Meyer error = SYSCTL_OUT(req, &en, sizeof(en)); 20164e7d18fSConrad Meyer if (error != 0 || req->newptr == NULL) 20264e7d18fSConrad Meyer return (error); 20364e7d18fSConrad Meyer return (EPERM); 20464e7d18fSConrad Meyer } 20564e7d18fSConrad Meyer 206e5054602SMark Johnston /* 207e5054602SMark Johnston * Checks for netdump support on a network interface 208e5054602SMark Johnston * 209e5054602SMark Johnston * Parameters: 210e5054602SMark Johnston * ifp The network interface that is being tested for support 211e5054602SMark Johnston * 212e5054602SMark Johnston * Returns: 213e5054602SMark Johnston * int 1 if the interface is supported, 0 if not 214e5054602SMark Johnston */ 215e5054602SMark Johnston static bool 216e5054602SMark Johnston netdump_supported_nic(struct ifnet *ifp) 217e5054602SMark Johnston { 218e5054602SMark Johnston 219e5054602SMark Johnston return (ifp->if_netdump_methods != NULL); 220e5054602SMark Johnston } 221e5054602SMark Johnston 222e5054602SMark Johnston /*- 223e5054602SMark Johnston * Network specific primitives. 224e5054602SMark Johnston * Following down the code they are divided ordered as: 225e5054602SMark Johnston * - Packet buffer primitives 226e5054602SMark Johnston * - Output primitives 227e5054602SMark Johnston * - Input primitives 228e5054602SMark Johnston * - Polling primitives 229e5054602SMark Johnston */ 230e5054602SMark Johnston 231e5054602SMark Johnston /* 232e5054602SMark Johnston * Handles creation of the ethernet header, then places outgoing packets into 233e5054602SMark Johnston * the tx buffer for the NIC 234e5054602SMark Johnston * 235e5054602SMark Johnston * Parameters: 236e5054602SMark Johnston * m The mbuf containing the packet to be sent (will be freed by 237e5054602SMark Johnston * this function or the NIC driver) 238e5054602SMark Johnston * ifp The interface to send on 239e5054602SMark Johnston * dst The destination ethernet address (source address will be looked 240e5054602SMark Johnston * up using ifp) 241e5054602SMark Johnston * etype The ETHERTYPE_* value for the protocol that is being sent 242e5054602SMark Johnston * 243e5054602SMark Johnston * Returns: 244e5054602SMark Johnston * int see errno.h, 0 for success 245e5054602SMark Johnston */ 246e5054602SMark Johnston static int 247e5054602SMark Johnston netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct ether_addr dst, 248e5054602SMark Johnston u_short etype) 249e5054602SMark Johnston { 250e5054602SMark Johnston struct ether_header *eh; 251e5054602SMark Johnston 252e5054602SMark Johnston if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) != IFF_UP) || 253e5054602SMark Johnston (ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) { 254e5054602SMark Johnston if_printf(ifp, "netdump_ether_output: interface isn't up\n"); 255e5054602SMark Johnston m_freem(m); 256e5054602SMark Johnston return (ENETDOWN); 257e5054602SMark Johnston } 258e5054602SMark Johnston 259e5054602SMark Johnston /* Fill in the ethernet header. */ 260e5054602SMark Johnston M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); 261e5054602SMark Johnston if (m == NULL) { 262e5054602SMark Johnston printf("%s: out of mbufs\n", __func__); 263e5054602SMark Johnston return (ENOBUFS); 264e5054602SMark Johnston } 265e5054602SMark Johnston eh = mtod(m, struct ether_header *); 266e5054602SMark Johnston memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); 267e5054602SMark Johnston memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN); 268e5054602SMark Johnston eh->ether_type = htons(etype); 269e5054602SMark Johnston return ((ifp->if_netdump_methods->nd_transmit)(ifp, m)); 270e5054602SMark Johnston } 271e5054602SMark Johnston 272e5054602SMark Johnston /* 273e5054602SMark Johnston * Unreliable transmission of an mbuf chain to the netdump server 274e5054602SMark Johnston * Note: can't handle fragmentation; fails if the packet is larger than 275e5054602SMark Johnston * nd_ifp->if_mtu after adding the UDP/IP headers 276e5054602SMark Johnston * 277e5054602SMark Johnston * Parameters: 278e5054602SMark Johnston * m mbuf chain 279e5054602SMark Johnston * 280e5054602SMark Johnston * Returns: 281e5054602SMark Johnston * int see errno.h, 0 for success 282e5054602SMark Johnston */ 283e5054602SMark Johnston static int 284e5054602SMark Johnston netdump_udp_output(struct mbuf *m) 285e5054602SMark Johnston { 286e5054602SMark Johnston struct udpiphdr *ui; 287e5054602SMark Johnston struct ip *ip; 288e5054602SMark Johnston 28964e7d18fSConrad Meyer MPASS(netdump_enabled()); 290e5054602SMark Johnston 291e5054602SMark Johnston M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT); 292e5054602SMark Johnston if (m == NULL) { 293e5054602SMark Johnston printf("%s: out of mbufs\n", __func__); 294e5054602SMark Johnston return (ENOBUFS); 295e5054602SMark Johnston } 296e5054602SMark Johnston 297e5054602SMark Johnston if (m->m_pkthdr.len > nd_ifp->if_mtu) { 298e5054602SMark Johnston printf("netdump_udp_output: Packet is too big: %d > MTU %u\n", 299e5054602SMark Johnston m->m_pkthdr.len, nd_ifp->if_mtu); 300e5054602SMark Johnston m_freem(m); 301e5054602SMark Johnston return (ENOBUFS); 302e5054602SMark Johnston } 303e5054602SMark Johnston 304e5054602SMark Johnston ui = mtod(m, struct udpiphdr *); 305e5054602SMark Johnston bzero(ui->ui_x1, sizeof(ui->ui_x1)); 306e5054602SMark Johnston ui->ui_pr = IPPROTO_UDP; 307e5054602SMark Johnston ui->ui_len = htons(m->m_pkthdr.len - sizeof(struct ip)); 308e5054602SMark Johnston ui->ui_ulen = ui->ui_len; 309e5054602SMark Johnston ui->ui_src = nd_client; 310e5054602SMark Johnston ui->ui_dst = nd_server; 311e5054602SMark Johnston /* Use this src port so that the server can connect() the socket */ 312e5054602SMark Johnston ui->ui_sport = htons(NETDUMP_ACKPORT); 313e5054602SMark Johnston ui->ui_dport = htons(nd_server_port); 314e5054602SMark Johnston ui->ui_sum = 0; 315e5054602SMark Johnston if ((ui->ui_sum = in_cksum(m, m->m_pkthdr.len)) == 0) 316e5054602SMark Johnston ui->ui_sum = 0xffff; 317e5054602SMark Johnston 318e5054602SMark Johnston ip = mtod(m, struct ip *); 319e5054602SMark Johnston ip->ip_v = IPVERSION; 320e5054602SMark Johnston ip->ip_hl = sizeof(struct ip) >> 2; 321e5054602SMark Johnston ip->ip_tos = 0; 322e5054602SMark Johnston ip->ip_len = htons(m->m_pkthdr.len); 323e5054602SMark Johnston ip->ip_id = 0; 324e5054602SMark Johnston ip->ip_off = htons(IP_DF); 325e5054602SMark Johnston ip->ip_ttl = 255; 326e5054602SMark Johnston ip->ip_sum = 0; 327e5054602SMark Johnston ip->ip_sum = in_cksum(m, sizeof(struct ip)); 328e5054602SMark Johnston 329e5054602SMark Johnston return (netdump_ether_output(m, nd_ifp, nd_gw_mac, ETHERTYPE_IP)); 330e5054602SMark Johnston } 331e5054602SMark Johnston 332e5054602SMark Johnston /* 333e5054602SMark Johnston * Builds and sends a single ARP request to locate the server 334e5054602SMark Johnston * 335e5054602SMark Johnston * Return value: 336e5054602SMark Johnston * 0 on success 337e5054602SMark Johnston * errno on error 338e5054602SMark Johnston */ 339e5054602SMark Johnston static int 340e5054602SMark Johnston netdump_send_arp(in_addr_t dst) 341e5054602SMark Johnston { 342e5054602SMark Johnston struct ether_addr bcast; 343e5054602SMark Johnston struct mbuf *m; 344e5054602SMark Johnston struct arphdr *ah; 345e5054602SMark Johnston int pktlen; 346e5054602SMark Johnston 34764e7d18fSConrad Meyer MPASS(netdump_enabled()); 348e5054602SMark Johnston 349e5054602SMark Johnston /* Fill-up a broadcast address. */ 350e5054602SMark Johnston memset(&bcast, 0xFF, ETHER_ADDR_LEN); 351e5054602SMark Johnston m = m_gethdr(M_NOWAIT, MT_DATA); 352e5054602SMark Johnston if (m == NULL) { 353e5054602SMark Johnston printf("netdump_send_arp: Out of mbufs\n"); 354e5054602SMark Johnston return (ENOBUFS); 355e5054602SMark Johnston } 356e5054602SMark Johnston pktlen = arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr)); 357e5054602SMark Johnston m->m_len = pktlen; 358e5054602SMark Johnston m->m_pkthdr.len = pktlen; 359e5054602SMark Johnston MH_ALIGN(m, pktlen); 360e5054602SMark Johnston ah = mtod(m, struct arphdr *); 361e5054602SMark Johnston ah->ar_hrd = htons(ARPHRD_ETHER); 362e5054602SMark Johnston ah->ar_pro = htons(ETHERTYPE_IP); 363e5054602SMark Johnston ah->ar_hln = ETHER_ADDR_LEN; 364e5054602SMark Johnston ah->ar_pln = sizeof(struct in_addr); 365e5054602SMark Johnston ah->ar_op = htons(ARPOP_REQUEST); 366e5054602SMark Johnston memcpy(ar_sha(ah), IF_LLADDR(nd_ifp), ETHER_ADDR_LEN); 367e5054602SMark Johnston ((struct in_addr *)ar_spa(ah))->s_addr = nd_client.s_addr; 368e5054602SMark Johnston bzero(ar_tha(ah), ETHER_ADDR_LEN); 369e5054602SMark Johnston ((struct in_addr *)ar_tpa(ah))->s_addr = dst; 370e5054602SMark Johnston return (netdump_ether_output(m, nd_ifp, bcast, ETHERTYPE_ARP)); 371e5054602SMark Johnston } 372e5054602SMark Johnston 373e5054602SMark Johnston /* 374e5054602SMark Johnston * Sends ARP requests to locate the server and waits for a response. 375e5054602SMark Johnston * We first try to ARP the server itself, and fall back to the provided 376e5054602SMark Johnston * gateway if the server appears to be off-link. 377e5054602SMark Johnston * 378e5054602SMark Johnston * Return value: 379e5054602SMark Johnston * 0 on success 380e5054602SMark Johnston * errno on error 381e5054602SMark Johnston */ 382e5054602SMark Johnston static int 383e5054602SMark Johnston netdump_arp_gw(void) 384e5054602SMark Johnston { 385e5054602SMark Johnston in_addr_t dst; 386e5054602SMark Johnston int error, polls, retries; 387e5054602SMark Johnston 388e5054602SMark Johnston dst = nd_server.s_addr; 389e5054602SMark Johnston restart: 390e5054602SMark Johnston for (retries = 0; retries < nd_arp_retries && have_gw_mac == 0; 391e5054602SMark Johnston retries++) { 392e5054602SMark Johnston error = netdump_send_arp(dst); 393e5054602SMark Johnston if (error != 0) 394e5054602SMark Johnston return (error); 395e5054602SMark Johnston for (polls = 0; polls < nd_polls && have_gw_mac == 0; polls++) { 396e5054602SMark Johnston netdump_network_poll(); 397e5054602SMark Johnston DELAY(500); 398e5054602SMark Johnston } 399e5054602SMark Johnston if (have_gw_mac == 0) 400e5054602SMark Johnston printf("(ARP retry)"); 401e5054602SMark Johnston } 402e5054602SMark Johnston if (have_gw_mac != 0) 403e5054602SMark Johnston return (0); 404e5054602SMark Johnston if (dst == nd_server.s_addr && nd_server.s_addr != nd_gateway.s_addr) { 405e5054602SMark Johnston printf("Failed to ARP server, trying to reach gateway...\n"); 406e5054602SMark Johnston dst = nd_gateway.s_addr; 407e5054602SMark Johnston goto restart; 408e5054602SMark Johnston } 409e5054602SMark Johnston 410e5054602SMark Johnston printf("\nARP timed out.\n"); 411e5054602SMark Johnston return (ETIMEDOUT); 412e5054602SMark Johnston } 413e5054602SMark Johnston 414e5054602SMark Johnston /* 415e5054602SMark Johnston * Dummy free function for netdump clusters. 416e5054602SMark Johnston */ 417e5054602SMark Johnston static void 418e5054602SMark Johnston netdump_mbuf_free(struct mbuf *m __unused) 419e5054602SMark Johnston { 420e5054602SMark Johnston } 421e5054602SMark Johnston 422e5054602SMark Johnston /* 423e5054602SMark Johnston * Construct and reliably send a netdump packet. May fail from a resource 424e5054602SMark Johnston * shortage or extreme number of unacknowledged retransmissions. Wait for 425e5054602SMark Johnston * an acknowledgement before returning. Splits packets into chunks small 426e5054602SMark Johnston * enough to be sent without fragmentation (looks up the interface MTU) 427e5054602SMark Johnston * 428e5054602SMark Johnston * Parameters: 429e5054602SMark Johnston * type netdump packet type (HERALD, FINISHED, or VMCORE) 430e5054602SMark Johnston * offset vmcore data offset (bytes) 431e5054602SMark Johnston * data vmcore data 432e5054602SMark Johnston * datalen vmcore data size (bytes) 433e5054602SMark Johnston * 434e5054602SMark Johnston * Returns: 435e5054602SMark Johnston * int see errno.h, 0 for success 436e5054602SMark Johnston */ 437e5054602SMark Johnston static int 438e5054602SMark Johnston netdump_send(uint32_t type, off_t offset, unsigned char *data, uint32_t datalen) 439e5054602SMark Johnston { 440e5054602SMark Johnston struct netdump_msg_hdr *nd_msg_hdr; 441e5054602SMark Johnston struct mbuf *m, *m2; 442e5054602SMark Johnston uint64_t want_acks; 443e5054602SMark Johnston uint32_t i, pktlen, sent_so_far; 444e5054602SMark Johnston int retries, polls, error; 445e5054602SMark Johnston 446e5054602SMark Johnston want_acks = 0; 447e5054602SMark Johnston rcvd_acks = 0; 448e5054602SMark Johnston retries = 0; 449e5054602SMark Johnston 45064e7d18fSConrad Meyer MPASS(netdump_enabled()); 451e5054602SMark Johnston 452e5054602SMark Johnston retransmit: 453e5054602SMark Johnston /* Chunks can be too big to fit in packets. */ 454e5054602SMark Johnston for (i = sent_so_far = 0; sent_so_far < datalen || 455e5054602SMark Johnston (i == 0 && datalen == 0); i++) { 456e5054602SMark Johnston pktlen = datalen - sent_so_far; 457e5054602SMark Johnston 458e5054602SMark Johnston /* First bound: the packet structure. */ 459e5054602SMark Johnston pktlen = min(pktlen, NETDUMP_DATASIZE); 460e5054602SMark Johnston 461e5054602SMark Johnston /* Second bound: the interface MTU (assume no IP options). */ 462e5054602SMark Johnston pktlen = min(pktlen, nd_ifp->if_mtu - sizeof(struct udpiphdr) - 463e5054602SMark Johnston sizeof(struct netdump_msg_hdr)); 464e5054602SMark Johnston 465e5054602SMark Johnston /* 466e5054602SMark Johnston * Check if it is retransmitting and this has been ACKed 467e5054602SMark Johnston * already. 468e5054602SMark Johnston */ 469e5054602SMark Johnston if ((rcvd_acks & (1 << i)) != 0) { 470e5054602SMark Johnston sent_so_far += pktlen; 471e5054602SMark Johnston continue; 472e5054602SMark Johnston } 473e5054602SMark Johnston 474e5054602SMark Johnston /* 475e5054602SMark Johnston * Get and fill a header mbuf, then chain data as an extended 476e5054602SMark Johnston * mbuf. 477e5054602SMark Johnston */ 478e5054602SMark Johnston m = m_gethdr(M_NOWAIT, MT_DATA); 479e5054602SMark Johnston if (m == NULL) { 480e5054602SMark Johnston printf("netdump_send: Out of mbufs\n"); 481e5054602SMark Johnston return (ENOBUFS); 482e5054602SMark Johnston } 483e5054602SMark Johnston m->m_len = sizeof(struct netdump_msg_hdr); 484e5054602SMark Johnston m->m_pkthdr.len = sizeof(struct netdump_msg_hdr); 485e5054602SMark Johnston MH_ALIGN(m, sizeof(struct netdump_msg_hdr)); 486e5054602SMark Johnston nd_msg_hdr = mtod(m, struct netdump_msg_hdr *); 487e5054602SMark Johnston nd_msg_hdr->mh_seqno = htonl(nd_seqno + i); 488e5054602SMark Johnston nd_msg_hdr->mh_type = htonl(type); 489e5054602SMark Johnston nd_msg_hdr->mh_offset = htobe64(offset + sent_so_far); 490e5054602SMark Johnston nd_msg_hdr->mh_len = htonl(pktlen); 491e5054602SMark Johnston nd_msg_hdr->mh__pad = 0; 492e5054602SMark Johnston 493e5054602SMark Johnston if (pktlen != 0) { 494e5054602SMark Johnston m2 = m_get(M_NOWAIT, MT_DATA); 495e5054602SMark Johnston if (m2 == NULL) { 496e5054602SMark Johnston m_freem(m); 497e5054602SMark Johnston printf("netdump_send: Out of mbufs\n"); 498e5054602SMark Johnston return (ENOBUFS); 499e5054602SMark Johnston } 500e5054602SMark Johnston MEXTADD(m2, data + sent_so_far, pktlen, 501e5054602SMark Johnston netdump_mbuf_free, NULL, NULL, 0, EXT_DISPOSABLE); 502e5054602SMark Johnston m2->m_len = pktlen; 503e5054602SMark Johnston 504e5054602SMark Johnston m_cat(m, m2); 505e5054602SMark Johnston m->m_pkthdr.len += pktlen; 506e5054602SMark Johnston } 507e5054602SMark Johnston error = netdump_udp_output(m); 508e5054602SMark Johnston if (error != 0) 509e5054602SMark Johnston return (error); 510e5054602SMark Johnston 511e5054602SMark Johnston /* Note that we're waiting for this packet in the bitfield. */ 512e5054602SMark Johnston want_acks |= (1 << i); 513e5054602SMark Johnston sent_so_far += pktlen; 514e5054602SMark Johnston } 515e5054602SMark Johnston if (i >= NETDUMP_MAX_IN_FLIGHT) 516e5054602SMark Johnston printf("Warning: Sent more than %d packets (%d). " 517e5054602SMark Johnston "Acknowledgements will fail unless the size of " 518e5054602SMark Johnston "rcvd_acks/want_acks is increased.\n", 519e5054602SMark Johnston NETDUMP_MAX_IN_FLIGHT, i); 520e5054602SMark Johnston 521e5054602SMark Johnston /* 522e5054602SMark Johnston * Wait for acks. A *real* window would speed things up considerably. 523e5054602SMark Johnston */ 524e5054602SMark Johnston polls = 0; 525e5054602SMark Johnston while (rcvd_acks != want_acks) { 526e5054602SMark Johnston if (polls++ > nd_polls) { 527e5054602SMark Johnston if (retries++ > nd_retries) 528e5054602SMark Johnston return (ETIMEDOUT); 529e5054602SMark Johnston printf(". "); 530e5054602SMark Johnston goto retransmit; 531e5054602SMark Johnston } 532e5054602SMark Johnston netdump_network_poll(); 533e5054602SMark Johnston DELAY(500); 534e5054602SMark Johnston } 535e5054602SMark Johnston nd_seqno += i; 536e5054602SMark Johnston return (0); 537e5054602SMark Johnston } 538e5054602SMark Johnston 539e5054602SMark Johnston /* 540e5054602SMark Johnston * Handler for IP packets: checks their sanity and then processes any netdump 541e5054602SMark Johnston * ACK packets it finds. 542e5054602SMark Johnston * 543e5054602SMark Johnston * It needs to replicate partially the behaviour of ip_input() and 544e5054602SMark Johnston * udp_input(). 545e5054602SMark Johnston * 546e5054602SMark Johnston * Parameters: 547e5054602SMark Johnston * mb a pointer to an mbuf * containing the packet received 548e5054602SMark Johnston * Updates *mb if m_pullup et al change the pointer 549e5054602SMark Johnston * Assumes the calling function will take care of freeing the mbuf 550e5054602SMark Johnston */ 551e5054602SMark Johnston static void 552e5054602SMark Johnston netdump_handle_ip(struct mbuf **mb) 553e5054602SMark Johnston { 554e5054602SMark Johnston struct ip *ip; 555e5054602SMark Johnston struct udpiphdr *udp; 556e5054602SMark Johnston struct netdump_ack *nd_ack; 557e5054602SMark Johnston struct mbuf *m; 558e5054602SMark Johnston int rcv_ackno; 559e5054602SMark Johnston unsigned short hlen; 560e5054602SMark Johnston 561e5054602SMark Johnston /* IP processing. */ 562e5054602SMark Johnston m = *mb; 563e5054602SMark Johnston if (m->m_pkthdr.len < sizeof(struct ip)) { 564e5054602SMark Johnston NETDDEBUG("dropping packet too small for IP header\n"); 565e5054602SMark Johnston return; 566e5054602SMark Johnston } 567e5054602SMark Johnston if (m->m_len < sizeof(struct ip)) { 568e5054602SMark Johnston m = m_pullup(m, sizeof(struct ip)); 569e5054602SMark Johnston *mb = m; 570e5054602SMark Johnston if (m == NULL) { 571e5054602SMark Johnston NETDDEBUG("m_pullup failed\n"); 572e5054602SMark Johnston return; 573e5054602SMark Johnston } 574e5054602SMark Johnston } 575e5054602SMark Johnston ip = mtod(m, struct ip *); 576e5054602SMark Johnston 577e5054602SMark Johnston /* IP version. */ 578e5054602SMark Johnston if (ip->ip_v != IPVERSION) { 579e5054602SMark Johnston NETDDEBUG("bad IP version %d\n", ip->ip_v); 580e5054602SMark Johnston return; 581e5054602SMark Johnston } 582e5054602SMark Johnston 583e5054602SMark Johnston /* Header length. */ 584e5054602SMark Johnston hlen = ip->ip_hl << 2; 585e5054602SMark Johnston if (hlen < sizeof(struct ip)) { 586e5054602SMark Johnston NETDDEBUG("bad IP header length (%hu)\n", hlen); 587e5054602SMark Johnston return; 588e5054602SMark Johnston } 589e5054602SMark Johnston if (hlen > m->m_len) { 590e5054602SMark Johnston m = m_pullup(m, hlen); 591e5054602SMark Johnston *mb = m; 592e5054602SMark Johnston if (m == NULL) { 593e5054602SMark Johnston NETDDEBUG("m_pullup failed\n"); 594e5054602SMark Johnston return; 595e5054602SMark Johnston } 596e5054602SMark Johnston ip = mtod(m, struct ip *); 597e5054602SMark Johnston } 598e5054602SMark Johnston /* Ignore packets with IP options. */ 599e5054602SMark Johnston if (hlen > sizeof(struct ip)) { 600e5054602SMark Johnston NETDDEBUG("drop packet with IP options\n"); 601e5054602SMark Johnston return; 602e5054602SMark Johnston } 603e5054602SMark Johnston 604e5054602SMark Johnston #ifdef INVARIANTS 6056c1c6ae5SRodney W. Grimes if ((IN_LOOPBACK(ntohl(ip->ip_dst.s_addr)) || 6066c1c6ae5SRodney W. Grimes IN_LOOPBACK(ntohl(ip->ip_src.s_addr))) && 607e5054602SMark Johnston (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { 608e5054602SMark Johnston NETDDEBUG("Bad IP header (RFC1122)\n"); 609e5054602SMark Johnston return; 610e5054602SMark Johnston } 611e5054602SMark Johnston #endif 612e5054602SMark Johnston 613e5054602SMark Johnston /* Checksum. */ 614e5054602SMark Johnston if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) != 0) { 615e5054602SMark Johnston if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) == 0) { 616e5054602SMark Johnston NETDDEBUG("bad IP checksum\n"); 617e5054602SMark Johnston return; 618e5054602SMark Johnston } 619e5054602SMark Johnston } else { 620e5054602SMark Johnston /* XXX */ ; 621e5054602SMark Johnston } 622e5054602SMark Johnston 623e5054602SMark Johnston /* Convert fields to host byte order. */ 624e5054602SMark Johnston ip->ip_len = ntohs(ip->ip_len); 625e5054602SMark Johnston if (ip->ip_len < hlen) { 626e5054602SMark Johnston NETDDEBUG("IP packet smaller (%hu) than header (%hu)\n", 627e5054602SMark Johnston ip->ip_len, hlen); 628e5054602SMark Johnston return; 629e5054602SMark Johnston } 630e5054602SMark Johnston if (m->m_pkthdr.len < ip->ip_len) { 631e5054602SMark Johnston NETDDEBUG("IP packet bigger (%hu) than ethernet packet (%d)\n", 632e5054602SMark Johnston ip->ip_len, m->m_pkthdr.len); 633e5054602SMark Johnston return; 634e5054602SMark Johnston } 635e5054602SMark Johnston if (m->m_pkthdr.len > ip->ip_len) { 636e5054602SMark Johnston 637e5054602SMark Johnston /* Truncate the packet to the IP length. */ 638e5054602SMark Johnston if (m->m_len == m->m_pkthdr.len) { 639e5054602SMark Johnston m->m_len = ip->ip_len; 640e5054602SMark Johnston m->m_pkthdr.len = ip->ip_len; 641e5054602SMark Johnston } else 642e5054602SMark Johnston m_adj(m, ip->ip_len - m->m_pkthdr.len); 643e5054602SMark Johnston } 644e5054602SMark Johnston 645e5054602SMark Johnston ip->ip_off = ntohs(ip->ip_off); 646e5054602SMark Johnston 647e5054602SMark Johnston /* Check that the source is the server's IP. */ 648e5054602SMark Johnston if (ip->ip_src.s_addr != nd_server.s_addr) { 649e5054602SMark Johnston NETDDEBUG("drop packet not from server (from 0x%x)\n", 650e5054602SMark Johnston ip->ip_src.s_addr); 651e5054602SMark Johnston return; 652e5054602SMark Johnston } 653e5054602SMark Johnston 654e5054602SMark Johnston /* Check if the destination IP is ours. */ 655e5054602SMark Johnston if (ip->ip_dst.s_addr != nd_client.s_addr) { 656e5054602SMark Johnston NETDDEBUGV("drop packet not to our IP\n"); 657e5054602SMark Johnston return; 658e5054602SMark Johnston } 659e5054602SMark Johnston 660e5054602SMark Johnston if (ip->ip_p != IPPROTO_UDP) { 661e5054602SMark Johnston NETDDEBUG("drop non-UDP packet\n"); 662e5054602SMark Johnston return; 663e5054602SMark Johnston } 664e5054602SMark Johnston 665e5054602SMark Johnston /* Do not deal with fragments. */ 666e5054602SMark Johnston if ((ip->ip_off & (IP_MF | IP_OFFMASK)) != 0) { 667e5054602SMark Johnston NETDDEBUG("drop fragmented packet\n"); 668e5054602SMark Johnston return; 669e5054602SMark Johnston } 670e5054602SMark Johnston 671e5054602SMark Johnston /* UDP custom is to have packet length not include IP header. */ 672e5054602SMark Johnston ip->ip_len -= hlen; 673e5054602SMark Johnston 674e5054602SMark Johnston /* UDP processing. */ 675e5054602SMark Johnston 676e5054602SMark Johnston /* Get IP and UDP headers together, along with the netdump packet. */ 677e5054602SMark Johnston if (m->m_pkthdr.len < 678e5054602SMark Johnston sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) { 679e5054602SMark Johnston NETDDEBUG("ignoring small packet\n"); 680e5054602SMark Johnston return; 681e5054602SMark Johnston } 682e5054602SMark Johnston if (m->m_len < sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) { 683e5054602SMark Johnston m = m_pullup(m, sizeof(struct udpiphdr) + 684e5054602SMark Johnston sizeof(struct netdump_ack)); 685e5054602SMark Johnston *mb = m; 686e5054602SMark Johnston if (m == NULL) { 687e5054602SMark Johnston NETDDEBUG("m_pullup failed\n"); 688e5054602SMark Johnston return; 689e5054602SMark Johnston } 690e5054602SMark Johnston } 691e5054602SMark Johnston udp = mtod(m, struct udpiphdr *); 692e5054602SMark Johnston 693e5054602SMark Johnston if (ntohs(udp->ui_u.uh_dport) != NETDUMP_ACKPORT) { 694e5054602SMark Johnston NETDDEBUG("not on the netdump port.\n"); 695e5054602SMark Johnston return; 696e5054602SMark Johnston } 697e5054602SMark Johnston 698e5054602SMark Johnston /* Netdump processing. */ 699e5054602SMark Johnston 700e5054602SMark Johnston /* 701e5054602SMark Johnston * Packet is meant for us. Extract the ack sequence number and the 702e5054602SMark Johnston * port number if necessary. 703e5054602SMark Johnston */ 704e5054602SMark Johnston nd_ack = (struct netdump_ack *)(mtod(m, caddr_t) + 705e5054602SMark Johnston sizeof(struct udpiphdr)); 706e5054602SMark Johnston rcv_ackno = ntohl(nd_ack->na_seqno); 707e5054602SMark Johnston if (nd_server_port == NETDUMP_PORT) 708e5054602SMark Johnston nd_server_port = ntohs(udp->ui_u.uh_sport); 709e5054602SMark Johnston if (rcv_ackno >= nd_seqno + NETDUMP_MAX_IN_FLIGHT) 710e5054602SMark Johnston printf("%s: ACK %d too far in future!\n", __func__, rcv_ackno); 711e5054602SMark Johnston else if (rcv_ackno >= nd_seqno) { 712e5054602SMark Johnston /* We're interested in this ack. Record it. */ 713e5054602SMark Johnston rcvd_acks |= 1 << (rcv_ackno - nd_seqno); 714e5054602SMark Johnston } 715e5054602SMark Johnston } 716e5054602SMark Johnston 717e5054602SMark Johnston /* 718e5054602SMark Johnston * Handler for ARP packets: checks their sanity and then 719e5054602SMark Johnston * 1. If the ARP is a request for our IP, respond with our MAC address 720e5054602SMark Johnston * 2. If the ARP is a response from our server, record its MAC address 721e5054602SMark Johnston * 722e5054602SMark Johnston * It needs to replicate partially the behaviour of arpintr() and 723e5054602SMark Johnston * in_arpinput(). 724e5054602SMark Johnston * 725e5054602SMark Johnston * Parameters: 726e5054602SMark Johnston * mb a pointer to an mbuf * containing the packet received 727e5054602SMark Johnston * Updates *mb if m_pullup et al change the pointer 728e5054602SMark Johnston * Assumes the calling function will take care of freeing the mbuf 729e5054602SMark Johnston */ 730e5054602SMark Johnston static void 731e5054602SMark Johnston netdump_handle_arp(struct mbuf **mb) 732e5054602SMark Johnston { 733e5054602SMark Johnston char buf[INET_ADDRSTRLEN]; 734e5054602SMark Johnston struct in_addr isaddr, itaddr, myaddr; 735e5054602SMark Johnston struct ether_addr dst; 736e5054602SMark Johnston struct mbuf *m; 737e5054602SMark Johnston struct arphdr *ah; 738e5054602SMark Johnston struct ifnet *ifp; 739e5054602SMark Johnston uint8_t *enaddr; 740e5054602SMark Johnston int req_len, op; 741e5054602SMark Johnston 742e5054602SMark Johnston m = *mb; 743e5054602SMark Johnston ifp = m->m_pkthdr.rcvif; 744e5054602SMark Johnston if (m->m_len < sizeof(struct arphdr)) { 745e5054602SMark Johnston m = m_pullup(m, sizeof(struct arphdr)); 746e5054602SMark Johnston *mb = m; 747e5054602SMark Johnston if (m == NULL) { 748e5054602SMark Johnston NETDDEBUG("runt packet: m_pullup failed\n"); 749e5054602SMark Johnston return; 750e5054602SMark Johnston } 751e5054602SMark Johnston } 752e5054602SMark Johnston 753e5054602SMark Johnston ah = mtod(m, struct arphdr *); 754e5054602SMark Johnston if (ntohs(ah->ar_hrd) != ARPHRD_ETHER) { 755e5054602SMark Johnston NETDDEBUG("unknown hardware address 0x%2D)\n", 756e5054602SMark Johnston (unsigned char *)&ah->ar_hrd, ""); 757e5054602SMark Johnston return; 758e5054602SMark Johnston } 759e5054602SMark Johnston if (ntohs(ah->ar_pro) != ETHERTYPE_IP) { 760e5054602SMark Johnston NETDDEBUG("drop ARP for unknown protocol %d\n", 761e5054602SMark Johnston ntohs(ah->ar_pro)); 762e5054602SMark Johnston return; 763e5054602SMark Johnston } 764e5054602SMark Johnston req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr)); 765e5054602SMark Johnston if (m->m_len < req_len) { 766e5054602SMark Johnston m = m_pullup(m, req_len); 767e5054602SMark Johnston *mb = m; 768e5054602SMark Johnston if (m == NULL) { 769e5054602SMark Johnston NETDDEBUG("runt packet: m_pullup failed\n"); 770e5054602SMark Johnston return; 771e5054602SMark Johnston } 772e5054602SMark Johnston } 773e5054602SMark Johnston ah = mtod(m, struct arphdr *); 774e5054602SMark Johnston 775e5054602SMark Johnston op = ntohs(ah->ar_op); 776e5054602SMark Johnston memcpy(&isaddr, ar_spa(ah), sizeof(isaddr)); 777e5054602SMark Johnston memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr)); 778e5054602SMark Johnston enaddr = (uint8_t *)IF_LLADDR(ifp); 779e5054602SMark Johnston myaddr = nd_client; 780e5054602SMark Johnston 781e5054602SMark Johnston if (memcmp(ar_sha(ah), enaddr, ifp->if_addrlen) == 0) { 782e5054602SMark Johnston NETDDEBUG("ignoring ARP from myself\n"); 783e5054602SMark Johnston return; 784e5054602SMark Johnston } 785e5054602SMark Johnston 786e5054602SMark Johnston if (isaddr.s_addr == nd_client.s_addr) { 787e5054602SMark Johnston printf("%s: %*D is using my IP address %s!\n", __func__, 788e5054602SMark Johnston ifp->if_addrlen, (u_char *)ar_sha(ah), ":", 789e5054602SMark Johnston inet_ntoa_r(isaddr, buf)); 790e5054602SMark Johnston return; 791e5054602SMark Johnston } 792e5054602SMark Johnston 793e5054602SMark Johnston if (memcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen) == 0) { 794e5054602SMark Johnston NETDDEBUG("ignoring ARP from broadcast address\n"); 795e5054602SMark Johnston return; 796e5054602SMark Johnston } 797e5054602SMark Johnston 798e5054602SMark Johnston if (op == ARPOP_REPLY) { 799e5054602SMark Johnston if (isaddr.s_addr != nd_gateway.s_addr && 800e5054602SMark Johnston isaddr.s_addr != nd_server.s_addr) { 801e5054602SMark Johnston inet_ntoa_r(isaddr, buf); 802e5054602SMark Johnston NETDDEBUG( 803e5054602SMark Johnston "ignoring ARP reply from %s (not netdump server)\n", 804e5054602SMark Johnston buf); 805e5054602SMark Johnston return; 806e5054602SMark Johnston } 807e5054602SMark Johnston memcpy(nd_gw_mac.octet, ar_sha(ah), 808e5054602SMark Johnston min(ah->ar_hln, ETHER_ADDR_LEN)); 809e5054602SMark Johnston have_gw_mac = 1; 810e5054602SMark Johnston NETDDEBUG("got server MAC address %6D\n", nd_gw_mac.octet, ":"); 811e5054602SMark Johnston return; 812e5054602SMark Johnston } 813e5054602SMark Johnston 814e5054602SMark Johnston if (op != ARPOP_REQUEST) { 815e5054602SMark Johnston NETDDEBUG("ignoring ARP non-request/reply\n"); 816e5054602SMark Johnston return; 817e5054602SMark Johnston } 818e5054602SMark Johnston 819e5054602SMark Johnston if (itaddr.s_addr != nd_client.s_addr) { 820e5054602SMark Johnston NETDDEBUG("ignoring ARP not to our IP\n"); 821e5054602SMark Johnston return; 822e5054602SMark Johnston } 823e5054602SMark Johnston 824e5054602SMark Johnston memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); 825e5054602SMark Johnston memcpy(ar_sha(ah), enaddr, ah->ar_hln); 826e5054602SMark Johnston memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln); 827e5054602SMark Johnston memcpy(ar_spa(ah), &itaddr, ah->ar_pln); 828e5054602SMark Johnston ah->ar_op = htons(ARPOP_REPLY); 829e5054602SMark Johnston ah->ar_pro = htons(ETHERTYPE_IP); 830e5054602SMark Johnston m->m_flags &= ~(M_BCAST|M_MCAST); 831e5054602SMark Johnston m->m_len = arphdr_len(ah); 832e5054602SMark Johnston m->m_pkthdr.len = m->m_len; 833e5054602SMark Johnston 834e5054602SMark Johnston memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN); 835e5054602SMark Johnston netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP); 836e5054602SMark Johnston *mb = NULL; 837e5054602SMark Johnston } 838e5054602SMark Johnston 839e5054602SMark Johnston /* 840e5054602SMark Johnston * Handler for incoming packets directly from the network adapter 841e5054602SMark Johnston * Identifies the packet type (IP or ARP) and passes it along to one of the 842e5054602SMark Johnston * helper functions netdump_handle_ip or netdump_handle_arp. 843e5054602SMark Johnston * 844e5054602SMark Johnston * It needs to replicate partially the behaviour of ether_input() and 845e5054602SMark Johnston * ether_demux(). 846e5054602SMark Johnston * 847e5054602SMark Johnston * Parameters: 848e5054602SMark Johnston * ifp the interface the packet came from (should be nd_ifp) 849e5054602SMark Johnston * m an mbuf containing the packet received 850e5054602SMark Johnston */ 851e5054602SMark Johnston static void 852e5054602SMark Johnston netdump_pkt_in(struct ifnet *ifp, struct mbuf *m) 853e5054602SMark Johnston { 854e5054602SMark Johnston struct ifreq ifr; 855e5054602SMark Johnston struct ether_header *eh; 856e5054602SMark Johnston u_short etype; 857e5054602SMark Johnston 858e5054602SMark Johnston /* Ethernet processing. */ 859e5054602SMark Johnston if ((m->m_flags & M_PKTHDR) == 0) { 860e5054602SMark Johnston NETDDEBUG_IF(ifp, "discard frame without packet header\n"); 861e5054602SMark Johnston goto done; 862e5054602SMark Johnston } 863e5054602SMark Johnston if (m->m_len < ETHER_HDR_LEN) { 864e5054602SMark Johnston NETDDEBUG_IF(ifp, 865e5054602SMark Johnston "discard frame without leading eth header (len %u pktlen %u)\n", 866e5054602SMark Johnston m->m_len, m->m_pkthdr.len); 867e5054602SMark Johnston goto done; 868e5054602SMark Johnston } 869e5054602SMark Johnston if ((m->m_flags & M_HASFCS) != 0) { 870e5054602SMark Johnston m_adj(m, -ETHER_CRC_LEN); 871e5054602SMark Johnston m->m_flags &= ~M_HASFCS; 872e5054602SMark Johnston } 873e5054602SMark Johnston eh = mtod(m, struct ether_header *); 874e5054602SMark Johnston etype = ntohs(eh->ether_type); 875e5054602SMark Johnston if ((m->m_flags & M_VLANTAG) != 0 || etype == ETHERTYPE_VLAN) { 876e5054602SMark Johnston NETDDEBUG_IF(ifp, "ignoring vlan packets\n"); 877e5054602SMark Johnston goto done; 878e5054602SMark Johnston } 879e5054602SMark Johnston if (if_gethwaddr(ifp, &ifr) != 0) { 880e5054602SMark Johnston NETDDEBUG_IF(ifp, "failed to get hw addr for interface\n"); 881e5054602SMark Johnston goto done; 882e5054602SMark Johnston } 883e5054602SMark Johnston if (memcmp(ifr.ifr_addr.sa_data, eh->ether_dhost, 884e5054602SMark Johnston ETHER_ADDR_LEN) != 0) { 885e5054602SMark Johnston NETDDEBUG_IF(ifp, 886e5054602SMark Johnston "discard frame with incorrect destination addr\n"); 887e5054602SMark Johnston goto done; 888e5054602SMark Johnston } 889e5054602SMark Johnston 890e5054602SMark Johnston /* Done ethernet processing. Strip off the ethernet header. */ 891e5054602SMark Johnston m_adj(m, ETHER_HDR_LEN); 892e5054602SMark Johnston switch (etype) { 893e5054602SMark Johnston case ETHERTYPE_ARP: 894e5054602SMark Johnston netdump_handle_arp(&m); 895e5054602SMark Johnston break; 896e5054602SMark Johnston case ETHERTYPE_IP: 897e5054602SMark Johnston netdump_handle_ip(&m); 898e5054602SMark Johnston break; 899e5054602SMark Johnston default: 900e5054602SMark Johnston NETDDEBUG_IF(ifp, "dropping unknown ethertype %hu\n", etype); 901e5054602SMark Johnston break; 902e5054602SMark Johnston } 903e5054602SMark Johnston done: 904e5054602SMark Johnston if (m != NULL) 905e5054602SMark Johnston m_freem(m); 906e5054602SMark Johnston } 907e5054602SMark Johnston 908e5054602SMark Johnston /* 909e5054602SMark Johnston * After trapping, instead of assuming that most of the network stack is sane, 910e5054602SMark Johnston * we just poll the driver directly for packets. 911e5054602SMark Johnston */ 912e5054602SMark Johnston static void 913e5054602SMark Johnston netdump_network_poll(void) 914e5054602SMark Johnston { 915e5054602SMark Johnston 91664e7d18fSConrad Meyer MPASS(netdump_enabled()); 917e5054602SMark Johnston 918e5054602SMark Johnston nd_ifp->if_netdump_methods->nd_poll(nd_ifp, 1000); 919e5054602SMark Johnston } 920e5054602SMark Johnston 921e5054602SMark Johnston /*- 922e5054602SMark Johnston * Dumping specific primitives. 923e5054602SMark Johnston */ 924e5054602SMark Johnston 925e5054602SMark Johnston /* 926e5054602SMark Johnston * Callback from dumpsys() to dump a chunk of memory. 927e5054602SMark Johnston * Copies it out to our static buffer then sends it across the network. 928e5054602SMark Johnston * Detects the initial KDH and makes sure it is given a special packet type. 929e5054602SMark Johnston * 930e5054602SMark Johnston * Parameters: 931e5054602SMark Johnston * priv Unused. Optional private pointer. 932e5054602SMark Johnston * virtual Virtual address (where to read the data from) 933e5054602SMark Johnston * physical Unused. Physical memory address. 934e5054602SMark Johnston * offset Offset from start of core file 935e5054602SMark Johnston * length Data length 936e5054602SMark Johnston * 937e5054602SMark Johnston * Return value: 938e5054602SMark Johnston * 0 on success 939e5054602SMark Johnston * errno on error 940e5054602SMark Johnston */ 941e5054602SMark Johnston static int 942e5054602SMark Johnston netdump_dumper(void *priv __unused, void *virtual, 943e5054602SMark Johnston vm_offset_t physical __unused, off_t offset, size_t length) 944e5054602SMark Johnston { 945e5054602SMark Johnston int error; 946e5054602SMark Johnston 947e5054602SMark Johnston NETDDEBUGV("netdump_dumper(NULL, %p, NULL, %ju, %zu)\n", 948e5054602SMark Johnston virtual, (uintmax_t)offset, length); 949e5054602SMark Johnston 950e5054602SMark Johnston if (virtual == NULL) { 951*8726929dSMark Johnston if (nd_conf.nd_buf_len != 0) { 952*8726929dSMark Johnston error = netdump_send(NETDUMP_VMCORE, nd_conf.nd_tx_off, nd_buf, 953*8726929dSMark Johnston nd_conf.nd_buf_len); 954*8726929dSMark Johnston if (error != 0) { 955*8726929dSMark Johnston dump_failed = 1; 956*8726929dSMark Johnston } 957*8726929dSMark Johnston } 958*8726929dSMark Johnston 959e5054602SMark Johnston if (dump_failed != 0) 960e5054602SMark Johnston printf("failed to dump the kernel core\n"); 961e5054602SMark Johnston else if (netdump_send(NETDUMP_FINISHED, 0, NULL, 0) != 0) 962e5054602SMark Johnston printf("failed to close the transaction\n"); 963e5054602SMark Johnston else 964e5054602SMark Johnston printf("\nnetdump finished.\n"); 965e5054602SMark Johnston netdump_cleanup(); 966e5054602SMark Johnston return (0); 967e5054602SMark Johnston } 968e5054602SMark Johnston if (length > sizeof(nd_buf)) 969e5054602SMark Johnston return (ENOSPC); 970e5054602SMark Johnston 971*8726929dSMark Johnston if (nd_conf.nd_buf_len + length > sizeof(nd_buf) || 972*8726929dSMark Johnston (nd_conf.nd_buf_len != 0 && nd_conf.nd_tx_off + 973*8726929dSMark Johnston nd_conf.nd_buf_len != offset)) { 974*8726929dSMark Johnston error = netdump_send(NETDUMP_VMCORE, nd_conf.nd_tx_off, nd_buf, 975*8726929dSMark Johnston nd_conf.nd_buf_len); 976e5054602SMark Johnston if (error != 0) { 977e5054602SMark Johnston dump_failed = 1; 978e5054602SMark Johnston return (error); 979e5054602SMark Johnston } 980*8726929dSMark Johnston nd_conf.nd_buf_len = 0; 981*8726929dSMark Johnston nd_conf.nd_tx_off = offset; 982*8726929dSMark Johnston } 983*8726929dSMark Johnston 984*8726929dSMark Johnston memmove(nd_buf + nd_conf.nd_buf_len, virtual, length); 985*8726929dSMark Johnston nd_conf.nd_buf_len += length; 986*8726929dSMark Johnston 987e5054602SMark Johnston return (0); 988e5054602SMark Johnston } 989e5054602SMark Johnston 990e5054602SMark Johnston /* 991e5054602SMark Johnston * Perform any initalization needed prior to transmitting the kernel core. 992e5054602SMark Johnston */ 993e5054602SMark Johnston static int 994e5054602SMark Johnston netdump_start(struct dumperinfo *di) 995e5054602SMark Johnston { 996e5054602SMark Johnston char *path; 997e5054602SMark Johnston char buf[INET_ADDRSTRLEN]; 998e5054602SMark Johnston uint32_t len; 999e5054602SMark Johnston int error; 1000e5054602SMark Johnston 1001e5054602SMark Johnston error = 0; 1002e5054602SMark Johnston 1003e5054602SMark Johnston /* Check if the dumping is allowed to continue. */ 100464e7d18fSConrad Meyer if (!netdump_enabled()) 1005e5054602SMark Johnston return (EINVAL); 1006e5054602SMark Johnston 1007e5054602SMark Johnston if (panicstr == NULL) { 1008e5054602SMark Johnston printf( 1009e5054602SMark Johnston "netdump_start: netdump may only be used after a panic\n"); 1010e5054602SMark Johnston return (EINVAL); 1011e5054602SMark Johnston } 1012e5054602SMark Johnston 1013e5054602SMark Johnston if (nd_server.s_addr == INADDR_ANY) { 1014e5054602SMark Johnston printf("netdump_start: can't netdump; no server IP given\n"); 1015e5054602SMark Johnston return (EINVAL); 1016e5054602SMark Johnston } 1017e5054602SMark Johnston if (nd_client.s_addr == INADDR_ANY) { 1018e5054602SMark Johnston printf("netdump_start: can't netdump; no client IP given\n"); 1019e5054602SMark Johnston return (EINVAL); 1020e5054602SMark Johnston } 1021e5054602SMark Johnston 1022e5054602SMark Johnston /* We start dumping at offset 0. */ 1023e5054602SMark Johnston di->dumpoff = 0; 1024e5054602SMark Johnston 1025e5054602SMark Johnston nd_seqno = 1; 1026e5054602SMark Johnston 1027e5054602SMark Johnston /* 1028e5054602SMark Johnston * nd_server_port could have switched after the first ack the 1029e5054602SMark Johnston * first time it gets called. Adjust it accordingly. 1030e5054602SMark Johnston */ 1031e5054602SMark Johnston nd_server_port = NETDUMP_PORT; 1032e5054602SMark Johnston 1033e5054602SMark Johnston /* Switch to the netdump mbuf zones. */ 1034e5054602SMark Johnston netdump_mbuf_dump(); 1035e5054602SMark Johnston 1036e5054602SMark Johnston nd_ifp->if_netdump_methods->nd_event(nd_ifp, NETDUMP_START); 1037e5054602SMark Johnston 1038e5054602SMark Johnston /* Make the card use *our* receive callback. */ 1039e5054602SMark Johnston drv_if_input = nd_ifp->if_input; 1040e5054602SMark Johnston nd_ifp->if_input = netdump_pkt_in; 1041e5054602SMark Johnston 1042e5054602SMark Johnston if (nd_gateway.s_addr == INADDR_ANY) { 1043e5054602SMark Johnston restore_gw_addr = 1; 1044e5054602SMark Johnston nd_gateway.s_addr = nd_server.s_addr; 1045e5054602SMark Johnston } 1046e5054602SMark Johnston 1047e5054602SMark Johnston printf("netdump in progress. searching for server...\n"); 1048e5054602SMark Johnston if (netdump_arp_gw()) { 1049e5054602SMark Johnston printf("failed to locate server MAC address\n"); 1050e5054602SMark Johnston error = EINVAL; 1051e5054602SMark Johnston goto trig_abort; 1052e5054602SMark Johnston } 1053e5054602SMark Johnston 1054e5054602SMark Johnston if (nd_path[0] != '\0') { 1055e5054602SMark Johnston path = nd_path; 1056e5054602SMark Johnston len = strlen(path) + 1; 1057e5054602SMark Johnston } else { 1058e5054602SMark Johnston path = NULL; 1059e5054602SMark Johnston len = 0; 1060e5054602SMark Johnston } 1061e5054602SMark Johnston if (netdump_send(NETDUMP_HERALD, 0, path, len) != 0) { 1062e5054602SMark Johnston printf("failed to contact netdump server\n"); 1063e5054602SMark Johnston error = EINVAL; 1064e5054602SMark Johnston goto trig_abort; 1065e5054602SMark Johnston } 1066e5054602SMark Johnston printf("netdumping to %s (%6D)\n", inet_ntoa_r(nd_server, buf), 1067e5054602SMark Johnston nd_gw_mac.octet, ":"); 1068e5054602SMark Johnston return (0); 1069e5054602SMark Johnston 1070e5054602SMark Johnston trig_abort: 1071e5054602SMark Johnston netdump_cleanup(); 1072e5054602SMark Johnston return (error); 1073e5054602SMark Johnston } 1074e5054602SMark Johnston 1075e5054602SMark Johnston static int 1076e5054602SMark Johnston netdump_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh, 1077e5054602SMark Johnston void *key, uint32_t keysize) 1078e5054602SMark Johnston { 1079e5054602SMark Johnston int error; 1080e5054602SMark Johnston 1081e5054602SMark Johnston memcpy(nd_buf, kdh, sizeof(*kdh)); 1082e5054602SMark Johnston error = netdump_send(NETDUMP_KDH, 0, nd_buf, sizeof(*kdh)); 1083e5054602SMark Johnston if (error == 0 && keysize > 0) { 1084e5054602SMark Johnston if (keysize > sizeof(nd_buf)) 1085e5054602SMark Johnston return (EINVAL); 1086e5054602SMark Johnston memcpy(nd_buf, key, keysize); 1087e5054602SMark Johnston error = netdump_send(NETDUMP_EKCD_KEY, 0, nd_buf, keysize); 1088e5054602SMark Johnston } 1089e5054602SMark Johnston return (error); 1090e5054602SMark Johnston } 1091e5054602SMark Johnston 1092e5054602SMark Johnston /* 1093e5054602SMark Johnston * Cleanup routine for a possibly failed netdump. 1094e5054602SMark Johnston */ 1095e5054602SMark Johnston static void 1096e5054602SMark Johnston netdump_cleanup(void) 1097e5054602SMark Johnston { 1098e5054602SMark Johnston 1099e5054602SMark Johnston if (restore_gw_addr != 0) { 1100e5054602SMark Johnston nd_gateway.s_addr = INADDR_ANY; 1101e5054602SMark Johnston restore_gw_addr = 0; 1102e5054602SMark Johnston } 1103e5054602SMark Johnston if (drv_if_input != NULL) { 1104e5054602SMark Johnston nd_ifp->if_input = drv_if_input; 1105e5054602SMark Johnston drv_if_input = NULL; 1106e5054602SMark Johnston } 1107e5054602SMark Johnston nd_ifp->if_netdump_methods->nd_event(nd_ifp, NETDUMP_END); 1108e5054602SMark Johnston } 1109e5054602SMark Johnston 1110e5054602SMark Johnston /*- 1111e5054602SMark Johnston * KLD specific code. 1112e5054602SMark Johnston */ 1113e5054602SMark Johnston 1114e5054602SMark Johnston static struct cdevsw netdump_cdevsw = { 1115e5054602SMark Johnston .d_version = D_VERSION, 1116e5054602SMark Johnston .d_ioctl = netdump_ioctl, 1117e5054602SMark Johnston .d_name = "netdump", 1118e5054602SMark Johnston }; 1119e5054602SMark Johnston 1120e5054602SMark Johnston static struct cdev *netdump_cdev; 1121e5054602SMark Johnston 112264e7d18fSConrad Meyer static void 112364e7d18fSConrad Meyer netdump_unconfigure(void) 112464e7d18fSConrad Meyer { 112564e7d18fSConrad Meyer struct diocskerneldump_arg kda; 112664e7d18fSConrad Meyer 112764e7d18fSConrad Meyer NETDUMP_ASSERT_WLOCKED(); 112864e7d18fSConrad Meyer KASSERT(netdump_enabled(), ("%s: nd_ifp NULL", __func__)); 112964e7d18fSConrad Meyer 113064e7d18fSConrad Meyer bzero(&kda, sizeof(kda)); 113164e7d18fSConrad Meyer kda.kda_index = KDA_REMOVE_DEV; 113264e7d18fSConrad Meyer (void)dumper_remove(nd_conf.ndc_iface, &kda); 113364e7d18fSConrad Meyer 113464e7d18fSConrad Meyer netdump_mbuf_drain(); 113564e7d18fSConrad Meyer 113664e7d18fSConrad Meyer if_rele(nd_ifp); 113764e7d18fSConrad Meyer nd_ifp = NULL; 113864e7d18fSConrad Meyer 113964e7d18fSConrad Meyer bzero(&nd_conf, sizeof(nd_conf)); 114064e7d18fSConrad Meyer } 114164e7d18fSConrad Meyer 114264e7d18fSConrad Meyer static void 114364e7d18fSConrad Meyer netdump_ifdetach(void *arg __unused, struct ifnet *ifp) 114464e7d18fSConrad Meyer { 114564e7d18fSConrad Meyer 114664e7d18fSConrad Meyer NETDUMP_WLOCK(); 114764e7d18fSConrad Meyer if (ifp == nd_ifp) 114864e7d18fSConrad Meyer netdump_unconfigure(); 114964e7d18fSConrad Meyer NETDUMP_WUNLOCK(); 115064e7d18fSConrad Meyer } 115164e7d18fSConrad Meyer 1152e5054602SMark Johnston static int 11536b6e2954SConrad Meyer netdump_configure(struct diocskerneldump_arg *conf, struct thread *td) 1154e5054602SMark Johnston { 1155e5054602SMark Johnston struct ifnet *ifp; 1156e5054602SMark Johnston 115764e7d18fSConrad Meyer NETDUMP_ASSERT_WLOCKED(); 115864e7d18fSConrad Meyer 1159b35822d9SMark Johnston CURVNET_SET(TD_TO_VNET(td)); 1160b35822d9SMark Johnston if (!IS_DEFAULT_VNET(curvnet)) { 1161b35822d9SMark Johnston CURVNET_RESTORE(); 1162b35822d9SMark Johnston return (EINVAL); 1163b35822d9SMark Johnston } 116464e7d18fSConrad Meyer ifp = ifunit_ref(conf->kda_iface); 1165b35822d9SMark Johnston CURVNET_RESTORE(); 1166e5054602SMark Johnston 1167e5054602SMark Johnston if (ifp == NULL) 1168e5054602SMark Johnston return (ENOENT); 116964e7d18fSConrad Meyer if ((if_getflags(ifp) & IFF_UP) == 0) { 117064e7d18fSConrad Meyer if_rele(ifp); 1171e5054602SMark Johnston return (ENXIO); 117264e7d18fSConrad Meyer } 117364e7d18fSConrad Meyer if (!netdump_supported_nic(ifp) || ifp->if_type != IFT_ETHER) { 117464e7d18fSConrad Meyer if_rele(ifp); 11756b6e2954SConrad Meyer return (ENODEV); 117664e7d18fSConrad Meyer } 1177e5054602SMark Johnston 117864e7d18fSConrad Meyer if (netdump_enabled()) 117964e7d18fSConrad Meyer if_rele(nd_ifp); 1180e5054602SMark Johnston nd_ifp = ifp; 11816144b50fSConrad Meyer 118264e7d18fSConrad Meyer netdump_reinit_internal(ifp); 118364e7d18fSConrad Meyer 11846144b50fSConrad Meyer #define COPY_SIZED(elm) do { \ 11856144b50fSConrad Meyer _Static_assert(sizeof(nd_conf.ndc_ ## elm) == \ 11866144b50fSConrad Meyer sizeof(conf->kda_ ## elm), "elm " __XSTRING(elm) " mismatch"); \ 11876144b50fSConrad Meyer memcpy(&nd_conf.ndc_ ## elm, &conf->kda_ ## elm, \ 11886144b50fSConrad Meyer sizeof(nd_conf.ndc_ ## elm)); \ 11896144b50fSConrad Meyer } while (0) 11906144b50fSConrad Meyer COPY_SIZED(iface); 11916144b50fSConrad Meyer COPY_SIZED(server); 11926144b50fSConrad Meyer COPY_SIZED(client); 11936144b50fSConrad Meyer COPY_SIZED(gateway); 11946144b50fSConrad Meyer COPY_SIZED(af); 11956144b50fSConrad Meyer #undef COPY_SIZED 119664e7d18fSConrad Meyer 1197e5054602SMark Johnston return (0); 1198e5054602SMark Johnston } 1199e5054602SMark Johnston 1200e5054602SMark Johnston /* 1201e5054602SMark Johnston * Reinitialize the mbuf pool used by drivers while dumping. This is called 120264e7d18fSConrad Meyer * from the generic ioctl handler for SIOCSIFMTU after any NIC driver has 120364e7d18fSConrad Meyer * reconfigured itself. (I.e., it may not be a configured netdump interface.) 1204e5054602SMark Johnston */ 1205e5054602SMark Johnston void 1206e5054602SMark Johnston netdump_reinit(struct ifnet *ifp) 1207e5054602SMark Johnston { 120864e7d18fSConrad Meyer 120964e7d18fSConrad Meyer NETDUMP_WLOCK(); 121064e7d18fSConrad Meyer if (ifp != nd_ifp) { 121164e7d18fSConrad Meyer NETDUMP_WUNLOCK(); 121264e7d18fSConrad Meyer return; 121364e7d18fSConrad Meyer } 121464e7d18fSConrad Meyer netdump_reinit_internal(ifp); 121564e7d18fSConrad Meyer NETDUMP_WUNLOCK(); 121664e7d18fSConrad Meyer } 121764e7d18fSConrad Meyer 121864e7d18fSConrad Meyer static void 121964e7d18fSConrad Meyer netdump_reinit_internal(struct ifnet *ifp) 122064e7d18fSConrad Meyer { 1221e5054602SMark Johnston int clsize, nmbuf, ncl, nrxr; 1222e5054602SMark Johnston 122364e7d18fSConrad Meyer NETDUMP_ASSERT_WLOCKED(); 1224e5054602SMark Johnston 1225e5054602SMark Johnston ifp->if_netdump_methods->nd_init(ifp, &nrxr, &ncl, &clsize); 1226e5054602SMark Johnston KASSERT(nrxr > 0, ("invalid receive ring count %d", nrxr)); 1227e5054602SMark Johnston 1228e5054602SMark Johnston /* 1229e5054602SMark Johnston * We need two headers per message on the transmit side. Multiply by 1230e5054602SMark Johnston * four to give us some breathing room. 1231e5054602SMark Johnston */ 1232e5054602SMark Johnston nmbuf = ncl * (4 + nrxr); 1233e5054602SMark Johnston ncl *= nrxr; 1234e5054602SMark Johnston netdump_mbuf_reinit(nmbuf, ncl, clsize); 1235e5054602SMark Johnston } 1236e5054602SMark Johnston 1237e5054602SMark Johnston /* 1238e5054602SMark Johnston * ioctl(2) handler for the netdump device. This is currently only used to 1239e5054602SMark Johnston * register netdump as a dump device. 1240e5054602SMark Johnston * 1241e5054602SMark Johnston * Parameters: 1242e5054602SMark Johnston * dev, Unused. 1243e5054602SMark Johnston * cmd, The ioctl to be handled. 1244e5054602SMark Johnston * addr, The parameter for the ioctl. 1245e5054602SMark Johnston * flags, Unused. 1246e5054602SMark Johnston * td, The thread invoking this ioctl. 1247e5054602SMark Johnston * 1248e5054602SMark Johnston * Returns: 1249e5054602SMark Johnston * 0 on success, and an errno value on failure. 1250e5054602SMark Johnston */ 1251e5054602SMark Johnston static int 1252e5054602SMark Johnston netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr, 1253e5054602SMark Johnston int flags __unused, struct thread *td) 1254e5054602SMark Johnston { 12556b6e2954SConrad Meyer struct diocskerneldump_arg kda_copy, *conf; 1256e5054602SMark Johnston struct dumperinfo dumper; 1257e5054602SMark Johnston uint8_t *encryptedkey; 1258e5054602SMark Johnston int error; 1259a9f7f192SConrad Meyer #ifdef COMPAT_FREEBSD11 1260e5054602SMark Johnston u_int u; 1261a9f7f192SConrad Meyer #endif 12626b6e2954SConrad Meyer #ifdef COMPAT_FREEBSD12 12636b6e2954SConrad Meyer struct diocskerneldump_arg_freebsd12 *kda12; 12646b6e2954SConrad Meyer struct netdump_conf_freebsd12 *conf12; 12656b6e2954SConrad Meyer #endif 1266e5054602SMark Johnston 12676b6e2954SConrad Meyer conf = NULL; 1268e5054602SMark Johnston error = 0; 126964e7d18fSConrad Meyer NETDUMP_WLOCK(); 127064e7d18fSConrad Meyer 1271e5054602SMark Johnston switch (cmd) { 127260ade167SConrad Meyer #ifdef COMPAT_FREEBSD11 127360ade167SConrad Meyer case DIOCSKERNELDUMP_FREEBSD11: 12746b6e2954SConrad Meyer gone_in(13, "11.x ABI compatibility"); 1275e5054602SMark Johnston u = *(u_int *)addr; 1276e5054602SMark Johnston if (u != 0) { 1277e5054602SMark Johnston error = ENXIO; 1278e5054602SMark Johnston break; 1279e5054602SMark Johnston } 128064e7d18fSConrad Meyer if (netdump_enabled()) 128164e7d18fSConrad Meyer netdump_unconfigure(); 128260ade167SConrad Meyer break; 128360ade167SConrad Meyer #endif 12846b6e2954SConrad Meyer #ifdef COMPAT_FREEBSD12 12856b6e2954SConrad Meyer /* 12866b6e2954SConrad Meyer * Used by dumpon(8) in 12.x for clearing previous 12876b6e2954SConrad Meyer * configuration -- then NETDUMPSCONF_FREEBSD12 is used to 12886b6e2954SConrad Meyer * actually configure netdump. 12896b6e2954SConrad Meyer */ 12906b6e2954SConrad Meyer case DIOCSKERNELDUMP_FREEBSD12: 12916b6e2954SConrad Meyer gone_in(14, "12.x ABI compatibility"); 12926b6e2954SConrad Meyer 12936b6e2954SConrad Meyer kda12 = (void *)addr; 12946b6e2954SConrad Meyer if (kda12->kda12_enable) { 129560ade167SConrad Meyer error = ENXIO; 129660ade167SConrad Meyer break; 129760ade167SConrad Meyer } 129864e7d18fSConrad Meyer if (netdump_enabled()) 129964e7d18fSConrad Meyer netdump_unconfigure(); 1300e5054602SMark Johnston break; 13016b6e2954SConrad Meyer 13026b6e2954SConrad Meyer case NETDUMPGCONF_FREEBSD12: 13036b6e2954SConrad Meyer gone_in(14, "FreeBSD 12.x ABI compat"); 13046b6e2954SConrad Meyer conf12 = (void *)addr; 13056b6e2954SConrad Meyer 130664e7d18fSConrad Meyer if (!netdump_enabled()) { 1307e5054602SMark Johnston error = ENXIO; 1308e5054602SMark Johnston break; 1309e5054602SMark Johnston } 13106144b50fSConrad Meyer if (nd_conf.ndc_af != AF_INET) { 13116b6e2954SConrad Meyer error = EOPNOTSUPP; 1312e5054602SMark Johnston break; 13136b6e2954SConrad Meyer } 1314e5054602SMark Johnston 13156b6e2954SConrad Meyer strlcpy(conf12->ndc12_iface, nd_ifp->if_xname, 13166b6e2954SConrad Meyer sizeof(conf12->ndc12_iface)); 13176b6e2954SConrad Meyer memcpy(&conf12->ndc12_server, &nd_server, 13186b6e2954SConrad Meyer sizeof(conf12->ndc12_server)); 13196b6e2954SConrad Meyer memcpy(&conf12->ndc12_client, &nd_client, 13206b6e2954SConrad Meyer sizeof(conf12->ndc12_client)); 13216b6e2954SConrad Meyer memcpy(&conf12->ndc12_gateway, &nd_gateway, 13226b6e2954SConrad Meyer sizeof(conf12->ndc12_gateway)); 13236b6e2954SConrad Meyer break; 13246b6e2954SConrad Meyer #endif 13256b6e2954SConrad Meyer case DIOCGKERNELDUMP: 13266b6e2954SConrad Meyer conf = (void *)addr; 13276b6e2954SConrad Meyer /* 13286b6e2954SConrad Meyer * For now, index is ignored; netdump doesn't support multiple 13296b6e2954SConrad Meyer * configurations (yet). 13306b6e2954SConrad Meyer */ 133164e7d18fSConrad Meyer if (!netdump_enabled()) { 13326b6e2954SConrad Meyer error = ENXIO; 13336b6e2954SConrad Meyer conf = NULL; 13346b6e2954SConrad Meyer break; 13356b6e2954SConrad Meyer } 13366b6e2954SConrad Meyer 13376b6e2954SConrad Meyer strlcpy(conf->kda_iface, nd_ifp->if_xname, 13386b6e2954SConrad Meyer sizeof(conf->kda_iface)); 13396b6e2954SConrad Meyer memcpy(&conf->kda_server, &nd_server, sizeof(nd_server)); 13406b6e2954SConrad Meyer memcpy(&conf->kda_client, &nd_client, sizeof(nd_client)); 13416b6e2954SConrad Meyer memcpy(&conf->kda_gateway, &nd_gateway, sizeof(nd_gateway)); 13426144b50fSConrad Meyer conf->kda_af = nd_conf.ndc_af; 13436b6e2954SConrad Meyer conf = NULL; 13446b6e2954SConrad Meyer break; 13456b6e2954SConrad Meyer 13466b6e2954SConrad Meyer #ifdef COMPAT_FREEBSD12 13476b6e2954SConrad Meyer case NETDUMPSCONF_FREEBSD12: 13486b6e2954SConrad Meyer gone_in(14, "FreeBSD 12.x ABI compat"); 13496b6e2954SConrad Meyer 13506b6e2954SConrad Meyer conf12 = (struct netdump_conf_freebsd12 *)addr; 13516b6e2954SConrad Meyer 13526b6e2954SConrad Meyer _Static_assert(offsetof(struct diocskerneldump_arg, kda_server) 13536b6e2954SConrad Meyer == offsetof(struct netdump_conf_freebsd12, ndc12_server), 13546b6e2954SConrad Meyer "simplifying assumption"); 13556b6e2954SConrad Meyer 13566b6e2954SConrad Meyer memset(&kda_copy, 0, sizeof(kda_copy)); 13576b6e2954SConrad Meyer memcpy(&kda_copy, conf12, 13586b6e2954SConrad Meyer offsetof(struct diocskerneldump_arg, kda_server)); 13596b6e2954SConrad Meyer 13606b6e2954SConrad Meyer /* 12.x ABI could only configure IPv4 (INET) netdump. */ 13616b6e2954SConrad Meyer kda_copy.kda_af = AF_INET; 13626b6e2954SConrad Meyer memcpy(&kda_copy.kda_server.in4, &conf12->ndc12_server, 13636b6e2954SConrad Meyer sizeof(struct in_addr)); 13646b6e2954SConrad Meyer memcpy(&kda_copy.kda_client.in4, &conf12->ndc12_client, 13656b6e2954SConrad Meyer sizeof(struct in_addr)); 13666b6e2954SConrad Meyer memcpy(&kda_copy.kda_gateway.in4, &conf12->ndc12_gateway, 13676b6e2954SConrad Meyer sizeof(struct in_addr)); 13686b6e2954SConrad Meyer 13696b6e2954SConrad Meyer kda_copy.kda_index = 13706b6e2954SConrad Meyer (conf12->ndc12_kda.kda12_enable ? 0 : KDA_REMOVE_ALL); 13716b6e2954SConrad Meyer 13726b6e2954SConrad Meyer conf = &kda_copy; 13736b6e2954SConrad Meyer explicit_bzero(conf12, sizeof(*conf12)); 13746b6e2954SConrad Meyer /* FALLTHROUGH */ 13756b6e2954SConrad Meyer #endif 13766b6e2954SConrad Meyer case DIOCSKERNELDUMP: 13776b6e2954SConrad Meyer encryptedkey = NULL; 13786b6e2954SConrad Meyer if (cmd == DIOCSKERNELDUMP) { 13796b6e2954SConrad Meyer conf = (void *)addr; 13806b6e2954SConrad Meyer memcpy(&kda_copy, conf, sizeof(kda_copy)); 13816b6e2954SConrad Meyer } 13826b6e2954SConrad Meyer /* Netdump only supports IP4 at this time. */ 13836b6e2954SConrad Meyer if (conf->kda_af != AF_INET) { 13846b6e2954SConrad Meyer error = EPROTONOSUPPORT; 13856b6e2954SConrad Meyer break; 13866b6e2954SConrad Meyer } 13876b6e2954SConrad Meyer 13886b6e2954SConrad Meyer conf->kda_iface[sizeof(conf->kda_iface) - 1] = '\0'; 13896b6e2954SConrad Meyer if (conf->kda_index == KDA_REMOVE || 13906b6e2954SConrad Meyer conf->kda_index == KDA_REMOVE_DEV || 13916b6e2954SConrad Meyer conf->kda_index == KDA_REMOVE_ALL) { 139264e7d18fSConrad Meyer if (netdump_enabled()) 139364e7d18fSConrad Meyer netdump_unconfigure(); 139464e7d18fSConrad Meyer if (conf->kda_index == KDA_REMOVE_ALL) 139564e7d18fSConrad Meyer error = dumper_remove(NULL, conf); 1396e5054602SMark Johnston break; 1397e5054602SMark Johnston } 1398e5054602SMark Johnston 1399b35822d9SMark Johnston error = netdump_configure(conf, td); 1400e5054602SMark Johnston if (error != 0) 1401e5054602SMark Johnston break; 1402e5054602SMark Johnston 14036b6e2954SConrad Meyer if (conf->kda_encryption != KERNELDUMP_ENC_NONE) { 14046b6e2954SConrad Meyer if (conf->kda_encryptedkeysize <= 0 || 14056b6e2954SConrad Meyer conf->kda_encryptedkeysize > 14066b6e2954SConrad Meyer KERNELDUMP_ENCKEY_MAX_SIZE) { 14076b6e2954SConrad Meyer error = EINVAL; 14086b6e2954SConrad Meyer break; 14096b6e2954SConrad Meyer } 14106b6e2954SConrad Meyer encryptedkey = malloc(conf->kda_encryptedkeysize, 14116b6e2954SConrad Meyer M_TEMP, M_WAITOK); 14126b6e2954SConrad Meyer error = copyin(conf->kda_encryptedkey, encryptedkey, 14136b6e2954SConrad Meyer conf->kda_encryptedkeysize); 1414e5054602SMark Johnston if (error != 0) { 1415e5054602SMark Johnston free(encryptedkey, M_TEMP); 14166b6e2954SConrad Meyer break; 1417e5054602SMark Johnston } 14186b6e2954SConrad Meyer 14196b6e2954SConrad Meyer conf->kda_encryptedkey = encryptedkey; 1420e5054602SMark Johnston } 1421e5054602SMark Johnston 14229f78e2b8SMark Johnston memset(&dumper, 0, sizeof(dumper)); 1423e5054602SMark Johnston dumper.dumper_start = netdump_start; 1424e5054602SMark Johnston dumper.dumper_hdr = netdump_write_headers; 1425e5054602SMark Johnston dumper.dumper = netdump_dumper; 1426e5054602SMark Johnston dumper.priv = NULL; 1427e5054602SMark Johnston dumper.blocksize = NETDUMP_DATASIZE; 1428e5054602SMark Johnston dumper.maxiosize = MAXDUMPPGS * PAGE_SIZE; 1429e5054602SMark Johnston dumper.mediaoffset = 0; 1430e5054602SMark Johnston dumper.mediasize = 0; 1431e5054602SMark Johnston 14326b6e2954SConrad Meyer error = dumper_insert(&dumper, conf->kda_iface, conf); 1433e5054602SMark Johnston if (encryptedkey != NULL) { 14346b6e2954SConrad Meyer explicit_bzero(encryptedkey, 14356b6e2954SConrad Meyer conf->kda_encryptedkeysize); 1436e5054602SMark Johnston free(encryptedkey, M_TEMP); 1437e5054602SMark Johnston } 143864e7d18fSConrad Meyer if (error != 0) 143964e7d18fSConrad Meyer netdump_unconfigure(); 1440e5054602SMark Johnston break; 1441e5054602SMark Johnston default: 14426b6e2954SConrad Meyer error = ENOTTY; 1443e5054602SMark Johnston break; 1444e5054602SMark Johnston } 14456b6e2954SConrad Meyer explicit_bzero(&kda_copy, sizeof(kda_copy)); 14466b6e2954SConrad Meyer if (conf != NULL) 14476b6e2954SConrad Meyer explicit_bzero(conf, sizeof(*conf)); 144864e7d18fSConrad Meyer NETDUMP_WUNLOCK(); 1449e5054602SMark Johnston return (error); 1450e5054602SMark Johnston } 1451e5054602SMark Johnston 1452e5054602SMark Johnston /* 1453e5054602SMark Johnston * Called upon system init or kld load. Initializes the netdump parameters to 1454e5054602SMark Johnston * sane defaults (locates the first available NIC and uses the first IPv4 IP on 1455e5054602SMark Johnston * that card as the client IP). Leaves the server IP unconfigured. 1456e5054602SMark Johnston * 1457e5054602SMark Johnston * Parameters: 1458e5054602SMark Johnston * mod, Unused. 1459e5054602SMark Johnston * what, The module event type. 1460e5054602SMark Johnston * priv, Unused. 1461e5054602SMark Johnston * 1462e5054602SMark Johnston * Returns: 1463e5054602SMark Johnston * int, An errno value if an error occured, 0 otherwise. 1464e5054602SMark Johnston */ 1465e5054602SMark Johnston static int 1466e5054602SMark Johnston netdump_modevent(module_t mod __unused, int what, void *priv __unused) 1467e5054602SMark Johnston { 14686b6e2954SConrad Meyer struct diocskerneldump_arg conf; 1469e5054602SMark Johnston char *arg; 1470e5054602SMark Johnston int error; 1471e5054602SMark Johnston 1472e5054602SMark Johnston error = 0; 1473e5054602SMark Johnston switch (what) { 1474e5054602SMark Johnston case MOD_LOAD: 1475e5054602SMark Johnston error = make_dev_p(MAKEDEV_WAITOK, &netdump_cdev, 1476e5054602SMark Johnston &netdump_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "netdump"); 1477e5054602SMark Johnston if (error != 0) 1478e5054602SMark Johnston return (error); 1479e5054602SMark Johnston 148064e7d18fSConrad Meyer nd_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event, 148164e7d18fSConrad Meyer netdump_ifdetach, NULL, EVENTHANDLER_PRI_ANY); 148264e7d18fSConrad Meyer 1483e5054602SMark Johnston if ((arg = kern_getenv("net.dump.iface")) != NULL) { 14846b6e2954SConrad Meyer strlcpy(conf.kda_iface, arg, sizeof(conf.kda_iface)); 1485e5054602SMark Johnston freeenv(arg); 1486e5054602SMark Johnston 1487e5054602SMark Johnston if ((arg = kern_getenv("net.dump.server")) != NULL) { 14886b6e2954SConrad Meyer inet_aton(arg, &conf.kda_server.in4); 1489e5054602SMark Johnston freeenv(arg); 1490e5054602SMark Johnston } 1491e5054602SMark Johnston if ((arg = kern_getenv("net.dump.client")) != NULL) { 1492070e7bf9SConrad Meyer inet_aton(arg, &conf.kda_client.in4); 1493e5054602SMark Johnston freeenv(arg); 1494e5054602SMark Johnston } 1495e5054602SMark Johnston if ((arg = kern_getenv("net.dump.gateway")) != NULL) { 1496070e7bf9SConrad Meyer inet_aton(arg, &conf.kda_gateway.in4); 1497e5054602SMark Johnston freeenv(arg); 1498e5054602SMark Johnston } 14996b6e2954SConrad Meyer conf.kda_af = AF_INET; 1500e5054602SMark Johnston 1501e5054602SMark Johnston /* Ignore errors; we print a message to the console. */ 150264e7d18fSConrad Meyer NETDUMP_WLOCK(); 1503b35822d9SMark Johnston (void)netdump_configure(&conf, curthread); 150464e7d18fSConrad Meyer NETDUMP_WUNLOCK(); 1505e5054602SMark Johnston } 1506e5054602SMark Johnston break; 1507e5054602SMark Johnston case MOD_UNLOAD: 150864e7d18fSConrad Meyer NETDUMP_WLOCK(); 150964e7d18fSConrad Meyer if (netdump_enabled()) { 1510e5054602SMark Johnston printf("netdump: disabling dump device for unload\n"); 151164e7d18fSConrad Meyer netdump_unconfigure(); 1512e5054602SMark Johnston } 151364e7d18fSConrad Meyer NETDUMP_WUNLOCK(); 15146b6e2954SConrad Meyer destroy_dev(netdump_cdev); 151564e7d18fSConrad Meyer EVENTHANDLER_DEREGISTER(ifnet_departure_event, 151664e7d18fSConrad Meyer nd_detach_cookie); 1517e5054602SMark Johnston break; 1518e5054602SMark Johnston default: 1519e5054602SMark Johnston error = EOPNOTSUPP; 1520e5054602SMark Johnston break; 1521e5054602SMark Johnston } 1522e5054602SMark Johnston return (error); 1523e5054602SMark Johnston } 1524e5054602SMark Johnston 1525e5054602SMark Johnston static moduledata_t netdump_mod = { 1526e5054602SMark Johnston "netdump", 1527e5054602SMark Johnston netdump_modevent, 1528e5054602SMark Johnston NULL, 1529e5054602SMark Johnston }; 1530e5054602SMark Johnston 1531e5054602SMark Johnston MODULE_VERSION(netdump, 1); 1532e5054602SMark Johnston DECLARE_MODULE(netdump, netdump_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 1533