svn commit: r353696 - in head/sys: net netinet/netdump

Conrad Meyer cem at FreeBSD.org
Thu Oct 17 20:25:17 UTC 2019


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;


More information about the svn-src-all mailing list