/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2019 Isilon Systems, LLC.
 * Copyright (c) 2005-2014 Sandvine Incorporated
 * Copyright (c) 2000 Darrell Anderson <anderson@cs.duke.edu>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Debugnet provides a reliable, bidirectional, UDP-encapsulated datagram
 * transport while a machine is in a debug state.  (N-1 CPUs stopped,
 * interrupts disabled, may or may not be in a panic(9) state.)  Only one
 * stream may be active at a time.  A dedicated server must be running to
 * accept connections.
 */

#pragma once

#include <sys/types.h>
#include <netinet/in.h>

/*
 * Debugnet protocol details.
 */
#define	DEBUGNET_HERALD		1	/* Connection handshake. */
#define	DEBUGNET_FINISHED	2	/* Close the connection. */
#define	DEBUGNET_DATA		3	/* Contains data. */

struct debugnet_msg_hdr {
	uint32_t	mh_type;	/* Debugnet message type. */
	uint32_t	mh_seqno;	/* Match acks with msgs. */
	uint64_t	mh_offset;	/* Offset in fragment. */
	uint32_t	mh_len;		/* Attached data (bytes). */
	uint32_t	mh_aux2;	/* Consumer-specific. */
} __packed;

struct debugnet_ack {
	uint32_t	da_seqno;	/* Match acks with msgs. */
} __packed;

#define	DEBUGNET_MAX_IN_FLIGHT	64

#ifdef _KERNEL
/*
 * Hook API for network drivers.
 */
enum debugnet_ev {
	DEBUGNET_START,
	DEBUGNET_END,
};

struct ifnet;
struct mbuf;
typedef void debugnet_init_t(struct ifnet *, int *nrxr, int *ncl, int *clsize);
typedef void debugnet_event_t(struct ifnet *, enum debugnet_ev);
typedef int debugnet_transmit_t(struct ifnet *, struct mbuf *);
typedef int debugnet_poll_t(struct ifnet *, int);

struct debugnet_methods {
	debugnet_init_t		*dn_init;
	debugnet_event_t	*dn_event;
	debugnet_transmit_t	*dn_transmit;
	debugnet_poll_t		*dn_poll;
};

#define	DEBUGNET_SUPPORTED_NIC(ifp)				\
	((ifp)->if_debugnet_methods != NULL && (ifp)->if_type == IFT_ETHER)

struct debugnet_pcb; /* opaque */

/*
 * Debugnet consumer API.
 */
struct debugnet_conn_params {
	struct ifnet	*dc_ifp;
	in_addr_t	dc_client;
	in_addr_t	dc_server;
	in_addr_t	dc_gateway;

	uint16_t	dc_herald_port;
	uint16_t	dc_client_port;

	const void	*dc_herald_data;
	uint32_t	dc_herald_datalen;

	/*
	 * Consistent with debugnet_send(), aux parameters to debugnet
	 * functions are provided host-endian (but converted to
	 * network endian on the wire).
	 */
	uint32_t	dc_herald_aux2;
	uint64_t	dc_herald_offset;

	/*
	 * 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.
	 *
	 * The handler should ACK receieved packets with debugnet_ack_output.
	 */
	int			(*dc_rx_handler)(struct mbuf *);

	/* Cleanup signal for bidirectional protocols. */
	void		(*dc_finish_handler)(void);
};

/*
 * Open a stream to the specified server's herald port.
 *
 * If all goes well, the server will send ACK from a different port to our ack
 * port.  This allows servers to somewhat gracefully handle multiple debugnet
 * clients.  (Clients are limited to single connections.)
 *
 * Returns zero on success, or errno.
 */
int debugnet_connect(const struct debugnet_conn_params *,
    struct debugnet_pcb **pcb_out);

/*
 * Free a debugnet stream that was previously successfully opened.
 *
 * No attempt is made to cleanly terminate communication with the remote
 * server.  Consumers should first send an empty DEBUGNET_FINISHED message, or
 * otherwise let the remote know they are signing off.
 */
void debugnet_free(struct debugnet_pcb *);

/*
 * Send a message, with common debugnet_msg_hdr header, to the connected remote
 * server.
 *
 * - mhtype translates directly to mh_type (e.g., DEBUGNET_DATA, or some other
 *   protocol-specific type).
 * - Data and datalen describe the attached data; datalen may be zero.
 * - If auxdata is NULL, mh_offset's initial value and mh_aux2 will be zero.
 *   Otherwise, mh_offset's initial value will be auxdata->dp_offset_start and
 *   mh_aux2 will have the value of auxdata->dp_aux2.
 *
 * Returns zero on success, or an errno on failure.
 */
struct debugnet_proto_aux {
	uint64_t dp_offset_start;
	uint32_t dp_aux2;
};
int debugnet_send(struct debugnet_pcb *, uint32_t mhtype, const void *data,
    uint32_t datalen, const struct debugnet_proto_aux *auxdata);

/*
 * A simple wrapper around the above when no data or auxdata is needed.
 */
static inline int
debugnet_sendempty(struct debugnet_pcb *pcb, uint32_t mhtype)
{
	return (debugnet_send(pcb, mhtype, NULL, 0, NULL));
}

/*
 * Full-duplex RX should ACK received messages.
 */
int debugnet_ack_output(struct debugnet_pcb *, uint32_t seqno /*net endian*/);

/*
 * Check and/or wait for further packets.
 */
void debugnet_network_poll(struct debugnet_pcb *);

/*
 * PCB accessors.
 */

/*
 * Get the 48-bit MAC address of the discovered next hop (gateway, or
 * destination server if it is on the same segment.
 */
const unsigned char *debugnet_get_gw_mac(const struct debugnet_pcb *);

/*
 * Get the connected server address.
 */
const in_addr_t *debugnet_get_server_addr(const struct debugnet_pcb *);

/*
 * Get the connected server port.
 */
const uint16_t debugnet_get_server_port(const struct debugnet_pcb *);

/*
 * Callbacks from core mbuf code.
 */
void debugnet_any_ifnet_update(struct ifnet *);

/*
 * DDB parsing helper for common debugnet options.
 *
 * -s <server> [-g <gateway -c <localip> -i <interface>]
 *
 * Order is not significant.  Interface is an online interface that supports
 * debugnet and can route to the debugnet server.  The other parameters are all
 * IP addresses.  Only the server parameter is required.  The others are
 * inferred automatically from the routing table, if not explicitly provided.
 *
 * Provides basic '-h' using provided 'cmd' string.
 *
 * Returns zero on success, or errno.
 */
struct debugnet_ddb_config {
	struct ifnet	*dd_ifp;	/* not ref'd */
	in_addr_t	dd_client;
	in_addr_t	dd_server;
	in_addr_t	dd_gateway;
	bool		dd_has_client : 1;
	bool		dd_has_gateway : 1;
};
int debugnet_parse_ddb_cmd(const char *cmd,
    struct debugnet_ddb_config *result);

/* Expose sysctl variables for netdump(4) to alias. */
extern int debugnet_npolls;
extern int debugnet_nretries;
extern int debugnet_arp_nretries;

/*
 * Conditionally-defined macros for device drivers so we can avoid ifdef
 * wrappers in every single implementation.
 */
#ifdef DEBUGNET
#define	DEBUGNET_DEFINE(driver)					\
	static debugnet_init_t driver##_debugnet_init;		\
	static debugnet_event_t driver##_debugnet_event;	\
	static debugnet_transmit_t driver##_debugnet_transmit;	\
	static debugnet_poll_t driver##_debugnet_poll;		\
								\
	static struct debugnet_methods driver##_debugnet_methods = { \
		.dn_init = driver##_debugnet_init,		\
		.dn_event = driver##_debugnet_event,		\
		.dn_transmit = driver##_debugnet_transmit,	\
		.dn_poll = driver##_debugnet_poll,		\
	}

#define	DEBUGNET_NOTIFY_MTU(ifp)	debugnet_any_ifnet_update(ifp)

#define	DEBUGNET_SET(ifp, driver)				\
	if_setdebugnet_methods((ifp), &driver##_debugnet_methods)

#else /* !DEBUGNET || !INET */

#define	DEBUGNET_DEFINE(driver)
#define	DEBUGNET_NOTIFY_MTU(ifp)
#define	DEBUGNET_SET(ifp, driver)

#endif /* DEBUGNET && INET */
#endif /* _KERNEL */
