Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/gdb/netgdb.c
34820 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2019 Isilon Systems, LLC.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
/*
29
* netgdb.c
30
* FreeBSD subsystem supporting debugging the FreeBSD kernel over the network.
31
*
32
* There are three pieces necessary to use NetGDB.
33
*
34
* First, a dedicated proxy server must be running to accept connections from
35
* both NetGDB and gdb(1), and pass bidirectional traffic between the two
36
* protocols.
37
*
38
* Second, The NetGDB client is activated much like ordinary 'gdb' and
39
* similarly to 'netdump' in ddb(4). Like other debugnet(4) clients
40
* (netdump(4)), the network interface on the route to the proxy server must be
41
* online and support debugnet(4).
42
*
43
* Finally, the remote (k)gdb(1) uses 'target remote <proxy>:<port>' to connect
44
* to the proxy server.
45
*
46
* NetGDBv1 speaks the literal GDB remote serial protocol, and uses a 1:1
47
* relationship between GDB packets and plain debugnet packets. There is no
48
* encryption utilized to keep debugging sessions private, so this is only
49
* appropriate for local segments or trusted networks.
50
*/
51
52
#include <sys/cdefs.h>
53
#include "opt_ddb.h"
54
#ifndef DDB
55
#error "NetGDB cannot be used without DDB at this time"
56
#endif
57
58
#include <sys/errno.h>
59
#include <sys/param.h>
60
#include <sys/kdb.h>
61
#include <sys/sbuf.h>
62
#include <sys/socket.h>
63
#include <sys/sysctl.h>
64
#include <sys/ttydefaults.h>
65
66
#include <machine/gdb_machdep.h>
67
68
#ifdef DDB
69
#include <ddb/ddb.h>
70
#include <ddb/db_command.h>
71
#include <ddb/db_lex.h>
72
#endif
73
74
#include <net/debugnet.h>
75
#include <net/if.h>
76
#include <net/if_var.h>
77
#include <net/route.h>
78
79
#include <gdb/gdb.h>
80
#include <gdb/gdb_int.h>
81
#include <gdb/netgdb.h>
82
83
FEATURE(netgdb, "NetGDB support");
84
SYSCTL_NODE(_debug_gdb, OID_AUTO, netgdb, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
85
"NetGDB parameters");
86
87
static unsigned netgdb_debug;
88
SYSCTL_UINT(_debug_gdb_netgdb, OID_AUTO, debug, CTLFLAG_RWTUN,
89
&netgdb_debug, 0,
90
"Debug message verbosity (0: off; 1: on)");
91
92
#define NETGDB_DEBUG(f, ...) do { \
93
if (netgdb_debug > 0) \
94
printf(("%s [%s:%d]: " f), __func__, __FILE__, __LINE__, ## \
95
__VA_ARGS__); \
96
} while (false)
97
98
static void netgdb_fini(void);
99
100
/* Runtime state. */
101
static char netgdb_rxbuf[GDB_BUFSZ + 16]; /* Some overhead for framing. */
102
static struct sbuf netgdb_rxsb;
103
static ssize_t netgdb_rx_off;
104
105
static struct debugnet_pcb *netgdb_conn;
106
static struct gdb_dbgport *netgdb_prev_dbgport;
107
static int *netgdb_prev_kdb_inactive;
108
109
/* TODO(CEM) disable ack mode */
110
111
/*
112
* Attempt to accept the incoming packet. If we run into ENOBUFS or another
113
* error, return it.
114
*
115
* The mbuf chain will have all framing headers removed (ethernet, inet, udp,
116
* debugnet).
117
*/
118
static int
119
netgdb_rx(struct mbuf *m)
120
{
121
uint32_t rlen, count;
122
123
rlen = m->m_pkthdr.len;
124
#define _SBUF_FREESPACE(s) ((s)->s_size - ((s)->s_len + 1))
125
if (_SBUF_FREESPACE(&netgdb_rxsb) < rlen) {
126
NETGDB_DEBUG("Backpressure: Not ACKing RX of packet that "
127
"would overflow our buffer (%zd/%zd used).\n",
128
netgdb_rxsb.s_len, netgdb_rxsb.s_size);
129
return (ENOBUFS);
130
}
131
#undef _SBUF_FREESPACE
132
133
/*
134
* Inlined m_apply -- why isn't there a macro or inline function
135
* version?
136
*/
137
while (m != NULL && m->m_len == 0)
138
m = m->m_next;
139
while (rlen > 0) {
140
MPASS(m != NULL && m->m_len >= 0);
141
count = min((uint32_t)m->m_len, rlen);
142
(void)sbuf_bcat(&netgdb_rxsb, mtod(m, const void *), count);
143
rlen -= count;
144
m = m->m_next;
145
}
146
return (0);
147
}
148
149
static void
150
netgdb_finish(void)
151
{
152
sbuf_putc(&netgdb_rxsb, CTRL('C'));
153
}
154
155
/*
156
* The following routines implement a pseudo GDB debugport (an emulated serial
157
* driver that the MI gdb(4) code does I/O with).
158
*/
159
160
static int
161
netgdb_dbg_getc(void)
162
{
163
int c;
164
165
while (true) {
166
/* Pull bytes off any currently cached packet first. */
167
if (netgdb_rx_off < sbuf_len(&netgdb_rxsb)) {
168
c = netgdb_rxsb.s_buf[netgdb_rx_off];
169
netgdb_rx_off++;
170
break;
171
}
172
173
/* Reached EOF? Reuse buffer. */
174
sbuf_clear(&netgdb_rxsb);
175
netgdb_rx_off = 0;
176
177
/* Check for CTRL-C on console/serial, if any. */
178
if (netgdb_prev_dbgport != NULL) {
179
c = netgdb_prev_dbgport->gdb_getc();
180
if (c == CTRL('C'))
181
break;
182
}
183
184
debugnet_network_poll(netgdb_conn);
185
}
186
187
if (c == CTRL('C')) {
188
netgdb_fini();
189
/* Caller gdb_getc() will print that we got ^C. */
190
}
191
return (c);
192
}
193
194
static void
195
netgdb_dbg_sendpacket(const void *buf, size_t len)
196
{
197
struct debugnet_proto_aux aux;
198
int error;
199
200
MPASS(len <= UINT32_MAX);
201
202
/*
203
* GDB packet boundaries matter. debugnet_send() fragments a single
204
* request into many sequential debugnet messages. Mark full packet
205
* length and offset for potential reassembly by the proxy.
206
*/
207
aux = (struct debugnet_proto_aux) {
208
.dp_aux2 = len,
209
};
210
211
error = debugnet_send(netgdb_conn, DEBUGNET_DATA, buf, len, &aux);
212
if (error != 0) {
213
printf("%s: Network error: %d; trying to switch back to ddb.\n",
214
__func__, error);
215
netgdb_fini();
216
217
if (kdb_dbbe_select("ddb") != 0)
218
printf("The ddb backend could not be selected.\n");
219
else {
220
printf("using longjmp, hope it works!\n");
221
kdb_reenter();
222
}
223
}
224
225
}
226
227
/* Just used for + / - GDB-level ACKs. */
228
static void
229
netgdb_dbg_putc(int i)
230
{
231
char c;
232
233
c = i;
234
netgdb_dbg_sendpacket(&c, 1);
235
236
}
237
238
static struct gdb_dbgport netgdb_gdb_dbgport = {
239
.gdb_name = "netgdb",
240
.gdb_getc = netgdb_dbg_getc,
241
.gdb_putc = netgdb_dbg_putc,
242
.gdb_term = netgdb_fini,
243
.gdb_sendpacket = netgdb_dbg_sendpacket,
244
.gdb_dbfeatures = GDB_DBGP_FEAT_WANTTERM | GDB_DBGP_FEAT_RELIABLE,
245
};
246
247
static void
248
netgdb_init(void)
249
{
250
struct kdb_dbbe *be, **iter;
251
252
/*
253
* Force enable GDB. (If no other debugports were registered at boot,
254
* KDB thinks it doesn't exist.)
255
*/
256
SET_FOREACH(iter, kdb_dbbe_set) {
257
be = *iter;
258
if (strcmp(be->dbbe_name, "gdb") != 0)
259
continue;
260
if (be->dbbe_active == -1) {
261
netgdb_prev_kdb_inactive = &be->dbbe_active;
262
be->dbbe_active = 0;
263
}
264
break;
265
}
266
267
/* Force netgdb debugport. */
268
netgdb_prev_dbgport = gdb_cur;
269
gdb_cur = &netgdb_gdb_dbgport;
270
271
sbuf_new(&netgdb_rxsb, netgdb_rxbuf, sizeof(netgdb_rxbuf),
272
SBUF_FIXEDLEN);
273
netgdb_rx_off = 0;
274
}
275
276
static void
277
netgdb_fini(void)
278
{
279
280
/* TODO: tear down conn gracefully? */
281
if (netgdb_conn != NULL) {
282
debugnet_free(netgdb_conn);
283
netgdb_conn = NULL;
284
}
285
286
sbuf_delete(&netgdb_rxsb);
287
288
gdb_cur = netgdb_prev_dbgport;
289
290
if (netgdb_prev_kdb_inactive != NULL) {
291
*netgdb_prev_kdb_inactive = -1;
292
netgdb_prev_kdb_inactive = NULL;
293
}
294
}
295
296
#ifdef DDB
297
/*
298
* Usage: netgdb -s <server> [-g <gateway -c <localip> -i <interface>]
299
*
300
* Order is not significant.
301
*
302
* Currently, this command does not support configuring encryption or
303
* compression.
304
*/
305
DB_COMMAND_FLAGS(netgdb, db_netgdb_cmd, CS_OWN)
306
{
307
struct debugnet_ddb_config params;
308
struct debugnet_conn_params dcp;
309
struct debugnet_pcb *pcb;
310
char proxy_buf[INET_ADDRSTRLEN];
311
int error;
312
313
if (!KERNEL_PANICKED()) {
314
/* TODO: This limitation should be removed in future work. */
315
printf("%s: netgdb is currently limited to use only after a "
316
"panic. Sorry.\n", __func__);
317
return;
318
}
319
320
error = debugnet_parse_ddb_cmd("netgdb", &params);
321
if (error != 0) {
322
db_printf("Error configuring netgdb: %d\n", error);
323
return;
324
}
325
326
/*
327
* Must initialize netgdb_rxsb before debugnet_connect(), because we
328
* might be getting rx handler callbacks from the send->poll path
329
* during debugnet_connect().
330
*/
331
netgdb_init();
332
333
if (!params.dd_has_client)
334
params.dd_client = INADDR_ANY;
335
if (!params.dd_has_gateway)
336
params.dd_gateway = INADDR_ANY;
337
338
dcp = (struct debugnet_conn_params) {
339
.dc_ifp = params.dd_ifp,
340
.dc_client = params.dd_client,
341
.dc_server = params.dd_server,
342
.dc_gateway = params.dd_gateway,
343
.dc_herald_port = NETGDB_HERALDPORT,
344
.dc_client_port = NETGDB_CLIENTPORT,
345
.dc_herald_aux2 = NETGDB_PROTO_V1,
346
.dc_rx_handler = netgdb_rx,
347
.dc_finish_handler = netgdb_finish,
348
};
349
350
error = debugnet_connect(&dcp, &pcb);
351
if (error != 0) {
352
printf("failed to contact netgdb server: %d\n", error);
353
netgdb_fini();
354
return;
355
}
356
357
netgdb_conn = pcb;
358
359
if (kdb_dbbe_select("gdb") != 0) {
360
db_printf("The remote GDB backend could not be selected.\n");
361
netgdb_fini();
362
return;
363
}
364
365
/*
366
* Mark that we are done in ddb(4). Return -> kdb_trap() should
367
* re-enter with the new backend.
368
*/
369
db_cmd_loop_done = 1;
370
gdb_return_to_ddb = true;
371
db_printf("(detaching GDB will return control to DDB)\n");
372
373
const in_addr_t *proxy_addr = debugnet_get_server_addr(netgdb_conn);
374
const uint16_t proxy_port = debugnet_get_server_port(netgdb_conn) + 1;
375
inet_ntop(AF_INET, proxy_addr, proxy_buf, sizeof(proxy_buf));
376
if (inet_ntop(AF_INET, proxy_addr, proxy_buf, sizeof(proxy_buf)) == NULL) {
377
db_printf("Connected to proxy. "
378
"Use target remote <proxy address>:%hu to begin debugging.\n",
379
proxy_port);
380
} else {
381
db_printf("Connected to proxy. "
382
"Use target remote %s:%hu to begin debugging.\n",
383
proxy_buf, proxy_port);
384
}
385
#if 0
386
/* Aspirational, but does not work reliably. */
387
db_printf("(ctrl-c will return control to ddb)\n");
388
#endif
389
}
390
#endif /* DDB */
391
392