The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/gdb/netgdb.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: acb7352a6e1f56b748460364cbe6d509


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.