/*
 *	Copyright (c) 1995, 1996, Sun Microsystems, Inc.
 *
 * This software is provided by Sun ``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 Sun Microsystems be liable for any direct, indirect,
 * incidental, special, exemplary, or consequential damages.
 *
 * This software is not a product, and is provided for evaluation purposes
 * only.
 *
 * This software may not be resold without the express permission of
 * Sun Microsystems.
 *
 * Feedback on this tool may be sent to packet-shell-owner@sunroof.eng.sun.com.
 *
 * Packet Shell
 */

#pragma ident	"@(#)psh.h	1.32	00/01/14 SMI"

#include	<tcl.h>

/*
 * Global common definitions for the packet shell.
 *
 * The key data structures are packets and protocol table entries.
 *
 * Things to know about packets:
 *
 * A packet is conceptually a neutral container with a bunch of memory.
 * That memory is broken up into layers, rather like ISO protocol layers.
 * The packet shell only cares that each layer has a name, and a part of
 * the memory with its data.  Each protocol may have more than one segment
 * of memory associated with it, although in practice few protocols will
 * do that generally.  (A good reason to see one protocol have multiple
 * entries would be a ICMP packet, which contains its own IP header and
 * might have a IP header encapsulated inside the ICMP header.)
 * Description of each segment of memory is kept in the "packet table",
 * and consists of a name and an offset.  The special name "end" for
 * the protocol means no data is stored beyond that point in the packet.
 *
 * The protocol table keeps track of the description of the implementation
 * of the protocol.  This includes pointers to C functions, or the
 * references to Tcl procedures which implement that protocol's capabilities.
 */

#include <sys/types.h>

/*
 * Configuration stuff first...
 */

/* Nothing needed for Solaris, it's the default */

#ifdef FREEBSD
#define	NEED_SYS_TIME
#define	NO_BYTEORDER_H
#define	NEED_POSIX_TYPES
#endif

/*
 * Some systems don't have timeval from sys/types.h, and there is no
 * combination of includes that gets timeval in a portable and consistent
 * way.  So we use NEED_SYS_TIME as a configuration flag.  That's enough
 * to cope with FreeBSD and Solaris at a minimum.  Ideally, we should make
 * GNU autoconf do the work for this.
 */
#ifdef NEED_SYS_TIME
#include <sys/time.h>
#endif

/*
 * NO_BYTEORDER_H is a configuration variable, determined above based on
 * the system type.  It tells us which flavor of config file tells us our
 * endian-ness.
 */
#ifdef	NO_BYTEORDER_H
#include <machine/endian.h>
#else
#include <sys/byteorder.h>
#endif

/*
 * Within the packet is a table, called the packet table.  This table
 * Packet table structure, the name is the name of the protocol, and the
 * offset is where in this packet that protocol begins.
 */

typedef struct ptbl {
	char		*pt_name;		/* Protocol name */
	int		pt_offset;		/* Offset within packet */
} ptbl_t;

/*
 * Data structure describing a packet buffer for the protocols.  See above.
 */

typedef struct packet {
	char		*p_name;	/* Name of this packet variable */
	char		*p_buf;		/* Packet buffer */
	int		p_len;		/* Length of data currently in buf */
	int		p_maxlen;	/* Allocated size of buf */
	struct timeval	p_pat;		/* Packet arrival time */
	ptbl_t		*p_tbl;		/* Table of packet layering */
	int		p_nptbl;	/* Size of allocated ptbl array */
	int		p_lptbl;	/* Index of last active ptbl element */
} packet_t;

/*
 * Data structure used to hold the extensible information used to create
 * a new protocol (proto_tbl).  For input and output functions the following
 * logic is used:  for Tcl procedures the procedure name is stored in the
 * corresponding string and the pointer set to NULL, for C functions
 * the pointer is set to the symbollic address and the string holds
 * the function name.
 *
 * The difference between a Tcl procedure and a C function is that
 * get or set will equal NULL if it's a Tcl function.
 */

typedef struct pdefine_tbl {
	char			*pd_fieldname;	/* Fieldname */
	char			*pd_desc;	/* Description */
	int			pd_offset;	/* Offset in bytes */
	int			pd_size;	/* Size in bytes */
	char			*pd_getName;	/* Tcl get procedure */
	int			(*pd_get)();	/* C get function */
	char			*pd_setName;	/* Tcl set procedure */
	int			(*pd_set)();	/* C set function */
} pdefine_tbl_t;

/*
 * Data structure used to store the interfaces for a protocol library.
 * Store the symbollic address of the interface (ie. propen) from
 * the protocol library here.  This is the foundation structure for creating
 * a protocol library.  The pdefine command allows you to overlay the pinit,
 * decode, and list function with a Tcl procedure.  If pinitName, listName,
 * or decodeName isn't equal to NULL then invoke the Tcl procedure in the
 * string.  Otherwise invoke the C function in pinit, list, and decode
 * if it exists.
 */
typedef struct proto_tbl {
	char		*proto_name;		/* Protocol name */
	/*
	 * proto_epsize is the size of the <proto>_ep structure for this
	 * protocol.  This is needed by psh_popen(), since the allocation
	 * of an end-point is done in this function, and is then passed to
	 * the protocol library's propen() as ClientData.
	 */
	size_t		proto_epsize;
	/*
	 * "Traditional" Protocol library entry points.  These C functions
	 * are implemented in a dynamic object.
	 */
	int		(*proto_libinit)();	/* library init */
	int		(*proto_open)();	/* popen */
	int		(*proto_close)();	/* pclose */
	int		(*proto_init)();	/* pinit */
	int		(*proto_free)();	/* pfree */
	int		(*proto_decode)();	/* decode */
	int		(*proto_get)();		/* get */
	int		(*proto_set)();		/* set */
	int		(*proto_list)();	/* list */
	int		(*proto_waitaction)();	/* wait_action */

	/*
	 * The rest of the structure elements support the Tcl
	 * protocol definitions.
	 */
	char		*proto_tcl_pinit;	/* Tcl pinit proc */
	char		*proto_tcl_decode;	/* Tcl decode proc */
	char		*proto_tcl_list;	/* Tcl list proc */

	/*
	 * This is the hash table for the dynamic protocol structure
	 * (pdefine_tbl).  Uses the fieldname as the hash key and
	 * and store a pointer to the pdefine_tbl structure in the
	 * hash table.
	 */
	Tcl_HashTable	proto_defines;

	/*
	 * proto_len tracks the size, as implied by the highest
	 * value of an elements size plus its offset.  This is
	 * maintained by the packet shell core, but only for protocols
	 * which have at least one Tcl pdefine active.
	 */
	int		proto_len;
} proto_tbl_t;

/*
 * Control for an automated response to particular packets.  The filtering
 * and the associated action are script-based.
 */
struct auto_action {
	char			*aa_id;		/* the ID string */
	char			*aa_filter_comm; /* the filter command */
	char			*aa_action_comm; /* the action command */
	uchar_t			 aa_terminal;	/* continue after a match? */
	struct auto_action	*aa_next;	/* next filter/action pair */
};

/*
 * The end-point structure.  An end-point is the interface which enables
 * the user to send and receive packets.  It has a globally unique name,
 * a pointer to the protocol table of the protocol for which this end-point
 * pertains to, and a list of actions.  These end-points are created by
 * the popen psh command, which triggers the psh_popen() function.  The
 * end-point is allocated by the psh_new_ep() function, and initialized
 * by the specific protocol's propen() function.  It is then inserted into
 * the global hash table of end-points.  An end-point is removed and freed
 * when the user closes it via the pclose psh command.
 */
typedef struct psh_ep {
	char			*ep_name;	/* Name of the end point */
	proto_tbl_t		*ep_prototbl;	/* the ep's protocol table */
	struct auto_action	*ep_action_head; /* action list */
	uint_t			ep_action_count; /* # of actions in the list */
} ep_t;

/*
 * Packet table external interfaces
 *
 * Although you can manipulate the packet table directly, you should use
 * these external interfaces, which are intended to provide all you should
 * need to update the packet table.
 */
enum { PTBL_KEEP_OFFSET = -1 };
extern int ptbl_update(struct packet *, int, const char *, ssize_t);
extern int ptbl_init(struct packet *, const char *);
extern int ptbl_fix_length(struct packet *, int, int);
extern int bootstrap_packet(struct packet *, int);

extern struct packet *pckt_lookup(char *);

extern char *ipproto_name_by_number(int);
extern int ipproto_number_by_name(char *);
extern void init_ip_protos();

extern void ip_cksum(unsigned short *, unsigned char *, int);

int psh_ascii2addr(long, const char *, void *);
char *psh_addr2ascii(int, const void *, char *);

int pshaction_apply_actions(Tcl_Interp *, struct packet *, ep_t *, int *);

ep_t *psh_new_ep(char *, struct proto_tbl *);
ep_t *psh_find_ep(char *);
ep_t *psh_add_ep(ep_t *);

#define	PTBL_INCR	10	/* Increments of allocation for ptbl arrays */

#define	PSH_LIB_DIR	"/opt/psh/lib"
#define	PSH_TCLLIB_DIR	"/opt/psh/tcllib"
#define	PSH_LIB_ENV	"PSH_LIB"
#define	PSH_TCLLIB_ENV	"PSH_TCLLIB"
#define	PSH_STARTUP	"tcllib/pshrc"
#define	PSH_SYLLABLE	"/psh/"

#define	PSH_PKT_DEFAULT_LEN	"1600"

/*
 * Some handy macros...
 *
 * Note that although min()/MIN() max()/MAX() are "common" macros, there
 * is no portable way to use them, so we don't rely on the unportable
 * definitions some systems might have.
 */
#ifndef max
#define	max(a, b)	((a > b) ? (a) : (b))
#endif

#ifndef min
#define	min(a, b)	((a < b) ? (a) : (b))
#endif

/*
 * We assume these types come in via some general include file.  If
 * we don't see _SYS_INT_TYPES_H, we know we're not on Solaris 2.6 or greater,
 * so we expect the unsigned versions of these types to be missing.  (FreeBSD
 * is this way.)
 */

/*
 * These originate in the Makefile, aside from the one automatic rule below.
 * NEED_FIXED_SIZED_TYPES: System has no [u]int{size}_t typedefs, supply them.
 * NEED_UNSIGNED_FIXED_SIZED_TYPES: System has no uint{size}_t typedefs,
 *   so supply them.
 * All these configuration bits are kept in the Makefile, since that's expected
 * to be the only file changed for configuration.
 */
#ifndef _SYS_INT_TYPES_H
#define	NEED_UNSIGNED_FIXED_SIZED_TYPES	/* configure me */
#endif

#ifdef NEED_FIXED_SIZED_TYPES
typedef	char			int8_t;
typedef	short			int16_t;
typedef	int			int32_t;
typedef	long long		int64_t;
#endif /* NEED_FIXED_SIZED_TYPES */

#if defined(NEED_FIXED_SIZED_TYPES) || defined(NEED_UNSIGNED_FIXED_SIZED_TYPES)
typedef	unsigned char		uint8_t;
typedef	unsigned short		uint16_t;
typedef	unsigned int		uint32_t;
typedef	unsigned long long	uint64_t;
#endif

#ifdef	NEED_POSIX_TYPES
typedef unsigned char		uchar_t;
typedef unsigned short		ushort_t;
typedef unsigned int		uint_t;
typedef unsigned long		ulong_t;
#endif	/* NEED_POSIX_TYPES */

/*
 * Many systems don't have a basic boolean type...
 */
typedef enum { BTRUE = 1, BFALSE = 0 } bool_t;
