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 __FBSDID("$FreeBSD$"); 54dda17b36SConrad Meyer 55dda17b36SConrad Meyer #include "opt_ddb.h" 56dda17b36SConrad Meyer #ifndef DDB 57dda17b36SConrad Meyer #error "NetGDB cannot be used without DDB at this time" 58dda17b36SConrad Meyer #endif 59dda17b36SConrad Meyer 60*b498331bSJohn Reimer #include <sys/errno.h> 61dda17b36SConrad Meyer #include <sys/param.h> 62dda17b36SConrad Meyer #include <sys/kdb.h> 63dda17b36SConrad Meyer #include <sys/sbuf.h> 64dda17b36SConrad Meyer #include <sys/socket.h> 65dda17b36SConrad Meyer #include <sys/sysctl.h> 66dda17b36SConrad Meyer #include <sys/ttydefaults.h> 67dda17b36SConrad Meyer 68dda17b36SConrad Meyer #include <machine/gdb_machdep.h> 69dda17b36SConrad Meyer 70dda17b36SConrad Meyer #ifdef DDB 71dda17b36SConrad Meyer #include <ddb/ddb.h> 72dda17b36SConrad Meyer #include <ddb/db_command.h> 73dda17b36SConrad Meyer #include <ddb/db_lex.h> 74dda17b36SConrad Meyer #endif 75dda17b36SConrad Meyer 76dda17b36SConrad Meyer #include <net/debugnet.h> 77dda17b36SConrad Meyer #include <net/if.h> 78dda17b36SConrad Meyer #include <net/if_var.h> 79dda17b36SConrad Meyer #include <net/route.h> 80dda17b36SConrad Meyer 81dda17b36SConrad Meyer #include <gdb/gdb.h> 82dda17b36SConrad Meyer #include <gdb/gdb_int.h> 83dda17b36SConrad Meyer #include <gdb/netgdb.h> 84dda17b36SConrad Meyer 85dda17b36SConrad Meyer FEATURE(netgdb, "NetGDB support"); 867029da5cSPawel Biernacki SYSCTL_NODE(_debug_gdb, OID_AUTO, netgdb, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 87dda17b36SConrad Meyer "NetGDB parameters"); 88dda17b36SConrad Meyer 89dda17b36SConrad Meyer static unsigned netgdb_debug; 90dda17b36SConrad Meyer SYSCTL_UINT(_debug_gdb_netgdb, OID_AUTO, debug, CTLFLAG_RWTUN, 91dda17b36SConrad Meyer &netgdb_debug, 0, 92dda17b36SConrad Meyer "Debug message verbosity (0: off; 1: on)"); 93dda17b36SConrad Meyer 94dda17b36SConrad Meyer #define NETGDB_DEBUG(f, ...) do { \ 95dda17b36SConrad Meyer if (netgdb_debug > 0) \ 96dda17b36SConrad Meyer printf(("%s [%s:%d]: " f), __func__, __FILE__, __LINE__, ## \ 97dda17b36SConrad Meyer __VA_ARGS__); \ 98dda17b36SConrad Meyer } while (false) 99dda17b36SConrad Meyer 100dda17b36SConrad Meyer static void netgdb_fini(void); 101dda17b36SConrad Meyer 102dda17b36SConrad Meyer /* Runtime state. */ 103dda17b36SConrad Meyer static char netgdb_rxbuf[GDB_BUFSZ + 16]; /* Some overhead for framing. */ 104dda17b36SConrad Meyer static struct sbuf netgdb_rxsb; 105dda17b36SConrad Meyer static ssize_t netgdb_rx_off; 106dda17b36SConrad Meyer 107dda17b36SConrad Meyer static struct debugnet_pcb *netgdb_conn; 108dda17b36SConrad Meyer static struct gdb_dbgport *netgdb_prev_dbgport; 109dda17b36SConrad Meyer static int *netgdb_prev_kdb_inactive; 110dda17b36SConrad Meyer 111dda17b36SConrad Meyer /* TODO(CEM) disable ack mode */ 112dda17b36SConrad Meyer 113dda17b36SConrad Meyer /* 114*b498331bSJohn Reimer * Attempt to accept the incoming packet. If we run into ENOBUFS or another 115*b498331bSJohn Reimer * error, return it. 116dda17b36SConrad Meyer * 117*b498331bSJohn Reimer * The mbuf chain will have all framing headers removed (ethernet, inet, udp, 118*b498331bSJohn Reimer * debugnet). 119dda17b36SConrad Meyer */ 120*b498331bSJohn Reimer static int 121*b498331bSJohn Reimer netgdb_rx(struct mbuf *m) 122dda17b36SConrad Meyer { 123dda17b36SConrad Meyer uint32_t rlen, count; 124dda17b36SConrad Meyer 125*b498331bSJohn Reimer rlen = m->m_pkthdr.len; 126dda17b36SConrad Meyer #define _SBUF_FREESPACE(s) ((s)->s_size - ((s)->s_len + 1)) 127dda17b36SConrad Meyer if (_SBUF_FREESPACE(&netgdb_rxsb) < rlen) { 128dda17b36SConrad Meyer NETGDB_DEBUG("Backpressure: Not ACKing RX of packet that " 129dda17b36SConrad Meyer "would overflow our buffer (%zd/%zd used).\n", 130dda17b36SConrad Meyer netgdb_rxsb.s_len, netgdb_rxsb.s_size); 131*b498331bSJohn Reimer return (ENOBUFS); 132dda17b36SConrad Meyer } 133dda17b36SConrad Meyer #undef _SBUF_FREESPACE 134dda17b36SConrad Meyer 135dda17b36SConrad Meyer /* 136dda17b36SConrad Meyer * Inlined m_apply -- why isn't there a macro or inline function 137dda17b36SConrad Meyer * version? 138dda17b36SConrad Meyer */ 139dda17b36SConrad Meyer while (m != NULL && m->m_len == 0) 140dda17b36SConrad Meyer m = m->m_next; 141dda17b36SConrad Meyer while (rlen > 0) { 142dda17b36SConrad Meyer MPASS(m != NULL && m->m_len >= 0); 143dda17b36SConrad Meyer count = min((uint32_t)m->m_len, rlen); 144dda17b36SConrad Meyer (void)sbuf_bcat(&netgdb_rxsb, mtod(m, const void *), count); 145dda17b36SConrad Meyer rlen -= count; 146dda17b36SConrad Meyer m = m->m_next; 147dda17b36SConrad Meyer } 148*b498331bSJohn Reimer return (0); 149*b498331bSJohn Reimer } 150*b498331bSJohn Reimer 151*b498331bSJohn Reimer static void 152*b498331bSJohn Reimer netgdb_finish(void) 153*b498331bSJohn Reimer { 154*b498331bSJohn Reimer sbuf_putc(&netgdb_rxsb, CTRL('C')); 155dda17b36SConrad Meyer } 156dda17b36SConrad Meyer 157dda17b36SConrad Meyer /* 158dda17b36SConrad Meyer * The following routines implement a pseudo GDB debugport (an emulated serial 159dda17b36SConrad Meyer * driver that the MI gdb(4) code does I/O with). 160dda17b36SConrad Meyer */ 161dda17b36SConrad Meyer 162dda17b36SConrad Meyer static int 163dda17b36SConrad Meyer netgdb_dbg_getc(void) 164dda17b36SConrad Meyer { 165dda17b36SConrad Meyer int c; 166dda17b36SConrad Meyer 167dda17b36SConrad Meyer while (true) { 168dda17b36SConrad Meyer /* Pull bytes off any currently cached packet first. */ 169dda17b36SConrad Meyer if (netgdb_rx_off < sbuf_len(&netgdb_rxsb)) { 170dda17b36SConrad Meyer c = netgdb_rxsb.s_buf[netgdb_rx_off]; 171dda17b36SConrad Meyer netgdb_rx_off++; 172dda17b36SConrad Meyer break; 173dda17b36SConrad Meyer } 174dda17b36SConrad Meyer 175dda17b36SConrad Meyer /* Reached EOF? Reuse buffer. */ 176dda17b36SConrad Meyer sbuf_clear(&netgdb_rxsb); 177dda17b36SConrad Meyer netgdb_rx_off = 0; 178dda17b36SConrad Meyer 179dda17b36SConrad Meyer /* Check for CTRL-C on console/serial, if any. */ 180dda17b36SConrad Meyer if (netgdb_prev_dbgport != NULL) { 181dda17b36SConrad Meyer c = netgdb_prev_dbgport->gdb_getc(); 182dda17b36SConrad Meyer if (c == CTRL('C')) 183dda17b36SConrad Meyer break; 184dda17b36SConrad Meyer } 185dda17b36SConrad Meyer 186dda17b36SConrad Meyer debugnet_network_poll(netgdb_conn); 187dda17b36SConrad Meyer } 188dda17b36SConrad Meyer 189dda17b36SConrad Meyer if (c == CTRL('C')) { 190dda17b36SConrad Meyer netgdb_fini(); 191dda17b36SConrad Meyer /* Caller gdb_getc() will print that we got ^C. */ 192dda17b36SConrad Meyer } 193dda17b36SConrad Meyer return (c); 194dda17b36SConrad Meyer } 195dda17b36SConrad Meyer 196dda17b36SConrad Meyer static void 197dda17b36SConrad Meyer netgdb_dbg_sendpacket(const void *buf, size_t len) 198dda17b36SConrad Meyer { 199dda17b36SConrad Meyer struct debugnet_proto_aux aux; 200dda17b36SConrad Meyer int error; 201dda17b36SConrad Meyer 202dda17b36SConrad Meyer MPASS(len <= UINT32_MAX); 203dda17b36SConrad Meyer 204dda17b36SConrad Meyer /* 205dda17b36SConrad Meyer * GDB packet boundaries matter. debugnet_send() fragments a single 206dda17b36SConrad Meyer * request into many sequential debugnet messages. Mark full packet 207dda17b36SConrad Meyer * length and offset for potential reassembly by the proxy. 208dda17b36SConrad Meyer */ 209dda17b36SConrad Meyer aux = (struct debugnet_proto_aux) { 210dda17b36SConrad Meyer .dp_aux2 = len, 211dda17b36SConrad Meyer }; 212dda17b36SConrad Meyer 213dda17b36SConrad Meyer error = debugnet_send(netgdb_conn, DEBUGNET_DATA, buf, len, &aux); 214dda17b36SConrad Meyer if (error != 0) { 215dda17b36SConrad Meyer printf("%s: Network error: %d; trying to switch back to ddb.\n", 216dda17b36SConrad Meyer __func__, error); 217dda17b36SConrad Meyer netgdb_fini(); 218dda17b36SConrad Meyer 219dda17b36SConrad Meyer if (kdb_dbbe_select("ddb") != 0) 220dda17b36SConrad Meyer printf("The ddb backend could not be selected.\n"); 221dda17b36SConrad Meyer else { 222dda17b36SConrad Meyer printf("using longjmp, hope it works!\n"); 223dda17b36SConrad Meyer kdb_reenter(); 224dda17b36SConrad Meyer } 225dda17b36SConrad Meyer } 226dda17b36SConrad Meyer 227dda17b36SConrad Meyer } 228dda17b36SConrad Meyer 229dda17b36SConrad Meyer /* Just used for + / - GDB-level ACKs. */ 230dda17b36SConrad Meyer static void 231dda17b36SConrad Meyer netgdb_dbg_putc(int i) 232dda17b36SConrad Meyer { 233dda17b36SConrad Meyer char c; 234dda17b36SConrad Meyer 235dda17b36SConrad Meyer c = i; 236dda17b36SConrad Meyer netgdb_dbg_sendpacket(&c, 1); 237dda17b36SConrad Meyer 238dda17b36SConrad Meyer } 239dda17b36SConrad Meyer 240dda17b36SConrad Meyer static struct gdb_dbgport netgdb_gdb_dbgport = { 241dda17b36SConrad Meyer .gdb_name = "netgdb", 242dda17b36SConrad Meyer .gdb_getc = netgdb_dbg_getc, 243dda17b36SConrad Meyer .gdb_putc = netgdb_dbg_putc, 244dda17b36SConrad Meyer .gdb_term = netgdb_fini, 245dda17b36SConrad Meyer .gdb_sendpacket = netgdb_dbg_sendpacket, 2466310546dSConrad Meyer .gdb_dbfeatures = GDB_DBGP_FEAT_WANTTERM | GDB_DBGP_FEAT_RELIABLE, 247dda17b36SConrad Meyer }; 248dda17b36SConrad Meyer 249dda17b36SConrad Meyer static void 250dda17b36SConrad Meyer netgdb_init(void) 251dda17b36SConrad Meyer { 252dda17b36SConrad Meyer struct kdb_dbbe *be, **iter; 253dda17b36SConrad Meyer 254dda17b36SConrad Meyer /* 255dda17b36SConrad Meyer * Force enable GDB. (If no other debugports were registered at boot, 256dda17b36SConrad Meyer * KDB thinks it doesn't exist.) 257dda17b36SConrad Meyer */ 258dda17b36SConrad Meyer SET_FOREACH(iter, kdb_dbbe_set) { 259dda17b36SConrad Meyer be = *iter; 260dda17b36SConrad Meyer if (strcmp(be->dbbe_name, "gdb") != 0) 261dda17b36SConrad Meyer continue; 262dda17b36SConrad Meyer if (be->dbbe_active == -1) { 263dda17b36SConrad Meyer netgdb_prev_kdb_inactive = &be->dbbe_active; 264dda17b36SConrad Meyer be->dbbe_active = 0; 265dda17b36SConrad Meyer } 266dda17b36SConrad Meyer break; 267dda17b36SConrad Meyer } 268dda17b36SConrad Meyer 269dda17b36SConrad Meyer /* Force netgdb debugport. */ 270dda17b36SConrad Meyer netgdb_prev_dbgport = gdb_cur; 271dda17b36SConrad Meyer gdb_cur = &netgdb_gdb_dbgport; 272dda17b36SConrad Meyer 273dda17b36SConrad Meyer sbuf_new(&netgdb_rxsb, netgdb_rxbuf, sizeof(netgdb_rxbuf), 274dda17b36SConrad Meyer SBUF_FIXEDLEN); 275dda17b36SConrad Meyer netgdb_rx_off = 0; 276dda17b36SConrad Meyer } 277dda17b36SConrad Meyer 278dda17b36SConrad Meyer static void 279dda17b36SConrad Meyer netgdb_fini(void) 280dda17b36SConrad Meyer { 281dda17b36SConrad Meyer 282dda17b36SConrad Meyer /* TODO: tear down conn gracefully? */ 283dda17b36SConrad Meyer if (netgdb_conn != NULL) { 284dda17b36SConrad Meyer debugnet_free(netgdb_conn); 285dda17b36SConrad Meyer netgdb_conn = NULL; 286dda17b36SConrad Meyer } 287dda17b36SConrad Meyer 288dda17b36SConrad Meyer sbuf_delete(&netgdb_rxsb); 289dda17b36SConrad Meyer 290dda17b36SConrad Meyer gdb_cur = netgdb_prev_dbgport; 291dda17b36SConrad Meyer 292dda17b36SConrad Meyer if (netgdb_prev_kdb_inactive != NULL) { 293dda17b36SConrad Meyer *netgdb_prev_kdb_inactive = -1; 294dda17b36SConrad Meyer netgdb_prev_kdb_inactive = NULL; 295dda17b36SConrad Meyer } 296dda17b36SConrad Meyer } 297dda17b36SConrad Meyer 298dda17b36SConrad Meyer #ifdef DDB 299dda17b36SConrad Meyer /* 300dda17b36SConrad Meyer * Usage: netgdb -s <server> [-g <gateway -c <localip> -i <interface>] 301dda17b36SConrad Meyer * 302dda17b36SConrad Meyer * Order is not significant. 303dda17b36SConrad Meyer * 304dda17b36SConrad Meyer * Currently, this command does not support configuring encryption or 305dda17b36SConrad Meyer * compression. 306dda17b36SConrad Meyer */ 307258958b3SMitchell Horne DB_COMMAND_FLAGS(netgdb, db_netgdb_cmd, CS_OWN) 308dda17b36SConrad Meyer { 309dda17b36SConrad Meyer struct debugnet_ddb_config params; 310dda17b36SConrad Meyer struct debugnet_conn_params dcp; 311dda17b36SConrad Meyer struct debugnet_pcb *pcb; 312*b498331bSJohn Reimer char proxy_buf[INET_ADDRSTRLEN]; 313dda17b36SConrad Meyer int error; 314dda17b36SConrad Meyer 315879e0604SMateusz Guzik if (!KERNEL_PANICKED()) { 316dda17b36SConrad Meyer /* TODO: This limitation should be removed in future work. */ 317dda17b36SConrad Meyer printf("%s: netgdb is currently limited to use only after a " 318dda17b36SConrad Meyer "panic. Sorry.\n", __func__); 319dda17b36SConrad Meyer return; 320dda17b36SConrad Meyer } 321dda17b36SConrad Meyer 322dda17b36SConrad Meyer error = debugnet_parse_ddb_cmd("netgdb", ¶ms); 323dda17b36SConrad Meyer if (error != 0) { 324dda17b36SConrad Meyer db_printf("Error configuring netgdb: %d\n", error); 325dda17b36SConrad Meyer return; 326dda17b36SConrad Meyer } 327dda17b36SConrad Meyer 328dda17b36SConrad Meyer /* 329dda17b36SConrad Meyer * Must initialize netgdb_rxsb before debugnet_connect(), because we 330dda17b36SConrad Meyer * might be getting rx handler callbacks from the send->poll path 331dda17b36SConrad Meyer * during debugnet_connect(). 332dda17b36SConrad Meyer */ 333dda17b36SConrad Meyer netgdb_init(); 334dda17b36SConrad Meyer 335dda17b36SConrad Meyer if (!params.dd_has_client) 336dda17b36SConrad Meyer params.dd_client = INADDR_ANY; 337dda17b36SConrad Meyer if (!params.dd_has_gateway) 338dda17b36SConrad Meyer params.dd_gateway = INADDR_ANY; 339dda17b36SConrad Meyer 340dda17b36SConrad Meyer dcp = (struct debugnet_conn_params) { 341dda17b36SConrad Meyer .dc_ifp = params.dd_ifp, 342dda17b36SConrad Meyer .dc_client = params.dd_client, 343dda17b36SConrad Meyer .dc_server = params.dd_server, 344dda17b36SConrad Meyer .dc_gateway = params.dd_gateway, 345dda17b36SConrad Meyer .dc_herald_port = NETGDB_HERALDPORT, 346dda17b36SConrad Meyer .dc_client_port = NETGDB_CLIENTPORT, 347dda17b36SConrad Meyer .dc_herald_aux2 = NETGDB_PROTO_V1, 348dda17b36SConrad Meyer .dc_rx_handler = netgdb_rx, 349*b498331bSJohn Reimer .dc_finish_handler = netgdb_finish, 350dda17b36SConrad Meyer }; 351dda17b36SConrad Meyer 352dda17b36SConrad Meyer error = debugnet_connect(&dcp, &pcb); 353dda17b36SConrad Meyer if (error != 0) { 354dda17b36SConrad Meyer printf("failed to contact netgdb server: %d\n", error); 355dda17b36SConrad Meyer netgdb_fini(); 356dda17b36SConrad Meyer return; 357dda17b36SConrad Meyer } 358dda17b36SConrad Meyer 359dda17b36SConrad Meyer netgdb_conn = pcb; 360dda17b36SConrad Meyer 361dda17b36SConrad Meyer if (kdb_dbbe_select("gdb") != 0) { 362dda17b36SConrad Meyer db_printf("The remote GDB backend could not be selected.\n"); 363dda17b36SConrad Meyer netgdb_fini(); 364dda17b36SConrad Meyer return; 365dda17b36SConrad Meyer } 366dda17b36SConrad Meyer 367dda17b36SConrad Meyer /* 368dda17b36SConrad Meyer * Mark that we are done in ddb(4). Return -> kdb_trap() should 369dda17b36SConrad Meyer * re-enter with the new backend. 370dda17b36SConrad Meyer */ 371dda17b36SConrad Meyer db_cmd_loop_done = 1; 372dda17b36SConrad Meyer gdb_return_to_ddb = true; 373dda17b36SConrad Meyer db_printf("(detaching GDB will return control to DDB)\n"); 374*b498331bSJohn Reimer 375*b498331bSJohn Reimer const in_addr_t *proxy_addr = debugnet_get_server_addr(netgdb_conn); 376*b498331bSJohn Reimer const uint16_t proxy_port = debugnet_get_server_port(netgdb_conn) + 1; 377*b498331bSJohn Reimer inet_ntop(AF_INET, proxy_addr, proxy_buf, sizeof(proxy_buf)); 378*b498331bSJohn Reimer if (inet_ntop(AF_INET, proxy_addr, proxy_buf, sizeof(proxy_buf)) == NULL) { 379*b498331bSJohn Reimer db_printf("Connected to proxy. " 380*b498331bSJohn Reimer "Use target remote <proxy address>:%hu to begin debugging.\n", 381*b498331bSJohn Reimer proxy_port); 382*b498331bSJohn Reimer } else { 383*b498331bSJohn Reimer db_printf("Connected to proxy. " 384*b498331bSJohn Reimer "Use target remote %s:%hu to begin debugging.\n", 385*b498331bSJohn Reimer proxy_buf, proxy_port); 386*b498331bSJohn Reimer } 387dda17b36SConrad Meyer #if 0 388dda17b36SConrad Meyer /* Aspirational, but does not work reliably. */ 389dda17b36SConrad Meyer db_printf("(ctrl-c will return control to ddb)\n"); 390dda17b36SConrad Meyer #endif 391dda17b36SConrad Meyer } 392dda17b36SConrad Meyer #endif /* DDB */ 393