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