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
This commit is contained in:
parent
b2807792f1
commit
e9c6962599
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=353696
@ -175,7 +175,7 @@ debugnet_udp_output(struct debugnet_pcb *pcb, struct mbuf *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 mbuf *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, const void *data,
|
||||
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 @@ debugnet_send(struct debugnet_pcb *pcb, uint32_t type, const void *data,
|
||||
}
|
||||
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,6 +343,62 @@ debugnet_send(struct debugnet_pcb *pcb, uint32_t type, const void *data,
|
||||
* 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)
|
||||
{
|
||||
@ -325,10 +408,6 @@ debugnet_handle_ack(struct debugnet_pcb *pcb, struct mbuf **mb, uint16_t sport)
|
||||
|
||||
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 mbuf **mb)
|
||||
{
|
||||
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 mbuf **mb)
|
||||
}
|
||||
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 *dcp,
|
||||
.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 *dcp,
|
||||
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. */
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user