Author: cem
Date: Thu Oct 17 20:25:15 2019
New Revision: 353696
URL: https://svnweb.freebsd.org/changeset/base/353696

Log:
  debugnet(4): Add optional full-duplex mode
  
  It remains unattached to any client protocol.  Netdump is unaffected
  (remaining half-duplex).  The intended consumer is NetGDB.
  
  Submitted by: John Reimer <john.reimer AT emc.com> (earlier version)
  Discussed with:       markj
  Differential Revision:        https://reviews.freebsd.org/D21541

Modified:
  head/sys/net/debugnet.c
  head/sys/net/debugnet.h
  head/sys/net/debugnet_int.h
  head/sys/netinet/netdump/netdump_client.c

Modified: head/sys/net/debugnet.c
==============================================================================
--- head/sys/net/debugnet.c     Thu Oct 17 20:18:07 2019        (r353695)
+++ head/sys/net/debugnet.c     Thu Oct 17 20:25:15 2019        (r353696)
@@ -175,7 +175,7 @@ debugnet_udp_output(struct debugnet_pcb *pcb, struct m
        udp = mtod(m, void *);
        udp->uh_ulen = htons(m->m_pkthdr.len);
        /* Use this src port so that the server can connect() the socket */
-       udp->uh_sport = htons(pcb->dp_client_ack_port);
+       udp->uh_sport = htons(pcb->dp_client_port);
        udp->uh_dport = htons(pcb->dp_server_port);
        /* Computed later (protocol-dependent). */
        udp->uh_sum = 0;
@@ -183,6 +183,28 @@ debugnet_udp_output(struct debugnet_pcb *pcb, struct m
        return (debugnet_ip_output(pcb, m));
 }
 
+static int
+debugnet_ack_output(struct debugnet_pcb *pcb, uint32_t seqno /* net endian */)
+{
+       struct debugnet_ack *dn_ack;
+       struct mbuf *m;
+
+       DNETDEBUG("Acking with seqno %u\n", ntohl(seqno));
+
+       m = m_gethdr(M_NOWAIT, MT_DATA);
+       if (m == NULL) {
+               printf("%s: Out of mbufs\n", __func__);
+               return (ENOBUFS);
+       }
+       m->m_len = sizeof(*dn_ack);
+       m->m_pkthdr.len = sizeof(*dn_ack);
+       MH_ALIGN(m, sizeof(*dn_ack));
+       dn_ack = mtod(m, void *);
+       dn_ack->da_seqno = seqno;
+
+       return (debugnet_udp_output(pcb, m));
+}
+
 /*
  * Dummy free function for debugnet clusters.
  */
@@ -216,6 +238,9 @@ debugnet_send(struct debugnet_pcb *pcb, uint32_t type,
        uint32_t i, pktlen, sent_so_far;
        int retries, polls, error;
 
+       if (pcb->dp_state == DN_STATE_REMOTE_CLOSED)
+               return (ECONNRESET);
+
        want_acks = 0;
        pcb->dp_rcvd_acks = 0;
        retries = 0;
@@ -307,6 +332,8 @@ retransmit:
                }
                debugnet_network_poll(pcb->dp_ifp);
                DELAY(500);
+               if (pcb->dp_state == DN_STATE_REMOTE_CLOSED)
+                       return (ECONNRESET);
        }
        pcb->dp_seqno += i;
        return (0);
@@ -316,7 +343,63 @@ retransmit:
  * Network input primitives.
  */
 
+/*
+ * Just introspect the header enough to fire off a seqno ack and validate
+ * length fits.
+ */
 static void
+debugnet_handle_rx_msg(struct debugnet_pcb *pcb, struct mbuf **mb)
+{
+       const struct debugnet_msg_hdr *dnh;
+       struct mbuf *m;
+       int error;
+
+       m = *mb;
+
+       if (m->m_pkthdr.len < sizeof(*dnh)) {
+               DNETDEBUG("ignoring small debugnet_msg packet\n");
+               return;
+       }
+
+       /* Get ND header. */
+       if (m->m_len < sizeof(*dnh)) {
+               m = m_pullup(m, sizeof(*dnh));
+               *mb = m;
+               if (m == NULL) {
+                       DNETDEBUG("m_pullup failed\n");
+                       return;
+               }
+       }
+       dnh = mtod(m, const void *);
+
+       if (ntohl(dnh->mh_len) + sizeof(*dnh) > m->m_pkthdr.len) {
+               DNETDEBUG("Dropping short packet.\n");
+               return;
+       }
+
+       /*
+        * If the issue is transient (ENOBUFS), sender should resend.  If
+        * non-transient (like driver objecting to rx -> tx from the same
+        * thread), not much else we can do.
+        */
+       error = debugnet_ack_output(pcb, dnh->mh_seqno);
+       if (error != 0)
+               return;
+
+       if (ntohl(dnh->mh_type) == DEBUGNET_FINISHED) {
+               printf("Remote shut down the connection on us!\n");
+               pcb->dp_state = DN_STATE_REMOTE_CLOSED;
+
+               /*
+                * Continue through to the user handler so they are signalled
+                * not to wait for further rx.
+                */
+       }
+
+       pcb->dp_rx_handler(pcb, mb);
+}
+
+static void
 debugnet_handle_ack(struct debugnet_pcb *pcb, struct mbuf **mb, uint16_t sport)
 {
        const struct debugnet_ack *dn_ack;
@@ -325,10 +408,6 @@ debugnet_handle_ack(struct debugnet_pcb *pcb, struct m
 
        m = *mb;
 
-       if (m->m_pkthdr.len < sizeof(*dn_ack)) {
-               DNETDEBUG("ignoring small ACK packet\n");
-               return;
-       }
        /* Get Ack. */
        if (m->m_len < sizeof(*dn_ack)) {
                m = m_pullup(m, sizeof(*dn_ack));
@@ -363,7 +442,7 @@ debugnet_handle_udp(struct debugnet_pcb *pcb, struct m
 {
        const struct udphdr *udp;
        struct mbuf *m;
-       uint16_t sport;
+       uint16_t sport, ulen;
 
        /* UDP processing. */
 
@@ -384,15 +463,39 @@ debugnet_handle_udp(struct debugnet_pcb *pcb, struct m
        }
        udp = mtod(m, const void *);
 
-       /* For now, the only UDP packets we expect to receive are acks. */
-       if (ntohs(udp->uh_dport) != pcb->dp_client_ack_port) {
-               DNETDEBUG("not on the expected ACK port.\n");
+       /* We expect to receive UDP packets on the configured client port. */
+       if (ntohs(udp->uh_dport) != pcb->dp_client_port) {
+               DNETDEBUG("not on the expected port.\n");
                return;
        }
+
+       /* Check that ulen does not exceed actual size of data. */
+       ulen = ntohs(udp->uh_ulen);
+       if (m->m_pkthdr.len < ulen) {
+               DNETDEBUG("ignoring runt UDP packet\n");
+               return;
+       }
+
        sport = ntohs(udp->uh_sport);
 
        m_adj(m, sizeof(*udp));
-       debugnet_handle_ack(pcb, mb, sport);
+       ulen -= sizeof(*udp);
+
+       if (ulen == sizeof(struct debugnet_ack)) {
+               debugnet_handle_ack(pcb, mb, sport);
+               return;
+       }
+
+       if (pcb->dp_rx_handler == NULL) {
+               if (ulen < sizeof(struct debugnet_ack))
+                       DNETDEBUG("ignoring small ACK packet\n");
+               else
+                       DNETDEBUG("ignoring unexpected non-ACK packet on "
+                           "half-duplex connection.\n");
+               return;
+       }
+
+       debugnet_handle_rx_msg(pcb, mb);
 }
 
 /*
@@ -523,9 +626,10 @@ debugnet_connect(const struct debugnet_conn_params *dc
                .dp_server = dcp->dc_server,
                .dp_gateway = dcp->dc_gateway,
                .dp_server_port = dcp->dc_herald_port,  /* Initially */
-               .dp_client_ack_port = dcp->dc_client_ack_port,
+               .dp_client_port = dcp->dc_client_port,
                .dp_seqno = 1,
                .dp_ifp = dcp->dc_ifp,
+               .dp_rx_handler = dcp->dc_rx_handler,
        };
 
        /* Switch to the debugnet mbuf zones. */
@@ -593,7 +697,7 @@ debugnet_connect(const struct debugnet_conn_params *dc
                    serbuf, pcb->dp_server_port,
                    (pcb->dp_gateway == INADDR_ANY) ? "" : " via ",
                    (pcb->dp_gateway == INADDR_ANY) ? "" : gwbuf,
-                   clibuf, pcb->dp_client_ack_port, if_name(ifp));
+                   clibuf, pcb->dp_client_port, if_name(ifp));
        }
 
        /* Validate iface is online and supported. */

Modified: head/sys/net/debugnet.h
==============================================================================
--- head/sys/net/debugnet.h     Thu Oct 17 20:18:07 2019        (r353695)
+++ head/sys/net/debugnet.h     Thu Oct 17 20:25:15 2019        (r353696)
@@ -90,6 +90,8 @@ struct debugnet_methods {
 #define        DEBUGNET_SUPPORTED_NIC(ifp)                             \
        ((ifp)->if_debugnet_methods != NULL && (ifp)->if_type == IFT_ETHER)
 
+struct debugnet_pcb; /* opaque */
+
 /*
  * Debugnet consumer API.
  */
@@ -100,13 +102,30 @@ struct debugnet_conn_params {
        in_addr_t       dc_gateway;
 
        uint16_t        dc_herald_port;
-       uint16_t        dc_client_ack_port;
+       uint16_t        dc_client_port;
 
        const void      *dc_herald_data;
        uint32_t        dc_herald_datalen;
-};
 
-struct debugnet_pcb; /* opaque */
+       /*
+        * 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.
+        */
+       void            (*dc_rx_handler)(struct debugnet_pcb *, struct mbuf **);
+};
 
 /*
  * Open a unidirectional stream to the specified server's herald port.

Modified: head/sys/net/debugnet_int.h
==============================================================================
--- head/sys/net/debugnet_int.h Thu Oct 17 20:18:07 2019        (r353695)
+++ head/sys/net/debugnet_int.h Thu Oct 17 20:25:15 2019        (r353696)
@@ -50,6 +50,7 @@ enum dnet_pcb_st {
        DN_STATE_INIT = 1,
        DN_STATE_HAVE_GW_MAC,
        DN_STATE_GOT_HERALD_PORT,
+       DN_STATE_REMOTE_CLOSED,
 };
 
 struct debugnet_pcb {
@@ -67,8 +68,12 @@ struct debugnet_pcb {
        /* Saved driver if_input to restore on close. */
        void                    (*dp_drv_input)(struct ifnet *, struct mbuf *);
 
+       /* RX handler for bidirectional protocols. */
+       void                    (*dp_rx_handler)(struct debugnet_pcb *,
+                                   struct mbuf **);
+
        enum dnet_pcb_st        dp_state;
-       uint16_t                dp_client_ack_port;
+       uint16_t                dp_client_port;
        bool                    dp_event_started;
 };
 

Modified: head/sys/netinet/netdump/netdump_client.c
==============================================================================
--- head/sys/netinet/netdump/netdump_client.c   Thu Oct 17 20:18:07 2019        
(r353695)
+++ head/sys/netinet/netdump/netdump_client.c   Thu Oct 17 20:25:15 2019        
(r353696)
@@ -316,7 +316,7 @@ netdump_start(struct dumperinfo *di)
        dcp.dc_gateway = nd_gateway.s_addr;
 
        dcp.dc_herald_port = NETDUMP_PORT;
-       dcp.dc_client_ack_port = NETDUMP_ACKPORT;
+       dcp.dc_client_port = NETDUMP_ACKPORT;
 
        dcp.dc_herald_data = nd_path;
        dcp.dc_herald_datalen = (nd_path[0] == 0) ? 0 : strlen(nd_path) + 1;
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to