xref: /f-stack/freebsd/net/debugnet.c (revision 22ce4aff)
1*22ce4affSfengbojiang /*-
2*22ce4affSfengbojiang  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*22ce4affSfengbojiang  *
4*22ce4affSfengbojiang  * Copyright (c) 2019 Isilon Systems, LLC.
5*22ce4affSfengbojiang  * Copyright (c) 2005-2014 Sandvine Incorporated. All rights reserved.
6*22ce4affSfengbojiang  * Copyright (c) 2000 Darrell Anderson
7*22ce4affSfengbojiang  * All rights reserved.
8*22ce4affSfengbojiang  *
9*22ce4affSfengbojiang  * Redistribution and use in source and binary forms, with or without
10*22ce4affSfengbojiang  * modification, are permitted provided that the following conditions
11*22ce4affSfengbojiang  * are met:
12*22ce4affSfengbojiang  * 1. Redistributions of source code must retain the above copyright
13*22ce4affSfengbojiang  *    notice, this list of conditions and the following disclaimer.
14*22ce4affSfengbojiang  * 2. Redistributions in binary form must reproduce the above copyright
15*22ce4affSfengbojiang  *    notice, this list of conditions and the following disclaimer in the
16*22ce4affSfengbojiang  *    documentation and/or other materials provided with the distribution.
17*22ce4affSfengbojiang  *
18*22ce4affSfengbojiang  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19*22ce4affSfengbojiang  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*22ce4affSfengbojiang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*22ce4affSfengbojiang  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22*22ce4affSfengbojiang  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*22ce4affSfengbojiang  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*22ce4affSfengbojiang  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*22ce4affSfengbojiang  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*22ce4affSfengbojiang  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*22ce4affSfengbojiang  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*22ce4affSfengbojiang  * SUCH DAMAGE.
29*22ce4affSfengbojiang  */
30*22ce4affSfengbojiang 
31*22ce4affSfengbojiang #include <sys/cdefs.h>
32*22ce4affSfengbojiang __FBSDID("$FreeBSD$");
33*22ce4affSfengbojiang 
34*22ce4affSfengbojiang #include "opt_ddb.h"
35*22ce4affSfengbojiang #include "opt_inet.h"
36*22ce4affSfengbojiang 
37*22ce4affSfengbojiang #include <sys/param.h>
38*22ce4affSfengbojiang #include <sys/systm.h>
39*22ce4affSfengbojiang #include <sys/endian.h>
40*22ce4affSfengbojiang #include <sys/errno.h>
41*22ce4affSfengbojiang #include <sys/eventhandler.h>
42*22ce4affSfengbojiang #include <sys/socket.h>
43*22ce4affSfengbojiang #include <sys/sysctl.h>
44*22ce4affSfengbojiang 
45*22ce4affSfengbojiang #ifdef DDB
46*22ce4affSfengbojiang #include <ddb/ddb.h>
47*22ce4affSfengbojiang #include <ddb/db_lex.h>
48*22ce4affSfengbojiang #endif
49*22ce4affSfengbojiang 
50*22ce4affSfengbojiang #include <net/ethernet.h>
51*22ce4affSfengbojiang #include <net/if.h>
52*22ce4affSfengbojiang #include <net/if_arp.h>
53*22ce4affSfengbojiang #include <net/if_dl.h>
54*22ce4affSfengbojiang #include <net/if_types.h>
55*22ce4affSfengbojiang #include <net/if_var.h>
56*22ce4affSfengbojiang #include <net/route.h>
57*22ce4affSfengbojiang #include <net/route/nhop.h>
58*22ce4affSfengbojiang 
59*22ce4affSfengbojiang #include <netinet/in.h>
60*22ce4affSfengbojiang #include <netinet/in_fib.h>
61*22ce4affSfengbojiang #include <netinet/in_systm.h>
62*22ce4affSfengbojiang #include <netinet/in_var.h>
63*22ce4affSfengbojiang #include <netinet/ip.h>
64*22ce4affSfengbojiang #include <netinet/ip_var.h>
65*22ce4affSfengbojiang #include <netinet/ip_options.h>
66*22ce4affSfengbojiang #include <netinet/udp.h>
67*22ce4affSfengbojiang #include <netinet/udp_var.h>
68*22ce4affSfengbojiang 
69*22ce4affSfengbojiang #include <machine/in_cksum.h>
70*22ce4affSfengbojiang #include <machine/pcb.h>
71*22ce4affSfengbojiang 
72*22ce4affSfengbojiang #include <net/debugnet.h>
73*22ce4affSfengbojiang #define	DEBUGNET_INTERNAL
74*22ce4affSfengbojiang #include <net/debugnet_int.h>
75*22ce4affSfengbojiang 
76*22ce4affSfengbojiang FEATURE(debugnet, "Debugnet support");
77*22ce4affSfengbojiang 
78*22ce4affSfengbojiang SYSCTL_NODE(_net, OID_AUTO, debugnet, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
79*22ce4affSfengbojiang     "debugnet parameters");
80*22ce4affSfengbojiang 
81*22ce4affSfengbojiang unsigned debugnet_debug;
82*22ce4affSfengbojiang SYSCTL_UINT(_net_debugnet, OID_AUTO, debug, CTLFLAG_RWTUN,
83*22ce4affSfengbojiang     &debugnet_debug, 0,
84*22ce4affSfengbojiang     "Debug message verbosity (0: off; 1: on; 2: verbose)");
85*22ce4affSfengbojiang 
86*22ce4affSfengbojiang int debugnet_npolls = 2000;
87*22ce4affSfengbojiang SYSCTL_INT(_net_debugnet, OID_AUTO, npolls, CTLFLAG_RWTUN,
88*22ce4affSfengbojiang     &debugnet_npolls, 0,
89*22ce4affSfengbojiang     "Number of times to poll before assuming packet loss (0.5ms per poll)");
90*22ce4affSfengbojiang int debugnet_nretries = 10;
91*22ce4affSfengbojiang SYSCTL_INT(_net_debugnet, OID_AUTO, nretries, CTLFLAG_RWTUN,
92*22ce4affSfengbojiang     &debugnet_nretries, 0,
93*22ce4affSfengbojiang     "Number of retransmit attempts before giving up");
94*22ce4affSfengbojiang 
95*22ce4affSfengbojiang static bool g_debugnet_pcb_inuse;
96*22ce4affSfengbojiang static struct debugnet_pcb g_dnet_pcb;
97*22ce4affSfengbojiang 
98*22ce4affSfengbojiang /*
99*22ce4affSfengbojiang  * Simple accessors for opaque PCB.
100*22ce4affSfengbojiang  */
101*22ce4affSfengbojiang const unsigned char *
debugnet_get_gw_mac(const struct debugnet_pcb * pcb)102*22ce4affSfengbojiang debugnet_get_gw_mac(const struct debugnet_pcb *pcb)
103*22ce4affSfengbojiang {
104*22ce4affSfengbojiang 	MPASS(g_debugnet_pcb_inuse && pcb == &g_dnet_pcb &&
105*22ce4affSfengbojiang 	    pcb->dp_state >= DN_STATE_HAVE_GW_MAC);
106*22ce4affSfengbojiang 	return (pcb->dp_gw_mac.octet);
107*22ce4affSfengbojiang }
108*22ce4affSfengbojiang 
109*22ce4affSfengbojiang /*
110*22ce4affSfengbojiang  * Start of network primitives, beginning with output primitives.
111*22ce4affSfengbojiang  */
112*22ce4affSfengbojiang 
113*22ce4affSfengbojiang /*
114*22ce4affSfengbojiang  * Handles creation of the ethernet header, then places outgoing packets into
115*22ce4affSfengbojiang  * the tx buffer for the NIC
116*22ce4affSfengbojiang  *
117*22ce4affSfengbojiang  * Parameters:
118*22ce4affSfengbojiang  *	m	The mbuf containing the packet to be sent (will be freed by
119*22ce4affSfengbojiang  *		this function or the NIC driver)
120*22ce4affSfengbojiang  *	ifp	The interface to send on
121*22ce4affSfengbojiang  *	dst	The destination ethernet address (source address will be looked
122*22ce4affSfengbojiang  *		up using ifp)
123*22ce4affSfengbojiang  *	etype	The ETHERTYPE_* value for the protocol that is being sent
124*22ce4affSfengbojiang  *
125*22ce4affSfengbojiang  * Returns:
126*22ce4affSfengbojiang  *	int	see errno.h, 0 for success
127*22ce4affSfengbojiang  */
128*22ce4affSfengbojiang int
debugnet_ether_output(struct mbuf * m,struct ifnet * ifp,struct ether_addr dst,u_short etype)129*22ce4affSfengbojiang debugnet_ether_output(struct mbuf *m, struct ifnet *ifp, struct ether_addr dst,
130*22ce4affSfengbojiang     u_short etype)
131*22ce4affSfengbojiang {
132*22ce4affSfengbojiang 	struct ether_header *eh;
133*22ce4affSfengbojiang 
134*22ce4affSfengbojiang 	if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) != IFF_UP) ||
135*22ce4affSfengbojiang 	    (ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) {
136*22ce4affSfengbojiang 		if_printf(ifp, "%s: interface isn't up\n", __func__);
137*22ce4affSfengbojiang 		m_freem(m);
138*22ce4affSfengbojiang 		return (ENETDOWN);
139*22ce4affSfengbojiang 	}
140*22ce4affSfengbojiang 
141*22ce4affSfengbojiang 	/* Fill in the ethernet header. */
142*22ce4affSfengbojiang 	M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
143*22ce4affSfengbojiang 	if (m == NULL) {
144*22ce4affSfengbojiang 		printf("%s: out of mbufs\n", __func__);
145*22ce4affSfengbojiang 		return (ENOBUFS);
146*22ce4affSfengbojiang 	}
147*22ce4affSfengbojiang 	eh = mtod(m, struct ether_header *);
148*22ce4affSfengbojiang 	memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
149*22ce4affSfengbojiang 	memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN);
150*22ce4affSfengbojiang 	eh->ether_type = htons(etype);
151*22ce4affSfengbojiang 	return (ifp->if_debugnet_methods->dn_transmit(ifp, m));
152*22ce4affSfengbojiang }
153*22ce4affSfengbojiang 
154*22ce4affSfengbojiang /*
155*22ce4affSfengbojiang  * Unreliable transmission of an mbuf chain to the debugnet server
156*22ce4affSfengbojiang  * Note: can't handle fragmentation; fails if the packet is larger than
157*22ce4affSfengbojiang  *	 ifp->if_mtu after adding the UDP/IP headers
158*22ce4affSfengbojiang  *
159*22ce4affSfengbojiang  * Parameters:
160*22ce4affSfengbojiang  *	pcb	The debugnet context block
161*22ce4affSfengbojiang  *	m	mbuf chain
162*22ce4affSfengbojiang  *
163*22ce4affSfengbojiang  * Returns:
164*22ce4affSfengbojiang  *	int	see errno.h, 0 for success
165*22ce4affSfengbojiang  */
166*22ce4affSfengbojiang static int
debugnet_udp_output(struct debugnet_pcb * pcb,struct mbuf * m)167*22ce4affSfengbojiang debugnet_udp_output(struct debugnet_pcb *pcb, struct mbuf *m)
168*22ce4affSfengbojiang {
169*22ce4affSfengbojiang 	struct udphdr *udp;
170*22ce4affSfengbojiang 
171*22ce4affSfengbojiang 	MPASS(pcb->dp_state >= DN_STATE_HAVE_GW_MAC);
172*22ce4affSfengbojiang 
173*22ce4affSfengbojiang 	M_PREPEND(m, sizeof(*udp), M_NOWAIT);
174*22ce4affSfengbojiang 	if (m == NULL) {
175*22ce4affSfengbojiang 		printf("%s: out of mbufs\n", __func__);
176*22ce4affSfengbojiang 		return (ENOBUFS);
177*22ce4affSfengbojiang 	}
178*22ce4affSfengbojiang 
179*22ce4affSfengbojiang 	udp = mtod(m, void *);
180*22ce4affSfengbojiang 	udp->uh_ulen = htons(m->m_pkthdr.len);
181*22ce4affSfengbojiang 	/* Use this src port so that the server can connect() the socket */
182*22ce4affSfengbojiang 	udp->uh_sport = htons(pcb->dp_client_port);
183*22ce4affSfengbojiang 	udp->uh_dport = htons(pcb->dp_server_port);
184*22ce4affSfengbojiang 	/* Computed later (protocol-dependent). */
185*22ce4affSfengbojiang 	udp->uh_sum = 0;
186*22ce4affSfengbojiang 
187*22ce4affSfengbojiang 	return (debugnet_ip_output(pcb, m));
188*22ce4affSfengbojiang }
189*22ce4affSfengbojiang 
190*22ce4affSfengbojiang int
debugnet_ack_output(struct debugnet_pcb * pcb,uint32_t seqno)191*22ce4affSfengbojiang debugnet_ack_output(struct debugnet_pcb *pcb, uint32_t seqno /* net endian */)
192*22ce4affSfengbojiang {
193*22ce4affSfengbojiang 	struct debugnet_ack *dn_ack;
194*22ce4affSfengbojiang 	struct mbuf *m;
195*22ce4affSfengbojiang 
196*22ce4affSfengbojiang 	DNETDEBUG("Acking with seqno %u\n", ntohl(seqno));
197*22ce4affSfengbojiang 
198*22ce4affSfengbojiang 	m = m_gethdr(M_NOWAIT, MT_DATA);
199*22ce4affSfengbojiang 	if (m == NULL) {
200*22ce4affSfengbojiang 		printf("%s: Out of mbufs\n", __func__);
201*22ce4affSfengbojiang 		return (ENOBUFS);
202*22ce4affSfengbojiang 	}
203*22ce4affSfengbojiang 	m->m_len = sizeof(*dn_ack);
204*22ce4affSfengbojiang 	m->m_pkthdr.len = sizeof(*dn_ack);
205*22ce4affSfengbojiang 	MH_ALIGN(m, sizeof(*dn_ack));
206*22ce4affSfengbojiang 	dn_ack = mtod(m, void *);
207*22ce4affSfengbojiang 	dn_ack->da_seqno = seqno;
208*22ce4affSfengbojiang 
209*22ce4affSfengbojiang 	return (debugnet_udp_output(pcb, m));
210*22ce4affSfengbojiang }
211*22ce4affSfengbojiang 
212*22ce4affSfengbojiang /*
213*22ce4affSfengbojiang  * Dummy free function for debugnet clusters.
214*22ce4affSfengbojiang  */
215*22ce4affSfengbojiang static void
debugnet_mbuf_free(struct mbuf * m __unused)216*22ce4affSfengbojiang debugnet_mbuf_free(struct mbuf *m __unused)
217*22ce4affSfengbojiang {
218*22ce4affSfengbojiang }
219*22ce4affSfengbojiang 
220*22ce4affSfengbojiang /*
221*22ce4affSfengbojiang  * Construct and reliably send a debugnet packet.  May fail from a resource
222*22ce4affSfengbojiang  * shortage or extreme number of unacknowledged retransmissions.  Wait for
223*22ce4affSfengbojiang  * an acknowledgement before returning.  Splits packets into chunks small
224*22ce4affSfengbojiang  * enough to be sent without fragmentation (looks up the interface MTU)
225*22ce4affSfengbojiang  *
226*22ce4affSfengbojiang  * Parameters:
227*22ce4affSfengbojiang  *	type	debugnet packet type (HERALD, FINISHED, ...)
228*22ce4affSfengbojiang  *	data	data
229*22ce4affSfengbojiang  *	datalen	data size (bytes)
230*22ce4affSfengbojiang  *	auxdata	optional auxiliary information
231*22ce4affSfengbojiang  *
232*22ce4affSfengbojiang  * Returns:
233*22ce4affSfengbojiang  *	int see errno.h, 0 for success
234*22ce4affSfengbojiang  */
235*22ce4affSfengbojiang int
debugnet_send(struct debugnet_pcb * pcb,uint32_t type,const void * data,uint32_t datalen,const struct debugnet_proto_aux * auxdata)236*22ce4affSfengbojiang debugnet_send(struct debugnet_pcb *pcb, uint32_t type, const void *data,
237*22ce4affSfengbojiang     uint32_t datalen, const struct debugnet_proto_aux *auxdata)
238*22ce4affSfengbojiang {
239*22ce4affSfengbojiang 	struct debugnet_msg_hdr *dn_msg_hdr;
240*22ce4affSfengbojiang 	struct mbuf *m, *m2;
241*22ce4affSfengbojiang 	uint64_t want_acks;
242*22ce4affSfengbojiang 	uint32_t i, pktlen, sent_so_far;
243*22ce4affSfengbojiang 	int retries, polls, error;
244*22ce4affSfengbojiang 
245*22ce4affSfengbojiang 	if (pcb->dp_state == DN_STATE_REMOTE_CLOSED)
246*22ce4affSfengbojiang 		return (ECONNRESET);
247*22ce4affSfengbojiang 
248*22ce4affSfengbojiang 	want_acks = 0;
249*22ce4affSfengbojiang 	pcb->dp_rcvd_acks = 0;
250*22ce4affSfengbojiang 	retries = 0;
251*22ce4affSfengbojiang 
252*22ce4affSfengbojiang retransmit:
253*22ce4affSfengbojiang 	/* Chunks can be too big to fit in packets. */
254*22ce4affSfengbojiang 	for (i = sent_so_far = 0; sent_so_far < datalen ||
255*22ce4affSfengbojiang 	    (i == 0 && datalen == 0); i++) {
256*22ce4affSfengbojiang 		pktlen = datalen - sent_so_far;
257*22ce4affSfengbojiang 
258*22ce4affSfengbojiang 		/* Bound: the interface MTU (assume no IP options). */
259*22ce4affSfengbojiang 		pktlen = min(pktlen, pcb->dp_ifp->if_mtu -
260*22ce4affSfengbojiang 		    sizeof(struct udpiphdr) - sizeof(struct debugnet_msg_hdr));
261*22ce4affSfengbojiang 
262*22ce4affSfengbojiang 		/*
263*22ce4affSfengbojiang 		 * Check if it is retransmitting and this has been ACKed
264*22ce4affSfengbojiang 		 * already.
265*22ce4affSfengbojiang 		 */
266*22ce4affSfengbojiang 		if ((pcb->dp_rcvd_acks & (1 << i)) != 0) {
267*22ce4affSfengbojiang 			sent_so_far += pktlen;
268*22ce4affSfengbojiang 			continue;
269*22ce4affSfengbojiang 		}
270*22ce4affSfengbojiang 
271*22ce4affSfengbojiang 		/*
272*22ce4affSfengbojiang 		 * Get and fill a header mbuf, then chain data as an extended
273*22ce4affSfengbojiang 		 * mbuf.
274*22ce4affSfengbojiang 		 */
275*22ce4affSfengbojiang 		m = m_gethdr(M_NOWAIT, MT_DATA);
276*22ce4affSfengbojiang 		if (m == NULL) {
277*22ce4affSfengbojiang 			printf("%s: Out of mbufs\n", __func__);
278*22ce4affSfengbojiang 			return (ENOBUFS);
279*22ce4affSfengbojiang 		}
280*22ce4affSfengbojiang 		m->m_len = sizeof(struct debugnet_msg_hdr);
281*22ce4affSfengbojiang 		m->m_pkthdr.len = sizeof(struct debugnet_msg_hdr);
282*22ce4affSfengbojiang 		MH_ALIGN(m, sizeof(struct debugnet_msg_hdr));
283*22ce4affSfengbojiang 		dn_msg_hdr = mtod(m, struct debugnet_msg_hdr *);
284*22ce4affSfengbojiang 		dn_msg_hdr->mh_seqno = htonl(pcb->dp_seqno + i);
285*22ce4affSfengbojiang 		dn_msg_hdr->mh_type = htonl(type);
286*22ce4affSfengbojiang 		dn_msg_hdr->mh_len = htonl(pktlen);
287*22ce4affSfengbojiang 
288*22ce4affSfengbojiang 		if (auxdata != NULL) {
289*22ce4affSfengbojiang 			dn_msg_hdr->mh_offset =
290*22ce4affSfengbojiang 			    htobe64(auxdata->dp_offset_start + sent_so_far);
291*22ce4affSfengbojiang 			dn_msg_hdr->mh_aux2 = htobe32(auxdata->dp_aux2);
292*22ce4affSfengbojiang 		} else {
293*22ce4affSfengbojiang 			dn_msg_hdr->mh_offset = htobe64(sent_so_far);
294*22ce4affSfengbojiang 			dn_msg_hdr->mh_aux2 = 0;
295*22ce4affSfengbojiang 		}
296*22ce4affSfengbojiang 
297*22ce4affSfengbojiang 		if (pktlen != 0) {
298*22ce4affSfengbojiang 			m2 = m_get(M_NOWAIT, MT_DATA);
299*22ce4affSfengbojiang 			if (m2 == NULL) {
300*22ce4affSfengbojiang 				m_freem(m);
301*22ce4affSfengbojiang 				printf("%s: Out of mbufs\n", __func__);
302*22ce4affSfengbojiang 				return (ENOBUFS);
303*22ce4affSfengbojiang 			}
304*22ce4affSfengbojiang 			MEXTADD(m2, __DECONST(char *, data) + sent_so_far,
305*22ce4affSfengbojiang 			    pktlen, debugnet_mbuf_free, NULL, NULL, 0,
306*22ce4affSfengbojiang 			    EXT_DISPOSABLE);
307*22ce4affSfengbojiang 			m2->m_len = pktlen;
308*22ce4affSfengbojiang 
309*22ce4affSfengbojiang 			m_cat(m, m2);
310*22ce4affSfengbojiang 			m->m_pkthdr.len += pktlen;
311*22ce4affSfengbojiang 		}
312*22ce4affSfengbojiang 		error = debugnet_udp_output(pcb, m);
313*22ce4affSfengbojiang 		if (error != 0)
314*22ce4affSfengbojiang 			return (error);
315*22ce4affSfengbojiang 
316*22ce4affSfengbojiang 		/* Note that we're waiting for this packet in the bitfield. */
317*22ce4affSfengbojiang 		want_acks |= (1 << i);
318*22ce4affSfengbojiang 		sent_so_far += pktlen;
319*22ce4affSfengbojiang 	}
320*22ce4affSfengbojiang 	if (i >= DEBUGNET_MAX_IN_FLIGHT)
321*22ce4affSfengbojiang 		printf("Warning: Sent more than %d packets (%d). "
322*22ce4affSfengbojiang 		    "Acknowledgements will fail unless the size of "
323*22ce4affSfengbojiang 		    "rcvd_acks/want_acks is increased.\n",
324*22ce4affSfengbojiang 		    DEBUGNET_MAX_IN_FLIGHT, i);
325*22ce4affSfengbojiang 
326*22ce4affSfengbojiang 	/*
327*22ce4affSfengbojiang 	 * Wait for acks.  A *real* window would speed things up considerably.
328*22ce4affSfengbojiang 	 */
329*22ce4affSfengbojiang 	polls = 0;
330*22ce4affSfengbojiang 	while (pcb->dp_rcvd_acks != want_acks) {
331*22ce4affSfengbojiang 		if (polls++ > debugnet_npolls) {
332*22ce4affSfengbojiang 			if (retries++ > debugnet_nretries)
333*22ce4affSfengbojiang 				return (ETIMEDOUT);
334*22ce4affSfengbojiang 			printf(". ");
335*22ce4affSfengbojiang 			goto retransmit;
336*22ce4affSfengbojiang 		}
337*22ce4affSfengbojiang 		debugnet_network_poll(pcb);
338*22ce4affSfengbojiang 		DELAY(500);
339*22ce4affSfengbojiang 		if (pcb->dp_state == DN_STATE_REMOTE_CLOSED)
340*22ce4affSfengbojiang 			return (ECONNRESET);
341*22ce4affSfengbojiang 	}
342*22ce4affSfengbojiang 	pcb->dp_seqno += i;
343*22ce4affSfengbojiang 	return (0);
344*22ce4affSfengbojiang }
345*22ce4affSfengbojiang 
346*22ce4affSfengbojiang /*
347*22ce4affSfengbojiang  * Network input primitives.
348*22ce4affSfengbojiang  */
349*22ce4affSfengbojiang 
350*22ce4affSfengbojiang /*
351*22ce4affSfengbojiang  * Just introspect the header enough to fire off a seqno ack and validate
352*22ce4affSfengbojiang  * length fits.
353*22ce4affSfengbojiang  */
354*22ce4affSfengbojiang static void
debugnet_handle_rx_msg(struct debugnet_pcb * pcb,struct mbuf ** mb)355*22ce4affSfengbojiang debugnet_handle_rx_msg(struct debugnet_pcb *pcb, struct mbuf **mb)
356*22ce4affSfengbojiang {
357*22ce4affSfengbojiang 	const struct debugnet_msg_hdr *dnh;
358*22ce4affSfengbojiang 	struct mbuf *m;
359*22ce4affSfengbojiang 	int error;
360*22ce4affSfengbojiang 
361*22ce4affSfengbojiang 	m = *mb;
362*22ce4affSfengbojiang 
363*22ce4affSfengbojiang 	if (m->m_pkthdr.len < sizeof(*dnh)) {
364*22ce4affSfengbojiang 		DNETDEBUG("ignoring small debugnet_msg packet\n");
365*22ce4affSfengbojiang 		return;
366*22ce4affSfengbojiang 	}
367*22ce4affSfengbojiang 
368*22ce4affSfengbojiang 	/* Get ND header. */
369*22ce4affSfengbojiang 	if (m->m_len < sizeof(*dnh)) {
370*22ce4affSfengbojiang 		m = m_pullup(m, sizeof(*dnh));
371*22ce4affSfengbojiang 		*mb = m;
372*22ce4affSfengbojiang 		if (m == NULL) {
373*22ce4affSfengbojiang 			DNETDEBUG("m_pullup failed\n");
374*22ce4affSfengbojiang 			return;
375*22ce4affSfengbojiang 		}
376*22ce4affSfengbojiang 	}
377*22ce4affSfengbojiang 	dnh = mtod(m, const void *);
378*22ce4affSfengbojiang 
379*22ce4affSfengbojiang 	if (ntohl(dnh->mh_len) + sizeof(*dnh) > m->m_pkthdr.len) {
380*22ce4affSfengbojiang 		DNETDEBUG("Dropping short packet.\n");
381*22ce4affSfengbojiang 		return;
382*22ce4affSfengbojiang 	}
383*22ce4affSfengbojiang 
384*22ce4affSfengbojiang 	/*
385*22ce4affSfengbojiang 	 * If the issue is transient (ENOBUFS), sender should resend.  If
386*22ce4affSfengbojiang 	 * non-transient (like driver objecting to rx -> tx from the same
387*22ce4affSfengbojiang 	 * thread), not much else we can do.
388*22ce4affSfengbojiang 	 */
389*22ce4affSfengbojiang 	error = debugnet_ack_output(pcb, dnh->mh_seqno);
390*22ce4affSfengbojiang 	if (error != 0)
391*22ce4affSfengbojiang 		return;
392*22ce4affSfengbojiang 
393*22ce4affSfengbojiang 	if (ntohl(dnh->mh_type) == DEBUGNET_FINISHED) {
394*22ce4affSfengbojiang 		printf("Remote shut down the connection on us!\n");
395*22ce4affSfengbojiang 		pcb->dp_state = DN_STATE_REMOTE_CLOSED;
396*22ce4affSfengbojiang 
397*22ce4affSfengbojiang 		/*
398*22ce4affSfengbojiang 		 * Continue through to the user handler so they are signalled
399*22ce4affSfengbojiang 		 * not to wait for further rx.
400*22ce4affSfengbojiang 		 */
401*22ce4affSfengbojiang 	}
402*22ce4affSfengbojiang 
403*22ce4affSfengbojiang 	pcb->dp_rx_handler(pcb, mb);
404*22ce4affSfengbojiang }
405*22ce4affSfengbojiang 
406*22ce4affSfengbojiang static void
debugnet_handle_ack(struct debugnet_pcb * pcb,struct mbuf ** mb,uint16_t sport)407*22ce4affSfengbojiang debugnet_handle_ack(struct debugnet_pcb *pcb, struct mbuf **mb, uint16_t sport)
408*22ce4affSfengbojiang {
409*22ce4affSfengbojiang 	const struct debugnet_ack *dn_ack;
410*22ce4affSfengbojiang 	struct mbuf *m;
411*22ce4affSfengbojiang 	uint32_t rcv_ackno;
412*22ce4affSfengbojiang 
413*22ce4affSfengbojiang 	m = *mb;
414*22ce4affSfengbojiang 
415*22ce4affSfengbojiang 	/* Get Ack. */
416*22ce4affSfengbojiang 	if (m->m_len < sizeof(*dn_ack)) {
417*22ce4affSfengbojiang 		m = m_pullup(m, sizeof(*dn_ack));
418*22ce4affSfengbojiang 		*mb = m;
419*22ce4affSfengbojiang 		if (m == NULL) {
420*22ce4affSfengbojiang 			DNETDEBUG("m_pullup failed\n");
421*22ce4affSfengbojiang 			return;
422*22ce4affSfengbojiang 		}
423*22ce4affSfengbojiang 	}
424*22ce4affSfengbojiang 	dn_ack = mtod(m, const void *);
425*22ce4affSfengbojiang 
426*22ce4affSfengbojiang 	/* Debugnet processing. */
427*22ce4affSfengbojiang 	/*
428*22ce4affSfengbojiang 	 * Packet is meant for us.  Extract the ack sequence number and the
429*22ce4affSfengbojiang 	 * port number if necessary.
430*22ce4affSfengbojiang 	 */
431*22ce4affSfengbojiang 	rcv_ackno = ntohl(dn_ack->da_seqno);
432*22ce4affSfengbojiang 	if (pcb->dp_state < DN_STATE_GOT_HERALD_PORT) {
433*22ce4affSfengbojiang 		pcb->dp_server_port = sport;
434*22ce4affSfengbojiang 		pcb->dp_state = DN_STATE_GOT_HERALD_PORT;
435*22ce4affSfengbojiang 	}
436*22ce4affSfengbojiang 	if (rcv_ackno >= pcb->dp_seqno + DEBUGNET_MAX_IN_FLIGHT)
437*22ce4affSfengbojiang 		printf("%s: ACK %u too far in future!\n", __func__, rcv_ackno);
438*22ce4affSfengbojiang 	else if (rcv_ackno >= pcb->dp_seqno) {
439*22ce4affSfengbojiang 		/* We're interested in this ack. Record it. */
440*22ce4affSfengbojiang 		pcb->dp_rcvd_acks |= 1 << (rcv_ackno - pcb->dp_seqno);
441*22ce4affSfengbojiang 	}
442*22ce4affSfengbojiang }
443*22ce4affSfengbojiang 
444*22ce4affSfengbojiang void
debugnet_handle_udp(struct debugnet_pcb * pcb,struct mbuf ** mb)445*22ce4affSfengbojiang debugnet_handle_udp(struct debugnet_pcb *pcb, struct mbuf **mb)
446*22ce4affSfengbojiang {
447*22ce4affSfengbojiang 	const struct udphdr *udp;
448*22ce4affSfengbojiang 	struct mbuf *m;
449*22ce4affSfengbojiang 	uint16_t sport, ulen;
450*22ce4affSfengbojiang 
451*22ce4affSfengbojiang 	/* UDP processing. */
452*22ce4affSfengbojiang 
453*22ce4affSfengbojiang 	m = *mb;
454*22ce4affSfengbojiang 	if (m->m_pkthdr.len < sizeof(*udp)) {
455*22ce4affSfengbojiang 		DNETDEBUG("ignoring small UDP packet\n");
456*22ce4affSfengbojiang 		return;
457*22ce4affSfengbojiang 	}
458*22ce4affSfengbojiang 
459*22ce4affSfengbojiang 	/* Get UDP headers. */
460*22ce4affSfengbojiang 	if (m->m_len < sizeof(*udp)) {
461*22ce4affSfengbojiang 		m = m_pullup(m, sizeof(*udp));
462*22ce4affSfengbojiang 		*mb = m;
463*22ce4affSfengbojiang 		if (m == NULL) {
464*22ce4affSfengbojiang 			DNETDEBUG("m_pullup failed\n");
465*22ce4affSfengbojiang 			return;
466*22ce4affSfengbojiang 		}
467*22ce4affSfengbojiang 	}
468*22ce4affSfengbojiang 	udp = mtod(m, const void *);
469*22ce4affSfengbojiang 
470*22ce4affSfengbojiang 	/* We expect to receive UDP packets on the configured client port. */
471*22ce4affSfengbojiang 	if (ntohs(udp->uh_dport) != pcb->dp_client_port) {
472*22ce4affSfengbojiang 		DNETDEBUG("not on the expected port.\n");
473*22ce4affSfengbojiang 		return;
474*22ce4affSfengbojiang 	}
475*22ce4affSfengbojiang 
476*22ce4affSfengbojiang 	/* Check that ulen does not exceed actual size of data. */
477*22ce4affSfengbojiang 	ulen = ntohs(udp->uh_ulen);
478*22ce4affSfengbojiang 	if (m->m_pkthdr.len < ulen) {
479*22ce4affSfengbojiang 		DNETDEBUG("ignoring runt UDP packet\n");
480*22ce4affSfengbojiang 		return;
481*22ce4affSfengbojiang 	}
482*22ce4affSfengbojiang 
483*22ce4affSfengbojiang 	sport = ntohs(udp->uh_sport);
484*22ce4affSfengbojiang 
485*22ce4affSfengbojiang 	m_adj(m, sizeof(*udp));
486*22ce4affSfengbojiang 	ulen -= sizeof(*udp);
487*22ce4affSfengbojiang 
488*22ce4affSfengbojiang 	if (ulen == sizeof(struct debugnet_ack)) {
489*22ce4affSfengbojiang 		debugnet_handle_ack(pcb, mb, sport);
490*22ce4affSfengbojiang 		return;
491*22ce4affSfengbojiang 	}
492*22ce4affSfengbojiang 
493*22ce4affSfengbojiang 	if (pcb->dp_rx_handler == NULL) {
494*22ce4affSfengbojiang 		if (ulen < sizeof(struct debugnet_ack))
495*22ce4affSfengbojiang 			DNETDEBUG("ignoring small ACK packet\n");
496*22ce4affSfengbojiang 		else
497*22ce4affSfengbojiang 			DNETDEBUG("ignoring unexpected non-ACK packet on "
498*22ce4affSfengbojiang 			    "half-duplex connection.\n");
499*22ce4affSfengbojiang 		return;
500*22ce4affSfengbojiang 	}
501*22ce4affSfengbojiang 
502*22ce4affSfengbojiang 	debugnet_handle_rx_msg(pcb, mb);
503*22ce4affSfengbojiang }
504*22ce4affSfengbojiang 
505*22ce4affSfengbojiang /*
506*22ce4affSfengbojiang  * Handler for incoming packets directly from the network adapter
507*22ce4affSfengbojiang  * Identifies the packet type (IP or ARP) and passes it along to one of the
508*22ce4affSfengbojiang  * helper functions debugnet_handle_ip or debugnet_handle_arp.
509*22ce4affSfengbojiang  *
510*22ce4affSfengbojiang  * It needs to partially replicate the behaviour of ether_input() and
511*22ce4affSfengbojiang  * ether_demux().
512*22ce4affSfengbojiang  *
513*22ce4affSfengbojiang  * Parameters:
514*22ce4affSfengbojiang  *	ifp	the interface the packet came from
515*22ce4affSfengbojiang  *	m	an mbuf containing the packet received
516*22ce4affSfengbojiang  */
517*22ce4affSfengbojiang static void
debugnet_pkt_in(struct ifnet * ifp,struct mbuf * m)518*22ce4affSfengbojiang debugnet_pkt_in(struct ifnet *ifp, struct mbuf *m)
519*22ce4affSfengbojiang {
520*22ce4affSfengbojiang 	struct ifreq ifr;
521*22ce4affSfengbojiang 	struct ether_header *eh;
522*22ce4affSfengbojiang 	u_short etype;
523*22ce4affSfengbojiang 
524*22ce4affSfengbojiang 	/* Ethernet processing. */
525*22ce4affSfengbojiang 	if ((m->m_flags & M_PKTHDR) == 0) {
526*22ce4affSfengbojiang 		DNETDEBUG_IF(ifp, "discard frame without packet header\n");
527*22ce4affSfengbojiang 		goto done;
528*22ce4affSfengbojiang 	}
529*22ce4affSfengbojiang 	if (m->m_len < ETHER_HDR_LEN) {
530*22ce4affSfengbojiang 		DNETDEBUG_IF(ifp,
531*22ce4affSfengbojiang 	    "discard frame without leading eth header (len %u pktlen %u)\n",
532*22ce4affSfengbojiang 		    m->m_len, m->m_pkthdr.len);
533*22ce4affSfengbojiang 		goto done;
534*22ce4affSfengbojiang 	}
535*22ce4affSfengbojiang 	if ((m->m_flags & M_HASFCS) != 0) {
536*22ce4affSfengbojiang 		m_adj(m, -ETHER_CRC_LEN);
537*22ce4affSfengbojiang 		m->m_flags &= ~M_HASFCS;
538*22ce4affSfengbojiang 	}
539*22ce4affSfengbojiang 	eh = mtod(m, struct ether_header *);
540*22ce4affSfengbojiang 	etype = ntohs(eh->ether_type);
541*22ce4affSfengbojiang 	if ((m->m_flags & M_VLANTAG) != 0 || etype == ETHERTYPE_VLAN) {
542*22ce4affSfengbojiang 		DNETDEBUG_IF(ifp, "ignoring vlan packets\n");
543*22ce4affSfengbojiang 		goto done;
544*22ce4affSfengbojiang 	}
545*22ce4affSfengbojiang 	if (if_gethwaddr(ifp, &ifr) != 0) {
546*22ce4affSfengbojiang 		DNETDEBUG_IF(ifp, "failed to get hw addr for interface\n");
547*22ce4affSfengbojiang 		goto done;
548*22ce4affSfengbojiang 	}
549*22ce4affSfengbojiang 	if (memcmp(ifr.ifr_addr.sa_data, eh->ether_dhost,
550*22ce4affSfengbojiang 	    ETHER_ADDR_LEN) != 0 &&
551*22ce4affSfengbojiang 	    (etype != ETHERTYPE_ARP || !ETHER_IS_BROADCAST(eh->ether_dhost))) {
552*22ce4affSfengbojiang 		DNETDEBUG_IF(ifp,
553*22ce4affSfengbojiang 		    "discard frame with incorrect destination addr\n");
554*22ce4affSfengbojiang 		goto done;
555*22ce4affSfengbojiang 	}
556*22ce4affSfengbojiang 
557*22ce4affSfengbojiang 	MPASS(g_debugnet_pcb_inuse);
558*22ce4affSfengbojiang 
559*22ce4affSfengbojiang 	/* Done ethernet processing. Strip off the ethernet header. */
560*22ce4affSfengbojiang 	m_adj(m, ETHER_HDR_LEN);
561*22ce4affSfengbojiang 	switch (etype) {
562*22ce4affSfengbojiang 	case ETHERTYPE_ARP:
563*22ce4affSfengbojiang 		debugnet_handle_arp(&g_dnet_pcb, &m);
564*22ce4affSfengbojiang 		break;
565*22ce4affSfengbojiang 	case ETHERTYPE_IP:
566*22ce4affSfengbojiang 		debugnet_handle_ip(&g_dnet_pcb, &m);
567*22ce4affSfengbojiang 		break;
568*22ce4affSfengbojiang 	default:
569*22ce4affSfengbojiang 		DNETDEBUG_IF(ifp, "dropping unknown ethertype %hu\n", etype);
570*22ce4affSfengbojiang 		break;
571*22ce4affSfengbojiang 	}
572*22ce4affSfengbojiang done:
573*22ce4affSfengbojiang 	if (m != NULL)
574*22ce4affSfengbojiang 		m_freem(m);
575*22ce4affSfengbojiang }
576*22ce4affSfengbojiang 
577*22ce4affSfengbojiang /*
578*22ce4affSfengbojiang  * Network polling primitive.
579*22ce4affSfengbojiang  *
580*22ce4affSfengbojiang  * Instead of assuming that most of the network stack is sane, we just poll the
581*22ce4affSfengbojiang  * driver directly for packets.
582*22ce4affSfengbojiang  */
583*22ce4affSfengbojiang void
debugnet_network_poll(struct debugnet_pcb * pcb)584*22ce4affSfengbojiang debugnet_network_poll(struct debugnet_pcb *pcb)
585*22ce4affSfengbojiang {
586*22ce4affSfengbojiang 	struct ifnet *ifp;
587*22ce4affSfengbojiang 
588*22ce4affSfengbojiang 	ifp = pcb->dp_ifp;
589*22ce4affSfengbojiang 	ifp->if_debugnet_methods->dn_poll(ifp, 1000);
590*22ce4affSfengbojiang }
591*22ce4affSfengbojiang 
592*22ce4affSfengbojiang /*
593*22ce4affSfengbojiang  * Start of consumer API surface.
594*22ce4affSfengbojiang  */
595*22ce4affSfengbojiang void
debugnet_free(struct debugnet_pcb * pcb)596*22ce4affSfengbojiang debugnet_free(struct debugnet_pcb *pcb)
597*22ce4affSfengbojiang {
598*22ce4affSfengbojiang 	struct ifnet *ifp;
599*22ce4affSfengbojiang 
600*22ce4affSfengbojiang 	MPASS(g_debugnet_pcb_inuse);
601*22ce4affSfengbojiang 	MPASS(pcb == &g_dnet_pcb);
602*22ce4affSfengbojiang 
603*22ce4affSfengbojiang 	ifp = pcb->dp_ifp;
604*22ce4affSfengbojiang 	if (ifp != NULL) {
605*22ce4affSfengbojiang 		if (pcb->dp_drv_input != NULL)
606*22ce4affSfengbojiang 			ifp->if_input = pcb->dp_drv_input;
607*22ce4affSfengbojiang 		if (pcb->dp_event_started)
608*22ce4affSfengbojiang 			ifp->if_debugnet_methods->dn_event(ifp, DEBUGNET_END);
609*22ce4affSfengbojiang 	}
610*22ce4affSfengbojiang 	debugnet_mbuf_finish();
611*22ce4affSfengbojiang 
612*22ce4affSfengbojiang 	g_debugnet_pcb_inuse = false;
613*22ce4affSfengbojiang 	memset(&g_dnet_pcb, 0xfd, sizeof(g_dnet_pcb));
614*22ce4affSfengbojiang }
615*22ce4affSfengbojiang 
616*22ce4affSfengbojiang int
debugnet_connect(const struct debugnet_conn_params * dcp,struct debugnet_pcb ** pcb_out)617*22ce4affSfengbojiang debugnet_connect(const struct debugnet_conn_params *dcp,
618*22ce4affSfengbojiang     struct debugnet_pcb **pcb_out)
619*22ce4affSfengbojiang {
620*22ce4affSfengbojiang 	struct debugnet_proto_aux herald_auxdata;
621*22ce4affSfengbojiang 	struct debugnet_pcb *pcb;
622*22ce4affSfengbojiang 	struct ifnet *ifp;
623*22ce4affSfengbojiang 	int error;
624*22ce4affSfengbojiang 
625*22ce4affSfengbojiang 	if (g_debugnet_pcb_inuse) {
626*22ce4affSfengbojiang 		printf("%s: Only one connection at a time.\n", __func__);
627*22ce4affSfengbojiang 		return (EBUSY);
628*22ce4affSfengbojiang 	}
629*22ce4affSfengbojiang 
630*22ce4affSfengbojiang 	pcb = &g_dnet_pcb;
631*22ce4affSfengbojiang 	*pcb = (struct debugnet_pcb) {
632*22ce4affSfengbojiang 		.dp_state = DN_STATE_INIT,
633*22ce4affSfengbojiang 		.dp_client = dcp->dc_client,
634*22ce4affSfengbojiang 		.dp_server = dcp->dc_server,
635*22ce4affSfengbojiang 		.dp_gateway = dcp->dc_gateway,
636*22ce4affSfengbojiang 		.dp_server_port = dcp->dc_herald_port,	/* Initially */
637*22ce4affSfengbojiang 		.dp_client_port = dcp->dc_client_port,
638*22ce4affSfengbojiang 		.dp_seqno = 1,
639*22ce4affSfengbojiang 		.dp_ifp = dcp->dc_ifp,
640*22ce4affSfengbojiang 		.dp_rx_handler = dcp->dc_rx_handler,
641*22ce4affSfengbojiang 	};
642*22ce4affSfengbojiang 
643*22ce4affSfengbojiang 	/* Switch to the debugnet mbuf zones. */
644*22ce4affSfengbojiang 	debugnet_mbuf_start();
645*22ce4affSfengbojiang 
646*22ce4affSfengbojiang 	/* At least one needed parameter is missing; infer it. */
647*22ce4affSfengbojiang 	if (pcb->dp_client == INADDR_ANY || pcb->dp_gateway == INADDR_ANY ||
648*22ce4affSfengbojiang 	    pcb->dp_ifp == NULL) {
649*22ce4affSfengbojiang 		struct sockaddr_in dest_sin, *gw_sin, *local_sin;
650*22ce4affSfengbojiang 		struct ifnet *rt_ifp;
651*22ce4affSfengbojiang 		struct nhop_object *nh;
652*22ce4affSfengbojiang 
653*22ce4affSfengbojiang 		memset(&dest_sin, 0, sizeof(dest_sin));
654*22ce4affSfengbojiang 		dest_sin = (struct sockaddr_in) {
655*22ce4affSfengbojiang 			.sin_len = sizeof(dest_sin),
656*22ce4affSfengbojiang 			.sin_family = AF_INET,
657*22ce4affSfengbojiang 			.sin_addr.s_addr = pcb->dp_server,
658*22ce4affSfengbojiang 		};
659*22ce4affSfengbojiang 
660*22ce4affSfengbojiang 		CURVNET_SET(vnet0);
661*22ce4affSfengbojiang 		nh = fib4_lookup_debugnet(RT_DEFAULT_FIB, dest_sin.sin_addr, 0,
662*22ce4affSfengbojiang 		    NHR_NONE);
663*22ce4affSfengbojiang 		CURVNET_RESTORE();
664*22ce4affSfengbojiang 
665*22ce4affSfengbojiang 		if (nh == NULL) {
666*22ce4affSfengbojiang 			printf("%s: Could not get route for that server.\n",
667*22ce4affSfengbojiang 			    __func__);
668*22ce4affSfengbojiang 			error = ENOENT;
669*22ce4affSfengbojiang 			goto cleanup;
670*22ce4affSfengbojiang 		}
671*22ce4affSfengbojiang 
672*22ce4affSfengbojiang 		if (nh->gw_sa.sa_family == AF_INET)
673*22ce4affSfengbojiang 			gw_sin = &nh->gw4_sa;
674*22ce4affSfengbojiang 		else {
675*22ce4affSfengbojiang 			if (nh->gw_sa.sa_family == AF_LINK)
676*22ce4affSfengbojiang 				DNETDEBUG("Destination address is on link.\n");
677*22ce4affSfengbojiang 			gw_sin = NULL;
678*22ce4affSfengbojiang 		}
679*22ce4affSfengbojiang 
680*22ce4affSfengbojiang 		MPASS(nh->nh_ifa->ifa_addr->sa_family == AF_INET);
681*22ce4affSfengbojiang 		local_sin = (struct sockaddr_in *)nh->nh_ifa->ifa_addr;
682*22ce4affSfengbojiang 
683*22ce4affSfengbojiang 		rt_ifp = nh->nh_ifp;
684*22ce4affSfengbojiang 
685*22ce4affSfengbojiang 		if (pcb->dp_client == INADDR_ANY)
686*22ce4affSfengbojiang 			pcb->dp_client = local_sin->sin_addr.s_addr;
687*22ce4affSfengbojiang 		if (pcb->dp_gateway == INADDR_ANY && gw_sin != NULL)
688*22ce4affSfengbojiang 			pcb->dp_gateway = gw_sin->sin_addr.s_addr;
689*22ce4affSfengbojiang 		if (pcb->dp_ifp == NULL)
690*22ce4affSfengbojiang 			pcb->dp_ifp = rt_ifp;
691*22ce4affSfengbojiang 	}
692*22ce4affSfengbojiang 
693*22ce4affSfengbojiang 	ifp = pcb->dp_ifp;
694*22ce4affSfengbojiang 
695*22ce4affSfengbojiang 	if (debugnet_debug > 0) {
696*22ce4affSfengbojiang 		char serbuf[INET_ADDRSTRLEN], clibuf[INET_ADDRSTRLEN],
697*22ce4affSfengbojiang 		    gwbuf[INET_ADDRSTRLEN];
698*22ce4affSfengbojiang 		inet_ntop(AF_INET, &pcb->dp_server, serbuf, sizeof(serbuf));
699*22ce4affSfengbojiang 		inet_ntop(AF_INET, &pcb->dp_client, clibuf, sizeof(clibuf));
700*22ce4affSfengbojiang 		if (pcb->dp_gateway != INADDR_ANY)
701*22ce4affSfengbojiang 			inet_ntop(AF_INET, &pcb->dp_gateway, gwbuf, sizeof(gwbuf));
702*22ce4affSfengbojiang 		DNETDEBUG("Connecting to %s:%d%s%s from %s:%d on %s\n",
703*22ce4affSfengbojiang 		    serbuf, pcb->dp_server_port,
704*22ce4affSfengbojiang 		    (pcb->dp_gateway == INADDR_ANY) ? "" : " via ",
705*22ce4affSfengbojiang 		    (pcb->dp_gateway == INADDR_ANY) ? "" : gwbuf,
706*22ce4affSfengbojiang 		    clibuf, pcb->dp_client_port, if_name(ifp));
707*22ce4affSfengbojiang 	}
708*22ce4affSfengbojiang 
709*22ce4affSfengbojiang 	/* Validate iface is online and supported. */
710*22ce4affSfengbojiang 	if (!DEBUGNET_SUPPORTED_NIC(ifp)) {
711*22ce4affSfengbojiang 		printf("%s: interface '%s' does not support debugnet\n",
712*22ce4affSfengbojiang 		    __func__, if_name(ifp));
713*22ce4affSfengbojiang 		error = ENODEV;
714*22ce4affSfengbojiang 		goto cleanup;
715*22ce4affSfengbojiang 	}
716*22ce4affSfengbojiang 	if ((if_getflags(ifp) & IFF_UP) == 0) {
717*22ce4affSfengbojiang 		printf("%s: interface '%s' link is down\n", __func__,
718*22ce4affSfengbojiang 		    if_name(ifp));
719*22ce4affSfengbojiang 		error = ENXIO;
720*22ce4affSfengbojiang 		goto cleanup;
721*22ce4affSfengbojiang 	}
722*22ce4affSfengbojiang 
723*22ce4affSfengbojiang 	ifp->if_debugnet_methods->dn_event(ifp, DEBUGNET_START);
724*22ce4affSfengbojiang 	pcb->dp_event_started = true;
725*22ce4affSfengbojiang 
726*22ce4affSfengbojiang 	/*
727*22ce4affSfengbojiang 	 * We maintain the invariant that g_debugnet_pcb_inuse is always true
728*22ce4affSfengbojiang 	 * while the debugnet ifp's if_input is overridden with
729*22ce4affSfengbojiang 	 * debugnet_pkt_in.
730*22ce4affSfengbojiang 	 */
731*22ce4affSfengbojiang 	g_debugnet_pcb_inuse = true;
732*22ce4affSfengbojiang 
733*22ce4affSfengbojiang 	/* Make the card use *our* receive callback. */
734*22ce4affSfengbojiang 	pcb->dp_drv_input = ifp->if_input;
735*22ce4affSfengbojiang 	ifp->if_input = debugnet_pkt_in;
736*22ce4affSfengbojiang 
737*22ce4affSfengbojiang 	printf("%s: searching for %s MAC...\n", __func__,
738*22ce4affSfengbojiang 	    (dcp->dc_gateway == INADDR_ANY) ? "server" : "gateway");
739*22ce4affSfengbojiang 
740*22ce4affSfengbojiang 	error = debugnet_arp_gw(pcb);
741*22ce4affSfengbojiang 	if (error != 0) {
742*22ce4affSfengbojiang 		printf("%s: failed to locate MAC address\n", __func__);
743*22ce4affSfengbojiang 		goto cleanup;
744*22ce4affSfengbojiang 	}
745*22ce4affSfengbojiang 	MPASS(pcb->dp_state == DN_STATE_HAVE_GW_MAC);
746*22ce4affSfengbojiang 
747*22ce4affSfengbojiang 	herald_auxdata = (struct debugnet_proto_aux) {
748*22ce4affSfengbojiang 		.dp_offset_start = dcp->dc_herald_offset,
749*22ce4affSfengbojiang 		.dp_aux2 = dcp->dc_herald_aux2,
750*22ce4affSfengbojiang 	};
751*22ce4affSfengbojiang 	error = debugnet_send(pcb, DEBUGNET_HERALD, dcp->dc_herald_data,
752*22ce4affSfengbojiang 	    dcp->dc_herald_datalen, &herald_auxdata);
753*22ce4affSfengbojiang 	if (error != 0) {
754*22ce4affSfengbojiang 		printf("%s: failed to herald debugnet server\n", __func__);
755*22ce4affSfengbojiang 		goto cleanup;
756*22ce4affSfengbojiang 	}
757*22ce4affSfengbojiang 
758*22ce4affSfengbojiang 	*pcb_out = pcb;
759*22ce4affSfengbojiang 	return (0);
760*22ce4affSfengbojiang 
761*22ce4affSfengbojiang cleanup:
762*22ce4affSfengbojiang 	debugnet_free(pcb);
763*22ce4affSfengbojiang 	return (error);
764*22ce4affSfengbojiang }
765*22ce4affSfengbojiang 
766*22ce4affSfengbojiang /*
767*22ce4affSfengbojiang  * Pre-allocated dump-time mbuf tracking.
768*22ce4affSfengbojiang  *
769*22ce4affSfengbojiang  * We just track the high water mark we've ever seen and allocate appropriately
770*22ce4affSfengbojiang  * for that iface/mtu combo.
771*22ce4affSfengbojiang  */
772*22ce4affSfengbojiang static struct {
773*22ce4affSfengbojiang 	int nmbuf;
774*22ce4affSfengbojiang 	int ncl;
775*22ce4affSfengbojiang 	int clsize;
776*22ce4affSfengbojiang } dn_hwm;
777*22ce4affSfengbojiang static struct mtx dn_hwm_lk;
778*22ce4affSfengbojiang MTX_SYSINIT(debugnet_hwm_lock, &dn_hwm_lk, "Debugnet HWM lock", MTX_DEF);
779*22ce4affSfengbojiang 
780*22ce4affSfengbojiang static void
dn_maybe_reinit_mbufs(int nmbuf,int ncl,int clsize)781*22ce4affSfengbojiang dn_maybe_reinit_mbufs(int nmbuf, int ncl, int clsize)
782*22ce4affSfengbojiang {
783*22ce4affSfengbojiang 	bool any;
784*22ce4affSfengbojiang 
785*22ce4affSfengbojiang 	any = false;
786*22ce4affSfengbojiang 	mtx_lock(&dn_hwm_lk);
787*22ce4affSfengbojiang 
788*22ce4affSfengbojiang 	if (nmbuf > dn_hwm.nmbuf) {
789*22ce4affSfengbojiang 		any = true;
790*22ce4affSfengbojiang 		dn_hwm.nmbuf = nmbuf;
791*22ce4affSfengbojiang 	} else
792*22ce4affSfengbojiang 		nmbuf = dn_hwm.nmbuf;
793*22ce4affSfengbojiang 
794*22ce4affSfengbojiang 	if (ncl > dn_hwm.ncl) {
795*22ce4affSfengbojiang 		any = true;
796*22ce4affSfengbojiang 		dn_hwm.ncl = ncl;
797*22ce4affSfengbojiang 	} else
798*22ce4affSfengbojiang 		ncl = dn_hwm.ncl;
799*22ce4affSfengbojiang 
800*22ce4affSfengbojiang 	if (clsize > dn_hwm.clsize) {
801*22ce4affSfengbojiang 		any = true;
802*22ce4affSfengbojiang 		dn_hwm.clsize = clsize;
803*22ce4affSfengbojiang 	} else
804*22ce4affSfengbojiang 		clsize = dn_hwm.clsize;
805*22ce4affSfengbojiang 
806*22ce4affSfengbojiang 	mtx_unlock(&dn_hwm_lk);
807*22ce4affSfengbojiang 
808*22ce4affSfengbojiang 	if (any)
809*22ce4affSfengbojiang 		debugnet_mbuf_reinit(nmbuf, ncl, clsize);
810*22ce4affSfengbojiang }
811*22ce4affSfengbojiang 
812*22ce4affSfengbojiang void
debugnet_any_ifnet_update(struct ifnet * ifp)813*22ce4affSfengbojiang debugnet_any_ifnet_update(struct ifnet *ifp)
814*22ce4affSfengbojiang {
815*22ce4affSfengbojiang 	int clsize, nmbuf, ncl, nrxr;
816*22ce4affSfengbojiang 
817*22ce4affSfengbojiang 	if (!DEBUGNET_SUPPORTED_NIC(ifp))
818*22ce4affSfengbojiang 		return;
819*22ce4affSfengbojiang 
820*22ce4affSfengbojiang 	ifp->if_debugnet_methods->dn_init(ifp, &nrxr, &ncl, &clsize);
821*22ce4affSfengbojiang 	KASSERT(nrxr > 0, ("invalid receive ring count %d", nrxr));
822*22ce4affSfengbojiang 
823*22ce4affSfengbojiang 	/*
824*22ce4affSfengbojiang 	 * We need two headers per message on the transmit side. Multiply by
825*22ce4affSfengbojiang 	 * four to give us some breathing room.
826*22ce4affSfengbojiang 	 */
827*22ce4affSfengbojiang 	nmbuf = ncl * (4 + nrxr);
828*22ce4affSfengbojiang 	ncl *= nrxr;
829*22ce4affSfengbojiang 
830*22ce4affSfengbojiang 	/*
831*22ce4affSfengbojiang 	 * Bandaid for drivers that (incorrectly) advertise LinkUp before their
832*22ce4affSfengbojiang 	 * dn_init method is available.
833*22ce4affSfengbojiang 	 */
834*22ce4affSfengbojiang 	if (nmbuf == 0 || ncl == 0 || clsize == 0) {
835*22ce4affSfengbojiang 		printf("%s: Bad dn_init result from %s (ifp %p), ignoring.\n",
836*22ce4affSfengbojiang 		    __func__, if_name(ifp), ifp);
837*22ce4affSfengbojiang 		return;
838*22ce4affSfengbojiang 	}
839*22ce4affSfengbojiang 	dn_maybe_reinit_mbufs(nmbuf, ncl, clsize);
840*22ce4affSfengbojiang }
841*22ce4affSfengbojiang 
842*22ce4affSfengbojiang /*
843*22ce4affSfengbojiang  * Unfortunately, the ifnet_arrival_event eventhandler hook is mostly useless
844*22ce4affSfengbojiang  * for us because drivers tend to if_attach before invoking DEBUGNET_SET().
845*22ce4affSfengbojiang  *
846*22ce4affSfengbojiang  * On the other hand, hooking DEBUGNET_SET() itself may still be too early,
847*22ce4affSfengbojiang  * because the driver is still in attach.  Since we cannot use down interfaces,
848*22ce4affSfengbojiang  * maybe hooking ifnet_event:IFNET_EVENT_UP is sufficient?  ... Nope, at least
849*22ce4affSfengbojiang  * with vtnet and dhcpclient that event just never occurs.
850*22ce4affSfengbojiang  *
851*22ce4affSfengbojiang  * So that's how I've landed on the lower level ifnet_link_event.
852*22ce4affSfengbojiang  */
853*22ce4affSfengbojiang 
854*22ce4affSfengbojiang static void
dn_ifnet_event(void * arg __unused,struct ifnet * ifp,int link_state)855*22ce4affSfengbojiang dn_ifnet_event(void *arg __unused, struct ifnet *ifp, int link_state)
856*22ce4affSfengbojiang {
857*22ce4affSfengbojiang 	if (link_state == LINK_STATE_UP)
858*22ce4affSfengbojiang 		debugnet_any_ifnet_update(ifp);
859*22ce4affSfengbojiang }
860*22ce4affSfengbojiang 
861*22ce4affSfengbojiang static eventhandler_tag dn_attach_cookie;
862*22ce4affSfengbojiang static void
dn_evh_init(void * ctx __unused)863*22ce4affSfengbojiang dn_evh_init(void *ctx __unused)
864*22ce4affSfengbojiang {
865*22ce4affSfengbojiang 	dn_attach_cookie = EVENTHANDLER_REGISTER(ifnet_link_event,
866*22ce4affSfengbojiang 	    dn_ifnet_event, NULL, EVENTHANDLER_PRI_ANY);
867*22ce4affSfengbojiang }
868*22ce4affSfengbojiang SYSINIT(dn_evh_init, SI_SUB_EVENTHANDLER + 1, SI_ORDER_ANY, dn_evh_init, NULL);
869*22ce4affSfengbojiang 
870*22ce4affSfengbojiang /*
871*22ce4affSfengbojiang  * DDB parsing helpers for debugnet(4) consumers.
872*22ce4affSfengbojiang  */
873*22ce4affSfengbojiang #ifdef DDB
874*22ce4affSfengbojiang struct my_inet_opt {
875*22ce4affSfengbojiang 	bool has_opt;
876*22ce4affSfengbojiang 	const char *printname;
877*22ce4affSfengbojiang 	in_addr_t *result;
878*22ce4affSfengbojiang };
879*22ce4affSfengbojiang 
880*22ce4affSfengbojiang static int
dn_parse_optarg_ipv4(struct my_inet_opt * opt)881*22ce4affSfengbojiang dn_parse_optarg_ipv4(struct my_inet_opt *opt)
882*22ce4affSfengbojiang {
883*22ce4affSfengbojiang 	in_addr_t tmp;
884*22ce4affSfengbojiang 	unsigned octet;
885*22ce4affSfengbojiang 	int t;
886*22ce4affSfengbojiang 
887*22ce4affSfengbojiang 	tmp = 0;
888*22ce4affSfengbojiang 	for (octet = 0; octet < 4; octet++) {
889*22ce4affSfengbojiang 		t = db_read_token_flags(DRT_WSPACE | DRT_DECIMAL);
890*22ce4affSfengbojiang 		if (t != tNUMBER) {
891*22ce4affSfengbojiang 			db_printf("%s:%s: octet %u expected number; found %d\n",
892*22ce4affSfengbojiang 			    __func__, opt->printname, octet, t);
893*22ce4affSfengbojiang 			return (EINVAL);
894*22ce4affSfengbojiang 		}
895*22ce4affSfengbojiang 		/*
896*22ce4affSfengbojiang 		 * db_lex lexes '-' distinctly from the number itself, but
897*22ce4affSfengbojiang 		 * let's document that invariant.
898*22ce4affSfengbojiang 		 */
899*22ce4affSfengbojiang 		MPASS(db_tok_number >= 0);
900*22ce4affSfengbojiang 
901*22ce4affSfengbojiang 		if (db_tok_number > UINT8_MAX) {
902*22ce4affSfengbojiang 			db_printf("%s:%s: octet %u out of range: %jd\n", __func__,
903*22ce4affSfengbojiang 			    opt->printname, octet, (intmax_t)db_tok_number);
904*22ce4affSfengbojiang 			return (EDOM);
905*22ce4affSfengbojiang 		}
906*22ce4affSfengbojiang 
907*22ce4affSfengbojiang 		/* Constructed host-endian and converted to network later. */
908*22ce4affSfengbojiang 		tmp = (tmp << 8) | db_tok_number;
909*22ce4affSfengbojiang 
910*22ce4affSfengbojiang 		if (octet < 3) {
911*22ce4affSfengbojiang 			t = db_read_token_flags(DRT_WSPACE);
912*22ce4affSfengbojiang 			if (t != tDOT) {
913*22ce4affSfengbojiang 				db_printf("%s:%s: octet %u expected '.'; found"
914*22ce4affSfengbojiang 				    " %d\n", __func__, opt->printname, octet,
915*22ce4affSfengbojiang 				    t);
916*22ce4affSfengbojiang 				return (EINVAL);
917*22ce4affSfengbojiang 			}
918*22ce4affSfengbojiang 		}
919*22ce4affSfengbojiang 	}
920*22ce4affSfengbojiang 
921*22ce4affSfengbojiang 	*opt->result = htonl(tmp);
922*22ce4affSfengbojiang 	opt->has_opt = true;
923*22ce4affSfengbojiang 	return (0);
924*22ce4affSfengbojiang }
925*22ce4affSfengbojiang 
926*22ce4affSfengbojiang int
debugnet_parse_ddb_cmd(const char * cmd,struct debugnet_ddb_config * result)927*22ce4affSfengbojiang debugnet_parse_ddb_cmd(const char *cmd, struct debugnet_ddb_config *result)
928*22ce4affSfengbojiang {
929*22ce4affSfengbojiang 	struct ifnet *ifp;
930*22ce4affSfengbojiang 	int t, error;
931*22ce4affSfengbojiang 	bool want_ifp;
932*22ce4affSfengbojiang 	char ch;
933*22ce4affSfengbojiang 
934*22ce4affSfengbojiang 	struct my_inet_opt opt_client = {
935*22ce4affSfengbojiang 		.printname = "client",
936*22ce4affSfengbojiang 		.result = &result->dd_client,
937*22ce4affSfengbojiang 	},
938*22ce4affSfengbojiang 	opt_server = {
939*22ce4affSfengbojiang 		.printname = "server",
940*22ce4affSfengbojiang 		.result = &result->dd_server,
941*22ce4affSfengbojiang 	},
942*22ce4affSfengbojiang 	opt_gateway = {
943*22ce4affSfengbojiang 		.printname = "gateway",
944*22ce4affSfengbojiang 		.result = &result->dd_gateway,
945*22ce4affSfengbojiang 	},
946*22ce4affSfengbojiang 	*cur_inet_opt;
947*22ce4affSfengbojiang 
948*22ce4affSfengbojiang 	ifp = NULL;
949*22ce4affSfengbojiang 	memset(result, 0, sizeof(*result));
950*22ce4affSfengbojiang 
951*22ce4affSfengbojiang 	/*
952*22ce4affSfengbojiang 	 * command [space] [-] [opt] [[space] [optarg]] ...
953*22ce4affSfengbojiang 	 *
954*22ce4affSfengbojiang 	 * db_command has already lexed 'command' for us.
955*22ce4affSfengbojiang 	 */
956*22ce4affSfengbojiang 	t = db_read_token_flags(DRT_WSPACE);
957*22ce4affSfengbojiang 	if (t == tWSPACE)
958*22ce4affSfengbojiang 		t = db_read_token_flags(DRT_WSPACE);
959*22ce4affSfengbojiang 
960*22ce4affSfengbojiang 	while (t != tEOL) {
961*22ce4affSfengbojiang 		if (t != tMINUS) {
962*22ce4affSfengbojiang 			db_printf("%s: Bad syntax; expected '-', got %d\n",
963*22ce4affSfengbojiang 			    cmd, t);
964*22ce4affSfengbojiang 			goto usage;
965*22ce4affSfengbojiang 		}
966*22ce4affSfengbojiang 
967*22ce4affSfengbojiang 		t = db_read_token_flags(DRT_WSPACE);
968*22ce4affSfengbojiang 		if (t != tIDENT) {
969*22ce4affSfengbojiang 			db_printf("%s: Bad syntax; expected tIDENT, got %d\n",
970*22ce4affSfengbojiang 			    cmd, t);
971*22ce4affSfengbojiang 			goto usage;
972*22ce4affSfengbojiang 		}
973*22ce4affSfengbojiang 
974*22ce4affSfengbojiang 		if (strlen(db_tok_string) > 1) {
975*22ce4affSfengbojiang 			db_printf("%s: Bad syntax; expected single option "
976*22ce4affSfengbojiang 			    "flag, got '%s'\n", cmd, db_tok_string);
977*22ce4affSfengbojiang 			goto usage;
978*22ce4affSfengbojiang 		}
979*22ce4affSfengbojiang 
980*22ce4affSfengbojiang 		want_ifp = false;
981*22ce4affSfengbojiang 		cur_inet_opt = NULL;
982*22ce4affSfengbojiang 		switch ((ch = db_tok_string[0])) {
983*22ce4affSfengbojiang 		default:
984*22ce4affSfengbojiang 			DNETDEBUG("Unexpected: '%c'\n", ch);
985*22ce4affSfengbojiang 			/* FALLTHROUGH */
986*22ce4affSfengbojiang 		case 'h':
987*22ce4affSfengbojiang 			goto usage;
988*22ce4affSfengbojiang 		case 'c':
989*22ce4affSfengbojiang 			cur_inet_opt = &opt_client;
990*22ce4affSfengbojiang 			break;
991*22ce4affSfengbojiang 		case 'g':
992*22ce4affSfengbojiang 			cur_inet_opt = &opt_gateway;
993*22ce4affSfengbojiang 			break;
994*22ce4affSfengbojiang 		case 's':
995*22ce4affSfengbojiang 			cur_inet_opt = &opt_server;
996*22ce4affSfengbojiang 			break;
997*22ce4affSfengbojiang 		case 'i':
998*22ce4affSfengbojiang 			want_ifp = true;
999*22ce4affSfengbojiang 			break;
1000*22ce4affSfengbojiang 		}
1001*22ce4affSfengbojiang 
1002*22ce4affSfengbojiang 		t = db_read_token_flags(DRT_WSPACE);
1003*22ce4affSfengbojiang 		if (t != tWSPACE) {
1004*22ce4affSfengbojiang 			db_printf("%s: Bad syntax; expected space after "
1005*22ce4affSfengbojiang 			    "flag %c, got %d\n", cmd, ch, t);
1006*22ce4affSfengbojiang 			goto usage;
1007*22ce4affSfengbojiang 		}
1008*22ce4affSfengbojiang 
1009*22ce4affSfengbojiang 		if (want_ifp) {
1010*22ce4affSfengbojiang 			t = db_read_token_flags(DRT_WSPACE);
1011*22ce4affSfengbojiang 			if (t != tIDENT) {
1012*22ce4affSfengbojiang 				db_printf("%s: Expected interface but got %d\n",
1013*22ce4affSfengbojiang 				    cmd, t);
1014*22ce4affSfengbojiang 				goto usage;
1015*22ce4affSfengbojiang 			}
1016*22ce4affSfengbojiang 
1017*22ce4affSfengbojiang 			CURVNET_SET(vnet0);
1018*22ce4affSfengbojiang 			/*
1019*22ce4affSfengbojiang 			 * We *don't* take a ref here because the only current
1020*22ce4affSfengbojiang 			 * consumer, db_netdump_cmd, does not need it.  It
1021*22ce4affSfengbojiang 			 * (somewhat redundantly) extracts the if_name(),
1022*22ce4affSfengbojiang 			 * re-lookups the ifp, and takes its own reference.
1023*22ce4affSfengbojiang 			 */
1024*22ce4affSfengbojiang 			ifp = ifunit(db_tok_string);
1025*22ce4affSfengbojiang 			CURVNET_RESTORE();
1026*22ce4affSfengbojiang 			if (ifp == NULL) {
1027*22ce4affSfengbojiang 				db_printf("Could not locate interface %s\n",
1028*22ce4affSfengbojiang 				    db_tok_string);
1029*22ce4affSfengbojiang 				goto cleanup;
1030*22ce4affSfengbojiang 			}
1031*22ce4affSfengbojiang 		} else {
1032*22ce4affSfengbojiang 			MPASS(cur_inet_opt != NULL);
1033*22ce4affSfengbojiang 			/* Assume IPv4 for now. */
1034*22ce4affSfengbojiang 			error = dn_parse_optarg_ipv4(cur_inet_opt);
1035*22ce4affSfengbojiang 			if (error != 0)
1036*22ce4affSfengbojiang 				goto cleanup;
1037*22ce4affSfengbojiang 		}
1038*22ce4affSfengbojiang 
1039*22ce4affSfengbojiang 		/* Skip (mandatory) whitespace after option, if not EOL. */
1040*22ce4affSfengbojiang 		t = db_read_token_flags(DRT_WSPACE);
1041*22ce4affSfengbojiang 		if (t == tEOL)
1042*22ce4affSfengbojiang 			break;
1043*22ce4affSfengbojiang 		if (t != tWSPACE) {
1044*22ce4affSfengbojiang 			db_printf("%s: Bad syntax; expected space after "
1045*22ce4affSfengbojiang 			    "flag %c option; got %d\n", cmd, ch, t);
1046*22ce4affSfengbojiang 			goto usage;
1047*22ce4affSfengbojiang 		}
1048*22ce4affSfengbojiang 		t = db_read_token_flags(DRT_WSPACE);
1049*22ce4affSfengbojiang 	}
1050*22ce4affSfengbojiang 
1051*22ce4affSfengbojiang 	if (!opt_server.has_opt) {
1052*22ce4affSfengbojiang 		db_printf("%s: need a destination server address\n", cmd);
1053*22ce4affSfengbojiang 		goto usage;
1054*22ce4affSfengbojiang 	}
1055*22ce4affSfengbojiang 
1056*22ce4affSfengbojiang 	result->dd_has_client = opt_client.has_opt;
1057*22ce4affSfengbojiang 	result->dd_has_gateway = opt_gateway.has_opt;
1058*22ce4affSfengbojiang 	result->dd_ifp = ifp;
1059*22ce4affSfengbojiang 
1060*22ce4affSfengbojiang 	/* We parsed the full line to tEOL already, or bailed with an error. */
1061*22ce4affSfengbojiang 	return (0);
1062*22ce4affSfengbojiang 
1063*22ce4affSfengbojiang usage:
1064*22ce4affSfengbojiang 	db_printf("Usage: %s -s <server> [-g <gateway> -c <localip> "
1065*22ce4affSfengbojiang 	    "-i <interface>]\n", cmd);
1066*22ce4affSfengbojiang 	error = EINVAL;
1067*22ce4affSfengbojiang 	/* FALLTHROUGH */
1068*22ce4affSfengbojiang cleanup:
1069*22ce4affSfengbojiang 	db_skip_to_eol();
1070*22ce4affSfengbojiang 	return (error);
1071*22ce4affSfengbojiang }
1072*22ce4affSfengbojiang #endif /* DDB */
1073