dda17b3672
NetGDB(4) is a component of a system using a panic-time network stack to remotely debug crashed FreeBSD kernels over the network, instead of traditional serial interfaces. There are three pieces in the complete NetGDB system. First, a dedicated proxy server must be running to accept connections from both NetGDB and gdb(1), and pass bidirectional traffic between the two protocols. Second, the NetGDB client is activated much like ordinary 'gdb' and similarly to 'netdump' in ddb(4) after a panic. Like other debugnet(4) clients (netdump(4)), the network interface on the route to the proxy server must be online and support debugnet(4). Finally, the remote (k)gdb(1) uses 'target remote <proxy>:<port>' (like any other TCP remote) to connect to the proxy server. The NetGDB v1 protocol speaks the literal GDB remote serial protocol, and uses a 1:1 relationship between GDB packets and sequences of debugnet packets (fragmented by MTU). There is no encryption utilized to keep debugging sessions private, so this is only appropriate for local segments or trusted networks. Submitted by: John Reimer <john.reimer AT emc.com> (earlier version) Discussed some with: emaste, markj Relnotes: sure Differential Revision: https://reviews.freebsd.org/D21568
276 lines
8.5 KiB
C
276 lines
8.5 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (c) 2019 Isilon Systems, LLC.
|
|
* Copyright (c) 2005-2014 Sandvine Incorporated
|
|
* Copyright (c) 2000 Darrell Anderson <anderson@cs.duke.edu>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/*
|
|
* Debugnet provides a reliable, bidirectional, UDP-encapsulated datagram
|
|
* transport while a machine is in a debug state. (N-1 CPUs stopped,
|
|
* interrupts disabled, may or may not be in a panic(9) state.) Only one
|
|
* stream may be active at a time. A dedicated server must be running to
|
|
* accept connections.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <sys/types.h>
|
|
#include <netinet/in.h>
|
|
|
|
/*
|
|
* Debugnet protocol details.
|
|
*/
|
|
#define DEBUGNET_HERALD 1 /* Connection handshake. */
|
|
#define DEBUGNET_FINISHED 2 /* Close the connection. */
|
|
#define DEBUGNET_DATA 3 /* Contains data. */
|
|
|
|
struct debugnet_msg_hdr {
|
|
uint32_t mh_type; /* Debugnet message type. */
|
|
uint32_t mh_seqno; /* Match acks with msgs. */
|
|
uint64_t mh_offset; /* Offset in fragment. */
|
|
uint32_t mh_len; /* Attached data (bytes). */
|
|
uint32_t mh_aux2; /* Consumer-specific. */
|
|
} __packed;
|
|
|
|
struct debugnet_ack {
|
|
uint32_t da_seqno; /* Match acks with msgs. */
|
|
} __packed;
|
|
|
|
#define DEBUGNET_MAX_IN_FLIGHT 64
|
|
|
|
#ifdef _KERNEL
|
|
/*
|
|
* Hook API for network drivers.
|
|
*/
|
|
enum debugnet_ev {
|
|
DEBUGNET_START,
|
|
DEBUGNET_END,
|
|
};
|
|
|
|
struct ifnet;
|
|
struct mbuf;
|
|
typedef void debugnet_init_t(struct ifnet *, int *nrxr, int *ncl, int *clsize);
|
|
typedef void debugnet_event_t(struct ifnet *, enum debugnet_ev);
|
|
typedef int debugnet_transmit_t(struct ifnet *, struct mbuf *);
|
|
typedef int debugnet_poll_t(struct ifnet *, int);
|
|
|
|
struct debugnet_methods {
|
|
debugnet_init_t *dn_init;
|
|
debugnet_event_t *dn_event;
|
|
debugnet_transmit_t *dn_transmit;
|
|
debugnet_poll_t *dn_poll;
|
|
};
|
|
|
|
#define DEBUGNET_SUPPORTED_NIC(ifp) \
|
|
((ifp)->if_debugnet_methods != NULL && (ifp)->if_type == IFT_ETHER)
|
|
|
|
struct debugnet_pcb; /* opaque */
|
|
|
|
/*
|
|
* Debugnet consumer API.
|
|
*/
|
|
struct debugnet_conn_params {
|
|
struct ifnet *dc_ifp;
|
|
in_addr_t dc_client;
|
|
in_addr_t dc_server;
|
|
in_addr_t dc_gateway;
|
|
|
|
uint16_t dc_herald_port;
|
|
uint16_t dc_client_port;
|
|
|
|
const void *dc_herald_data;
|
|
uint32_t dc_herald_datalen;
|
|
|
|
/*
|
|
* Consistent with debugnet_send(), aux paramaters to debugnet
|
|
* functions are provided host-endian (but converted to
|
|
* network endian on the wire).
|
|
*/
|
|
uint32_t dc_herald_aux2;
|
|
uint64_t dc_herald_offset;
|
|
|
|
/*
|
|
* If NULL, debugnet is a unidirectional channel from panic machine to
|
|
* remote server (like netdump).
|
|
*
|
|
* If handler is non-NULL, packets received on the client port that are
|
|
* not just tx acks are forwarded to the provided handler.
|
|
*
|
|
* The mbuf chain will have all non-debugnet framing headers removed
|
|
* (ethernet, inet, udp). It will start with a debugnet_msg_hdr, of
|
|
* which the header is guaranteed to be contiguous. If m_pullup is
|
|
* used, the supplied in-out mbuf pointer should be updated
|
|
* appropriately.
|
|
*
|
|
* If the handler frees the mbuf chain, it should set the mbuf pointer
|
|
* to NULL. Otherwise, the debugnet input framework will free the
|
|
* chain.
|
|
*
|
|
* The handler should ACK receieved packets with debugnet_ack_output.
|
|
*/
|
|
void (*dc_rx_handler)(struct debugnet_pcb *, struct mbuf **);
|
|
};
|
|
|
|
/*
|
|
* Open a stream to the specified server's herald port.
|
|
*
|
|
* If all goes well, the server will send ACK from a different port to our ack
|
|
* port. This allows servers to somewhat gracefully handle multiple debugnet
|
|
* clients. (Clients are limited to single connections.)
|
|
*
|
|
* Returns zero on success, or errno.
|
|
*/
|
|
int debugnet_connect(const struct debugnet_conn_params *,
|
|
struct debugnet_pcb **pcb_out);
|
|
|
|
/*
|
|
* Free a debugnet stream that was previously successfully opened.
|
|
*
|
|
* No attempt is made to cleanly terminate communication with the remote
|
|
* server. Consumers should first send an empty DEBUGNET_FINISHED message, or
|
|
* otherwise let the remote know they are signing off.
|
|
*/
|
|
void debugnet_free(struct debugnet_pcb *);
|
|
|
|
/*
|
|
* Send a message, with common debugnet_msg_hdr header, to the connected remote
|
|
* server.
|
|
*
|
|
* - mhtype translates directly to mh_type (e.g., DEBUGNET_DATA, or some other
|
|
* protocol-specific type).
|
|
* - Data and datalen describe the attached data; datalen may be zero.
|
|
* - If auxdata is NULL, mh_offset's initial value and mh_aux2 will be zero.
|
|
* Otherwise, mh_offset's initial value will be auxdata->dp_offset_start and
|
|
* mh_aux2 will have the value of auxdata->dp_aux2.
|
|
*
|
|
* Returns zero on success, or an errno on failure.
|
|
*/
|
|
struct debugnet_proto_aux {
|
|
uint64_t dp_offset_start;
|
|
uint32_t dp_aux2;
|
|
};
|
|
int debugnet_send(struct debugnet_pcb *, uint32_t mhtype, const void *data,
|
|
uint32_t datalen, const struct debugnet_proto_aux *auxdata);
|
|
|
|
/*
|
|
* A simple wrapper around the above when no data or auxdata is needed.
|
|
*/
|
|
static inline int
|
|
debugnet_sendempty(struct debugnet_pcb *pcb, uint32_t mhtype)
|
|
{
|
|
return (debugnet_send(pcb, mhtype, NULL, 0, NULL));
|
|
}
|
|
|
|
/*
|
|
* Full-duplex RX should ACK received messages.
|
|
*/
|
|
int debugnet_ack_output(struct debugnet_pcb *, uint32_t seqno /*net endian*/);
|
|
|
|
/*
|
|
* Check and/or wait for further packets.
|
|
*/
|
|
void debugnet_network_poll(struct debugnet_pcb *);
|
|
|
|
/*
|
|
* PCB accessors.
|
|
*/
|
|
|
|
/*
|
|
* Get the 48-bit MAC address of the discovered next hop (gateway, or
|
|
* destination server if it is on the same segment.
|
|
*/
|
|
const unsigned char *debugnet_get_gw_mac(const struct debugnet_pcb *);
|
|
|
|
/*
|
|
* Callbacks from core mbuf code.
|
|
*/
|
|
void debugnet_any_ifnet_update(struct ifnet *);
|
|
|
|
/*
|
|
* DDB parsing helper for common debugnet options.
|
|
*
|
|
* -s <server> [-g <gateway -c <localip> -i <interface>]
|
|
*
|
|
* Order is not significant. Interface is an online interface that supports
|
|
* debugnet and can route to the debugnet server. The other parameters are all
|
|
* IP addresses. Only the server parameter is required. The others are
|
|
* inferred automatically from the routing table, if not explicitly provided.
|
|
*
|
|
* Provides basic '-h' using provided 'cmd' string.
|
|
*
|
|
* Returns zero on success, or errno.
|
|
*/
|
|
struct debugnet_ddb_config {
|
|
struct ifnet *dd_ifp; /* not ref'd */
|
|
in_addr_t dd_client;
|
|
in_addr_t dd_server;
|
|
in_addr_t dd_gateway;
|
|
bool dd_has_client : 1;
|
|
bool dd_has_gateway : 1;
|
|
};
|
|
int debugnet_parse_ddb_cmd(const char *cmd,
|
|
struct debugnet_ddb_config *result);
|
|
|
|
/* Expose sysctl variables for netdump(4) to alias. */
|
|
extern int debugnet_npolls;
|
|
extern int debugnet_nretries;
|
|
extern int debugnet_arp_nretries;
|
|
|
|
/*
|
|
* Conditionally-defined macros for device drivers so we can avoid ifdef
|
|
* wrappers in every single implementation.
|
|
*/
|
|
#ifdef DEBUGNET
|
|
#define DEBUGNET_DEFINE(driver) \
|
|
static debugnet_init_t driver##_debugnet_init; \
|
|
static debugnet_event_t driver##_debugnet_event; \
|
|
static debugnet_transmit_t driver##_debugnet_transmit; \
|
|
static debugnet_poll_t driver##_debugnet_poll; \
|
|
\
|
|
static struct debugnet_methods driver##_debugnet_methods = { \
|
|
.dn_init = driver##_debugnet_init, \
|
|
.dn_event = driver##_debugnet_event, \
|
|
.dn_transmit = driver##_debugnet_transmit, \
|
|
.dn_poll = driver##_debugnet_poll, \
|
|
}
|
|
|
|
#define DEBUGNET_NOTIFY_MTU(ifp) debugnet_any_ifnet_update(ifp)
|
|
|
|
#define DEBUGNET_SET(ifp, driver) \
|
|
(ifp)->if_debugnet_methods = &driver##_debugnet_methods
|
|
|
|
#else /* !DEBUGNET || !INET */
|
|
|
|
#define DEBUGNET_DEFINE(driver)
|
|
#define DEBUGNET_NOTIFY_MTU(ifp)
|
|
#define DEBUGNET_SET(ifp, driver)
|
|
|
|
#endif /* DEBUGNET && INET */
|
|
#endif /* _KERNEL */
|