2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2019 Isilon Systems, LLC.
5 * Copyright (c) 2005-2014 Sandvine Incorporated. All rights reserved.
6 * Copyright (c) 2000 Darrell Anderson
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/cdefs.h>
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/endian.h>
38 #include <sys/errno.h>
39 #include <sys/eventhandler.h>
40 #include <sys/kernel.h>
42 #include <sys/mutex.h>
43 #include <sys/socket.h>
44 #include <sys/sysctl.h>
48 #include <ddb/db_lex.h>
51 #include <net/ethernet.h>
53 #include <net/if_arp.h>
54 #include <net/if_dl.h>
55 #include <net/if_types.h>
56 #include <net/if_var.h>
57 #include <net/if_private.h>
59 #include <net/route.h>
60 #include <net/route/nhop.h>
62 #include <netinet/in.h>
63 #include <netinet/in_fib.h>
64 #include <netinet/in_systm.h>
65 #include <netinet/in_var.h>
66 #include <netinet/ip.h>
67 #include <netinet/ip_var.h>
68 #include <netinet/ip_options.h>
69 #include <netinet/udp.h>
70 #include <netinet/udp_var.h>
72 #include <machine/in_cksum.h>
73 #include <machine/pcb.h>
75 #include <net/debugnet.h>
76 #define DEBUGNET_INTERNAL
77 #include <net/debugnet_int.h>
79 FEATURE(debugnet
, "Debugnet support");
81 SYSCTL_NODE(_net
, OID_AUTO
, debugnet
, CTLFLAG_RD
| CTLFLAG_MPSAFE
, NULL
,
82 "debugnet parameters");
84 unsigned debugnet_debug
;
85 SYSCTL_UINT(_net_debugnet
, OID_AUTO
, debug
, CTLFLAG_RWTUN
,
87 "Debug message verbosity (0: off; 1: on; 2: verbose)");
89 int debugnet_npolls
= 2000;
90 SYSCTL_INT(_net_debugnet
, OID_AUTO
, npolls
, CTLFLAG_RWTUN
,
92 "Number of times to poll before assuming packet loss (0.5ms per poll)");
93 int debugnet_nretries
= 10;
94 SYSCTL_INT(_net_debugnet
, OID_AUTO
, nretries
, CTLFLAG_RWTUN
,
95 &debugnet_nretries
, 0,
96 "Number of retransmit attempts before giving up");
97 int debugnet_fib
= RT_DEFAULT_FIB
;
98 SYSCTL_INT(_net_debugnet
, OID_AUTO
, fib
, CTLFLAG_RWTUN
,
100 "Fib to use when sending dump");
102 static bool g_debugnet_pcb_inuse
;
103 static struct debugnet_pcb g_dnet_pcb
;
106 * Simple accessors for opaque PCB.
108 const unsigned char *
109 debugnet_get_gw_mac(const struct debugnet_pcb
*pcb
)
111 MPASS(g_debugnet_pcb_inuse
&& pcb
== &g_dnet_pcb
&&
112 pcb
->dp_state
>= DN_STATE_HAVE_GW_MAC
);
113 return (pcb
->dp_gw_mac
.octet
);
117 debugnet_get_server_addr(const struct debugnet_pcb
*pcb
)
119 MPASS(g_debugnet_pcb_inuse
&& pcb
== &g_dnet_pcb
&&
120 pcb
->dp_state
>= DN_STATE_GOT_HERALD_PORT
);
121 return (&pcb
->dp_server
);
125 debugnet_get_server_port(const struct debugnet_pcb
*pcb
)
127 MPASS(g_debugnet_pcb_inuse
&& pcb
== &g_dnet_pcb
&&
128 pcb
->dp_state
>= DN_STATE_GOT_HERALD_PORT
);
129 return (pcb
->dp_server_port
);
133 * Start of network primitives, beginning with output primitives.
137 * Handles creation of the ethernet header, then places outgoing packets into
138 * the tx buffer for the NIC
141 * m The mbuf containing the packet to be sent (will be freed by
142 * this function or the NIC driver)
143 * ifp The interface to send on
144 * dst The destination ethernet address (source address will be looked
146 * etype The ETHERTYPE_* value for the protocol that is being sent
149 * int see errno.h, 0 for success
152 debugnet_ether_output(struct mbuf
*m
, struct ifnet
*ifp
, struct ether_addr dst
,
155 struct ether_header
*eh
;
157 if (((ifp
->if_flags
& (IFF_MONITOR
| IFF_UP
)) != IFF_UP
) ||
158 (ifp
->if_drv_flags
& IFF_DRV_RUNNING
) != IFF_DRV_RUNNING
) {
159 if_printf(ifp
, "%s: interface isn't up\n", __func__
);
164 /* Fill in the ethernet header. */
165 M_PREPEND(m
, ETHER_HDR_LEN
, M_NOWAIT
);
167 printf("%s: out of mbufs\n", __func__
);
170 eh
= mtod(m
, struct ether_header
*);
171 memcpy(eh
->ether_shost
, IF_LLADDR(ifp
), ETHER_ADDR_LEN
);
172 memcpy(eh
->ether_dhost
, dst
.octet
, ETHER_ADDR_LEN
);
173 eh
->ether_type
= htons(etype
);
174 return (ifp
->if_debugnet_methods
->dn_transmit(ifp
, m
));
178 * Unreliable transmission of an mbuf chain to the debugnet server
179 * Note: can't handle fragmentation; fails if the packet is larger than
180 * ifp->if_mtu after adding the UDP/IP headers
183 * pcb The debugnet context block
187 * int see errno.h, 0 for success
190 debugnet_udp_output(struct debugnet_pcb
*pcb
, struct mbuf
*m
)
194 MPASS(pcb
->dp_state
>= DN_STATE_HAVE_GW_MAC
);
196 M_PREPEND(m
, sizeof(*udp
), M_NOWAIT
);
198 printf("%s: out of mbufs\n", __func__
);
202 udp
= mtod(m
, struct udphdr
*);
203 udp
->uh_ulen
= htons(m
->m_pkthdr
.len
);
204 /* Use this src port so that the server can connect() the socket */
205 udp
->uh_sport
= htons(pcb
->dp_client_port
);
206 udp
->uh_dport
= htons(pcb
->dp_server_port
);
207 /* Computed later (protocol-dependent). */
210 return (debugnet_ip_output(pcb
, m
));
214 debugnet_ack_output(struct debugnet_pcb
*pcb
, uint32_t seqno
/* net endian */)
216 struct debugnet_ack
*dn_ack
;
219 DNETDEBUG("Acking with seqno %u\n", ntohl(seqno
));
221 m
= m_gethdr(M_NOWAIT
, MT_DATA
);
223 printf("%s: Out of mbufs\n", __func__
);
226 m
->m_len
= sizeof(*dn_ack
);
227 m
->m_pkthdr
.len
= sizeof(*dn_ack
);
228 MH_ALIGN(m
, sizeof(*dn_ack
));
229 dn_ack
= mtod(m
, struct debugnet_ack
*);
230 dn_ack
->da_seqno
= seqno
;
232 return (debugnet_udp_output(pcb
, m
));
236 * Dummy free function for debugnet clusters.
239 debugnet_mbuf_free(struct mbuf
*m __unused
)
244 * Construct and reliably send a debugnet packet. May fail from a resource
245 * shortage or extreme number of unacknowledged retransmissions. Wait for
246 * an acknowledgement before returning. Splits packets into chunks small
247 * enough to be sent without fragmentation (looks up the interface MTU)
250 * type debugnet packet type (HERALD, FINISHED, ...)
252 * datalen data size (bytes)
253 * auxdata optional auxiliary information
256 * int see errno.h, 0 for success
259 debugnet_send(struct debugnet_pcb
*pcb
, uint32_t type
, const void *data
,
260 uint32_t datalen
, const struct debugnet_proto_aux
*auxdata
)
262 struct debugnet_msg_hdr
*dn_msg_hdr
;
265 uint32_t i
, pktlen
, sent_so_far
;
266 int retries
, polls
, error
;
268 if (pcb
->dp_state
== DN_STATE_REMOTE_CLOSED
)
272 pcb
->dp_rcvd_acks
= 0;
276 /* Chunks can be too big to fit in packets. */
277 for (i
= sent_so_far
= 0; sent_so_far
< datalen
||
278 (i
== 0 && datalen
== 0); i
++) {
279 pktlen
= datalen
- sent_so_far
;
281 /* Bound: the interface MTU (assume no IP options). */
282 pktlen
= min(pktlen
, pcb
->dp_ifp
->if_mtu
-
283 sizeof(struct udpiphdr
) - sizeof(struct debugnet_msg_hdr
));
286 * Check if it is retransmitting and this has been ACKed
289 if ((pcb
->dp_rcvd_acks
& (1 << i
)) != 0) {
290 sent_so_far
+= pktlen
;
295 * Get and fill a header mbuf, then chain data as an extended
298 m
= m_gethdr(M_NOWAIT
, MT_DATA
);
300 printf("%s: Out of mbufs\n", __func__
);
303 m
->m_len
= sizeof(struct debugnet_msg_hdr
);
304 m
->m_pkthdr
.len
= sizeof(struct debugnet_msg_hdr
);
305 MH_ALIGN(m
, sizeof(struct debugnet_msg_hdr
));
306 dn_msg_hdr
= mtod(m
, struct debugnet_msg_hdr
*);
307 dn_msg_hdr
->mh_seqno
= htonl(pcb
->dp_seqno
+ i
);
308 dn_msg_hdr
->mh_type
= htonl(type
);
309 dn_msg_hdr
->mh_len
= htonl(pktlen
);
311 if (auxdata
!= NULL
) {
312 dn_msg_hdr
->mh_offset
=
313 htobe64(auxdata
->dp_offset_start
+ sent_so_far
);
314 dn_msg_hdr
->mh_aux2
= htobe32(auxdata
->dp_aux2
);
316 dn_msg_hdr
->mh_offset
= htobe64(sent_so_far
);
317 dn_msg_hdr
->mh_aux2
= 0;
321 m2
= m_get(M_NOWAIT
, MT_DATA
);
324 printf("%s: Out of mbufs\n", __func__
);
327 MEXTADD(m2
, __DECONST(char *, data
) + sent_so_far
,
328 pktlen
, debugnet_mbuf_free
, NULL
, NULL
, 0,
333 m
->m_pkthdr
.len
+= pktlen
;
335 error
= debugnet_udp_output(pcb
, m
);
339 /* Note that we're waiting for this packet in the bitfield. */
340 want_acks
|= (1 << i
);
341 sent_so_far
+= pktlen
;
343 if (i
>= DEBUGNET_MAX_IN_FLIGHT
)
344 printf("Warning: Sent more than %d packets (%d). "
345 "Acknowledgements will fail unless the size of "
346 "rcvd_acks/want_acks is increased.\n",
347 DEBUGNET_MAX_IN_FLIGHT
, i
);
350 * Wait for acks. A *real* window would speed things up considerably.
353 while (pcb
->dp_rcvd_acks
!= want_acks
) {
354 if (polls
++ > debugnet_npolls
) {
355 if (retries
++ > debugnet_nretries
)
360 debugnet_network_poll(pcb
);
362 if (pcb
->dp_state
== DN_STATE_REMOTE_CLOSED
)
370 * Network input primitives.
374 * Just introspect the header enough to fire off a seqno ack and validate
378 debugnet_handle_rx_msg(struct debugnet_pcb
*pcb
, struct mbuf
**mb
)
380 const struct debugnet_msg_hdr
*dnh
;
388 if (m
->m_pkthdr
.len
< sizeof(*dnh
)) {
389 DNETDEBUG("ignoring small debugnet_msg packet\n");
394 if (m
->m_len
< sizeof(*dnh
)) {
395 m
= m_pullup(m
, sizeof(*dnh
));
398 DNETDEBUG("m_pullup failed\n");
403 dnh
= mtod(m
, const struct debugnet_msg_hdr
*);
404 if (ntohl(dnh
->mh_len
) + sizeof(*dnh
) > m
->m_pkthdr
.len
) {
405 DNETDEBUG("Dropping short packet.\n");
409 hdr_type
= ntohl(dnh
->mh_type
);
410 if (hdr_type
!= DEBUGNET_DATA
) {
411 if (hdr_type
== DEBUGNET_FINISHED
) {
412 printf("Remote shut down the connection on us!\n");
413 pcb
->dp_state
= DN_STATE_REMOTE_CLOSED
;
414 if (pcb
->dp_finish_handler
!= NULL
) {
415 pcb
->dp_finish_handler();
418 DNETDEBUG("Got unexpected debugnet message %u\n", hdr_type
);
424 * If the issue is transient (ENOBUFS), sender should resend. If
425 * non-transient (like driver objecting to rx -> tx from the same
426 * thread), not much else we can do.
428 seqno
= dnh
->mh_seqno
; /* net endian */
429 m_adj(m
, sizeof(*dnh
));
431 error
= pcb
->dp_rx_handler(m
);
433 DNETDEBUG("RX handler was not able to accept message, error %d. "
434 "Skipping ack.\n", error
);
438 error
= debugnet_ack_output(pcb
, seqno
);
440 DNETDEBUG("Couldn't ACK rx packet %u; %d\n", ntohl(seqno
), error
);
445 debugnet_handle_ack(struct debugnet_pcb
*pcb
, struct mbuf
**mb
, uint16_t sport
)
447 const struct debugnet_ack
*dn_ack
;
454 if (m
->m_len
< sizeof(*dn_ack
)) {
455 m
= m_pullup(m
, sizeof(*dn_ack
));
458 DNETDEBUG("m_pullup failed\n");
462 dn_ack
= mtod(m
, const struct debugnet_ack
*);
464 /* Debugnet processing. */
466 * Packet is meant for us. Extract the ack sequence number and the
467 * port number if necessary.
469 rcv_ackno
= ntohl(dn_ack
->da_seqno
);
470 if (pcb
->dp_state
< DN_STATE_GOT_HERALD_PORT
) {
471 pcb
->dp_server_port
= sport
;
472 pcb
->dp_state
= DN_STATE_GOT_HERALD_PORT
;
474 if (rcv_ackno
>= pcb
->dp_seqno
+ DEBUGNET_MAX_IN_FLIGHT
)
475 printf("%s: ACK %u too far in future!\n", __func__
, rcv_ackno
);
476 else if (rcv_ackno
>= pcb
->dp_seqno
) {
477 /* We're interested in this ack. Record it. */
478 pcb
->dp_rcvd_acks
|= 1 << (rcv_ackno
- pcb
->dp_seqno
);
483 debugnet_handle_udp(struct debugnet_pcb
*pcb
, struct mbuf
**mb
)
485 const struct udphdr
*udp
;
487 uint16_t sport
, ulen
;
489 /* UDP processing. */
492 if (m
->m_pkthdr
.len
< sizeof(*udp
)) {
493 DNETDEBUG("ignoring small UDP packet\n");
497 /* Get UDP headers. */
498 if (m
->m_len
< sizeof(*udp
)) {
499 m
= m_pullup(m
, sizeof(*udp
));
502 DNETDEBUG("m_pullup failed\n");
506 udp
= mtod(m
, const struct udphdr
*);
508 /* We expect to receive UDP packets on the configured client port. */
509 if (ntohs(udp
->uh_dport
) != pcb
->dp_client_port
) {
510 DNETDEBUG("not on the expected port.\n");
514 /* Check that ulen does not exceed actual size of data. */
515 ulen
= ntohs(udp
->uh_ulen
);
516 if (m
->m_pkthdr
.len
< ulen
) {
517 DNETDEBUG("ignoring runt UDP packet\n");
521 sport
= ntohs(udp
->uh_sport
);
523 m_adj(m
, sizeof(*udp
));
524 ulen
-= sizeof(*udp
);
526 if (ulen
== sizeof(struct debugnet_ack
)) {
527 debugnet_handle_ack(pcb
, mb
, sport
);
531 if (pcb
->dp_rx_handler
== NULL
) {
532 if (ulen
< sizeof(struct debugnet_ack
))
533 DNETDEBUG("ignoring small ACK packet\n");
535 DNETDEBUG("ignoring unexpected non-ACK packet on "
536 "half-duplex connection.\n");
540 debugnet_handle_rx_msg(pcb
, mb
);
544 * Handler for incoming packets directly from the network adapter
545 * Identifies the packet type (IP or ARP) and passes it along to one of the
546 * helper functions debugnet_handle_ip or debugnet_handle_arp.
548 * It needs to partially replicate the behaviour of ether_input() and
552 * ifp the interface the packet came from
553 * m an mbuf containing the packet received
556 debugnet_input_one(struct ifnet
*ifp
, struct mbuf
*m
)
559 struct ether_header
*eh
;
562 /* Ethernet processing. */
563 if ((m
->m_flags
& M_PKTHDR
) == 0) {
564 DNETDEBUG_IF(ifp
, "discard frame without packet header\n");
567 if (m
->m_len
< ETHER_HDR_LEN
) {
569 "discard frame without leading eth header (len %d pktlen %d)\n",
570 m
->m_len
, m
->m_pkthdr
.len
);
573 eh
= mtod(m
, struct ether_header
*);
574 etype
= ntohs(eh
->ether_type
);
575 if ((m
->m_flags
& M_VLANTAG
) != 0 || etype
== ETHERTYPE_VLAN
) {
576 DNETDEBUG_IF(ifp
, "ignoring vlan packets\n");
579 if (if_gethwaddr(ifp
, &ifr
) != 0) {
580 DNETDEBUG_IF(ifp
, "failed to get hw addr for interface\n");
583 if (memcmp(ifr
.ifr_addr
.sa_data
, eh
->ether_dhost
,
584 ETHER_ADDR_LEN
) != 0 &&
585 (etype
!= ETHERTYPE_ARP
|| !ETHER_IS_BROADCAST(eh
->ether_dhost
))) {
587 "discard frame with incorrect destination addr\n");
591 MPASS(g_debugnet_pcb_inuse
);
593 /* Done ethernet processing. Strip off the ethernet header. */
594 m_adj(m
, ETHER_HDR_LEN
);
597 debugnet_handle_arp(&g_dnet_pcb
, &m
);
600 debugnet_handle_ip(&g_dnet_pcb
, &m
);
603 DNETDEBUG_IF(ifp
, "dropping unknown ethertype %hu\n", etype
);
612 debugnet_input(struct ifnet
*ifp
, struct mbuf
*m
)
619 debugnet_input_one(ifp
, m
);
625 * Network polling primitive.
627 * Instead of assuming that most of the network stack is sane, we just poll the
628 * driver directly for packets.
631 debugnet_network_poll(struct debugnet_pcb
*pcb
)
636 ifp
->if_debugnet_methods
->dn_poll(ifp
, 1000);
640 * Start of consumer API surface.
643 debugnet_free(struct debugnet_pcb
*pcb
)
647 MPASS(pcb
== &g_dnet_pcb
);
648 MPASS(pcb
->dp_drv_input
== NULL
|| g_debugnet_pcb_inuse
);
652 if (pcb
->dp_drv_input
!= NULL
)
653 ifp
->if_input
= pcb
->dp_drv_input
;
654 if (pcb
->dp_event_started
)
655 ifp
->if_debugnet_methods
->dn_event(ifp
, DEBUGNET_END
);
657 debugnet_mbuf_finish();
659 g_debugnet_pcb_inuse
= false;
660 memset(&g_dnet_pcb
, 0xfd, sizeof(g_dnet_pcb
));
664 debugnet_connect(const struct debugnet_conn_params
*dcp
,
665 struct debugnet_pcb
**pcb_out
)
667 struct debugnet_proto_aux herald_auxdata
;
668 struct debugnet_pcb
*pcb
;
672 if (g_debugnet_pcb_inuse
) {
673 printf("%s: Only one connection at a time.\n", __func__
);
678 *pcb
= (struct debugnet_pcb
) {
679 .dp_state
= DN_STATE_INIT
,
680 .dp_client
= dcp
->dc_client
,
681 .dp_server
= dcp
->dc_server
,
682 .dp_gateway
= dcp
->dc_gateway
,
683 .dp_server_port
= dcp
->dc_herald_port
, /* Initially */
684 .dp_client_port
= dcp
->dc_client_port
,
686 .dp_ifp
= dcp
->dc_ifp
,
687 .dp_rx_handler
= dcp
->dc_rx_handler
,
688 .dp_drv_input
= NULL
,
691 /* Switch to the debugnet mbuf zones. */
692 debugnet_mbuf_start();
694 /* At least one needed parameter is missing; infer it. */
695 if (pcb
->dp_client
== INADDR_ANY
|| pcb
->dp_gateway
== INADDR_ANY
||
696 pcb
->dp_ifp
== NULL
) {
697 struct sockaddr_in dest_sin
, *gw_sin
, *local_sin
;
698 struct ifnet
*rt_ifp
;
699 struct nhop_object
*nh
;
701 memset(&dest_sin
, 0, sizeof(dest_sin
));
702 dest_sin
= (struct sockaddr_in
) {
703 .sin_len
= sizeof(dest_sin
),
704 .sin_family
= AF_INET
,
705 .sin_addr
.s_addr
= pcb
->dp_server
,
709 nh
= fib4_lookup_debugnet(debugnet_fib
, dest_sin
.sin_addr
, 0,
714 printf("%s: Could not get route for that server.\n",
720 /* TODO support AF_INET6 */
721 if (nh
->gw_sa
.sa_family
== AF_INET
)
722 gw_sin
= &nh
->gw4_sa
;
724 if (nh
->gw_sa
.sa_family
== AF_LINK
)
725 DNETDEBUG("Destination address is on link.\n");
729 MPASS(nh
->nh_ifa
->ifa_addr
->sa_family
== AF_INET
);
730 local_sin
= (struct sockaddr_in
*)nh
->nh_ifa
->ifa_addr
;
734 if (pcb
->dp_client
== INADDR_ANY
)
735 pcb
->dp_client
= local_sin
->sin_addr
.s_addr
;
736 if (pcb
->dp_gateway
== INADDR_ANY
&& gw_sin
!= NULL
)
737 pcb
->dp_gateway
= gw_sin
->sin_addr
.s_addr
;
738 if (pcb
->dp_ifp
== NULL
)
739 pcb
->dp_ifp
= rt_ifp
;
744 if (debugnet_debug
> 0) {
745 char serbuf
[INET_ADDRSTRLEN
], clibuf
[INET_ADDRSTRLEN
],
746 gwbuf
[INET_ADDRSTRLEN
];
747 inet_ntop(AF_INET
, &pcb
->dp_server
, serbuf
, sizeof(serbuf
));
748 inet_ntop(AF_INET
, &pcb
->dp_client
, clibuf
, sizeof(clibuf
));
749 if (pcb
->dp_gateway
!= INADDR_ANY
)
750 inet_ntop(AF_INET
, &pcb
->dp_gateway
, gwbuf
, sizeof(gwbuf
));
751 DNETDEBUG("Connecting to %s:%d%s%s from %s:%d on %s\n",
752 serbuf
, pcb
->dp_server_port
,
753 (pcb
->dp_gateway
== INADDR_ANY
) ? "" : " via ",
754 (pcb
->dp_gateway
== INADDR_ANY
) ? "" : gwbuf
,
755 clibuf
, pcb
->dp_client_port
, if_name(ifp
));
758 /* Validate iface is online and supported. */
759 if (!DEBUGNET_SUPPORTED_NIC(ifp
)) {
760 printf("%s: interface '%s' does not support debugnet\n",
761 __func__
, if_name(ifp
));
765 if ((if_getflags(ifp
) & IFF_UP
) == 0) {
766 printf("%s: interface '%s' link is down\n", __func__
,
772 ifp
->if_debugnet_methods
->dn_event(ifp
, DEBUGNET_START
);
773 pcb
->dp_event_started
= true;
776 * We maintain the invariant that g_debugnet_pcb_inuse is always true
777 * while the debugnet ifp's if_input is overridden with
780 g_debugnet_pcb_inuse
= true;
782 /* Make the card use *our* receive callback. */
783 pcb
->dp_drv_input
= ifp
->if_input
;
784 ifp
->if_input
= debugnet_input
;
786 printf("%s: searching for %s MAC...\n", __func__
,
787 (dcp
->dc_gateway
== INADDR_ANY
) ? "server" : "gateway");
789 error
= debugnet_arp_gw(pcb
);
791 printf("%s: failed to locate MAC address\n", __func__
);
794 MPASS(pcb
->dp_state
== DN_STATE_HAVE_GW_MAC
);
796 herald_auxdata
= (struct debugnet_proto_aux
) {
797 .dp_offset_start
= dcp
->dc_herald_offset
,
798 .dp_aux2
= dcp
->dc_herald_aux2
,
800 error
= debugnet_send(pcb
, DEBUGNET_HERALD
, dcp
->dc_herald_data
,
801 dcp
->dc_herald_datalen
, &herald_auxdata
);
803 printf("%s: failed to herald debugnet server\n", __func__
);
816 * Pre-allocated dump-time mbuf tracking.
818 * We just track the high water mark we've ever seen and allocate appropriately
819 * for that iface/mtu combo.
826 static struct mtx dn_hwm_lk
;
827 MTX_SYSINIT(debugnet_hwm_lock
, &dn_hwm_lk
, "Debugnet HWM lock", MTX_DEF
);
830 dn_maybe_reinit_mbufs(int nmbuf
, int ncl
, int clsize
)
835 mtx_lock(&dn_hwm_lk
);
837 if (nmbuf
> dn_hwm
.nmbuf
) {
839 dn_hwm
.nmbuf
= nmbuf
;
841 nmbuf
= dn_hwm
.nmbuf
;
843 if (ncl
> dn_hwm
.ncl
) {
849 if (clsize
> dn_hwm
.clsize
) {
851 dn_hwm
.clsize
= clsize
;
853 clsize
= dn_hwm
.clsize
;
855 mtx_unlock(&dn_hwm_lk
);
858 debugnet_mbuf_reinit(nmbuf
, ncl
, clsize
);
862 debugnet_any_ifnet_update(struct ifnet
*ifp
)
864 int clsize
, nmbuf
, ncl
, nrxr
;
866 if (!DEBUGNET_SUPPORTED_NIC(ifp
))
869 ifp
->if_debugnet_methods
->dn_init(ifp
, &nrxr
, &ncl
, &clsize
);
870 KASSERT(nrxr
> 0, ("invalid receive ring count %d", nrxr
));
873 * We need two headers per message on the transmit side. Multiply by
874 * four to give us some breathing room.
876 nmbuf
= ncl
* (4 + nrxr
);
880 * Bandaid for drivers that (incorrectly) advertise LinkUp before their
881 * dn_init method is available.
883 if (nmbuf
== 0 || ncl
== 0 || clsize
== 0) {
887 printf("%s: Bad dn_init result from %s (ifp %p), ignoring.\n",
888 __func__
, if_name(ifp
), ifp
);
891 dn_maybe_reinit_mbufs(nmbuf
, ncl
, clsize
);
895 * Unfortunately, the ifnet_arrival_event eventhandler hook is mostly useless
896 * for us because drivers tend to if_attach before invoking DEBUGNET_SET().
898 * On the other hand, hooking DEBUGNET_SET() itself may still be too early,
899 * because the driver is still in attach. Since we cannot use down interfaces,
900 * maybe hooking ifnet_event:IFNET_EVENT_UP is sufficient? ... Nope, at least
901 * with vtnet and dhcpclient that event just never occurs.
903 * So that's how I've landed on the lower level ifnet_link_event.
907 dn_ifnet_event(void *arg __unused
, struct ifnet
*ifp
, int link_state
)
909 if (link_state
== LINK_STATE_UP
)
910 debugnet_any_ifnet_update(ifp
);
913 static eventhandler_tag dn_attach_cookie
;
915 dn_evh_init(void *ctx __unused
)
917 dn_attach_cookie
= EVENTHANDLER_REGISTER(ifnet_link_event
,
918 dn_ifnet_event
, NULL
, EVENTHANDLER_PRI_ANY
);
920 SYSINIT(dn_evh_init
, SI_SUB_EVENTHANDLER
+ 1, SI_ORDER_ANY
, dn_evh_init
, NULL
);
923 * DDB parsing helpers for debugnet(4) consumers.
928 const char *printname
;
933 dn_parse_optarg_ipv4(struct my_inet_opt
*opt
)
940 for (octet
= 0; octet
< 4; octet
++) {
941 t
= db_read_token_flags(DRT_WSPACE
| DRT_DECIMAL
);
943 db_printf("%s:%s: octet %u expected number; found %d\n",
944 __func__
, opt
->printname
, octet
, t
);
948 * db_lex lexes '-' distinctly from the number itself, but
949 * let's document that invariant.
951 MPASS(db_tok_number
>= 0);
953 if (db_tok_number
> UINT8_MAX
) {
954 db_printf("%s:%s: octet %u out of range: %jd\n", __func__
,
955 opt
->printname
, octet
, (intmax_t)db_tok_number
);
959 /* Constructed host-endian and converted to network later. */
960 tmp
= (tmp
<< 8) | db_tok_number
;
963 t
= db_read_token_flags(DRT_WSPACE
);
965 db_printf("%s:%s: octet %u expected '.'; found"
966 " %d\n", __func__
, opt
->printname
, octet
,
973 *opt
->result
= htonl(tmp
);
979 debugnet_parse_ddb_cmd(const char *cmd
, struct debugnet_ddb_config
*result
)
986 struct my_inet_opt opt_client
= {
987 .printname
= "client",
988 .result
= &result
->dd_client
,
991 .printname
= "server",
992 .result
= &result
->dd_server
,
995 .printname
= "gateway",
996 .result
= &result
->dd_gateway
,
1001 memset(result
, 0, sizeof(*result
));
1004 * command [space] [-] [opt] [[space] [optarg]] ...
1006 * db_command has already lexed 'command' for us.
1008 t
= db_read_token_flags(DRT_WSPACE
);
1010 t
= db_read_token_flags(DRT_WSPACE
);
1014 db_printf("%s: Bad syntax; expected '-', got %d\n",
1019 t
= db_read_token_flags(DRT_WSPACE
);
1021 db_printf("%s: Bad syntax; expected tIDENT, got %d\n",
1026 if (strlen(db_tok_string
) > 1) {
1027 db_printf("%s: Bad syntax; expected single option "
1028 "flag, got '%s'\n", cmd
, db_tok_string
);
1033 cur_inet_opt
= NULL
;
1034 switch ((ch
= db_tok_string
[0])) {
1036 DNETDEBUG("Unexpected: '%c'\n", ch
);
1041 cur_inet_opt
= &opt_client
;
1044 cur_inet_opt
= &opt_gateway
;
1047 cur_inet_opt
= &opt_server
;
1054 t
= db_read_token_flags(DRT_WSPACE
);
1056 db_printf("%s: Bad syntax; expected space after "
1057 "flag %c, got %d\n", cmd
, ch
, t
);
1062 t
= db_read_token_flags(DRT_WSPACE
);
1064 db_printf("%s: Expected interface but got %d\n",
1071 * We *don't* take a ref here because the only current
1072 * consumer, db_netdump_cmd, does not need it. It
1073 * (somewhat redundantly) extracts the if_name(),
1074 * re-lookups the ifp, and takes its own reference.
1076 ifp
= ifunit(db_tok_string
);
1079 db_printf("Could not locate interface %s\n",
1085 MPASS(cur_inet_opt
!= NULL
);
1086 /* Assume IPv4 for now. */
1087 error
= dn_parse_optarg_ipv4(cur_inet_opt
);
1092 /* Skip (mandatory) whitespace after option, if not EOL. */
1093 t
= db_read_token_flags(DRT_WSPACE
);
1097 db_printf("%s: Bad syntax; expected space after "
1098 "flag %c option; got %d\n", cmd
, ch
, t
);
1101 t
= db_read_token_flags(DRT_WSPACE
);
1104 if (!opt_server
.has_opt
) {
1105 db_printf("%s: need a destination server address\n", cmd
);
1109 result
->dd_has_client
= opt_client
.has_opt
;
1110 result
->dd_has_gateway
= opt_gateway
.has_opt
;
1111 result
->dd_ifp
= ifp
;
1113 /* We parsed the full line to tEOL already, or bailed with an error. */
1117 db_printf("Usage: %s -s <server> [-g <gateway> -c <localip> "
1118 "-i <interface>]\n", cmd
);