File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / ppp / ppp_fsm_option.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:25:53 2012 UTC (12 years, 4 months ago) by misho
Branches: libpdel, MAIN
CVS tags: v0_5_3, HEAD
libpdel


/*
 * Copyright (c) 2001-2002 Packet Design, LLC.
 * All rights reserved.
 * 
 * Subject to the following obligations and disclaimer of warranty,
 * use and redistribution of this software, in source or object code
 * forms, with or without modifications are expressly permitted by
 * Packet Design; provided, however, that:
 * 
 *    (i)  Any and all reproductions of the source or object code
 *         must include the copyright notice above and the following
 *         disclaimer of warranties; and
 *    (ii) No rights are granted, in any manner or form, to use
 *         Packet Design trademarks, including the mark "PACKET DESIGN"
 *         on advertising, endorsements, or otherwise except as such
 *         appears in the above copyright notice or in the software.
 * 
 * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
 * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
 * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
 * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
 * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
 * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
 * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
 * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
 * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 PACKET DESIGN IS ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Archie Cobbs <archie@freebsd.org>
 */

#include "ppp/ppp_defs.h"
#include "ppp/ppp_log.h"
#include "ppp/ppp_fsm_option.h"

#define FSM_OPTION_MTYPE	"ppp_fsm_option"

/***********************************************************************
			PUBLIC FUNCTIONS
***********************************************************************/

/*
 * Create new options array.
 */
struct ppp_fsm_options *
ppp_fsm_option_create(void)
{
	struct ppp_fsm_options *opt;

	if ((opt = MALLOC(FSM_OPTION_MTYPE, sizeof(*opt))) == NULL)
		return (NULL);
	memset(opt, 0, sizeof(*opt));
	return (opt);
}

/*
 * Destroy options array.
 */
void
ppp_fsm_option_destroy(struct ppp_fsm_options **optsp)
{
	struct ppp_fsm_options *const opts = *optsp;
	int i;

	if (opts == NULL)
		return;
	*optsp = NULL;
	for (i = 0; i < opts->num; i++)
		FREE(FSM_OPTION_MTYPE, opts->opts[i].data);
	FREE(FSM_OPTION_MTYPE, opts->opts);
	FREE(FSM_OPTION_MTYPE, opts);
}

/*
 * Add an option to an options array.
 */
int
ppp_fsm_option_add(struct ppp_fsm_options *opts,
	u_char type, u_char len, const void *data)
{
	struct ppp_fsm_option *opt;
	void *buf = NULL;
	void *mem;

	/* Copy option data into buffer */
	if (len > 0) {
		if ((buf = MALLOC(FSM_OPTION_MTYPE, len)) == NULL)
			return (-1);
		memcpy(buf, data, len);
	}

	/* Extend options array by one */
	if ((mem = REALLOC(FSM_OPTION_MTYPE, opts->opts,
	    (opts->num + 1) * sizeof(*opts->opts))) == NULL) {
		FREE(FSM_OPTION_MTYPE, buf);
		return (-1);
	}
	opts->opts = mem;
	opt = &opts->opts[opts->num++];

	/* Fill in option info */
	opt->type = type;
	opt->len = len;
	opt->data = buf;

	/* Done */
	return (0);
}

/*
 * Remove an option from an options array.
 */
int
ppp_fsm_option_del(struct ppp_fsm_options *opts, u_int index)
{
	struct ppp_fsm_option *const opt = opts->opts + index;

	if (index >= opts->num) {
		errno = EDOM;
		return (-1);
	}
	FREE(FSM_OPTION_MTYPE, opt->data);
	memmove(opts->opts + index, opts->opts + index + 1,
	    (--opts->num - index) * sizeof(*opts->opts));
	return (0);
}

/*
 * Reset an options array to empty.
 */
void
ppp_fsm_option_zero(struct ppp_fsm_options *opts)
{
	while (opts->num > 0)
		ppp_fsm_option_del(opts, 0);
	FREE(FSM_OPTION_MTYPE, opts->opts);
	opts->opts = NULL;
}

/*
 * Copy an options array.
 */
struct ppp_fsm_options *
ppp_fsm_option_copy(struct ppp_fsm_options *opts)
{
	struct ppp_fsm_options *copy;
	int i;

	if ((copy = ppp_fsm_option_create()) == NULL)
		return (NULL);
	for (i = 0; i < opts->num; i++) {
		const struct ppp_fsm_option *const opt = &opts->opts[i];

		if (ppp_fsm_option_add(copy,
		    opt->type, opt->len, opt->data) == -1) {
			ppp_fsm_option_destroy(&copy);
			return (NULL);
		}
	}
	return (copy);
}

/*
 * Compare two options arrays for equality.
 *
 * If "i1" or "i2" is equal to -1 then we compare all options.
 * Otherwise we just compare option "i1" of "o1" to option "i2" of "o2".
 */
int
ppp_fsm_option_equal(const struct ppp_fsm_options *o1,
	int i1, const struct ppp_fsm_options *o2, int i2)
{
	int i;

	/* Compare all options? */
	if (i1 == -1 && i2 == -1) {
		if (o1->num != o2->num)
			return (0);
		for (i = 0; i < o1->num; i++) {
			if (!ppp_fsm_option_equal(o1, i, o2, i))
				return (0);
		}
		return (1);
	}

	/* Sanity check indicies */
	if (i1 < 0 || i2 < 0 || i1 >= o1->num || i2 >= o2->num) {
		errno = EINVAL;
		return (-1);
	}

	/* Compare two options */
	if (o1->opts[i1].type != o2->opts[i2].type)
		return (0);
	if (o1->opts[i1].len != o2->opts[i2].len)
		return (0);
	if (memcmp(o1->opts[i1].data, o2->opts[i2].data, o1->opts[i1].len) != 0)
		return (0);
	return (1);
}

/*
 * Print out options into the log.
 */
void
ppp_fsm_options_decode(const struct ppp_fsm_optdesc *optlist,
	const u_char *data, u_int len, char *buf, size_t bmax)
{
	struct ppp_fsm_options *opts;
	int i;

	/* Decode options */
	if ((opts = ppp_fsm_option_unpack(data, len)) == NULL)
		return;

	/* Special case for empty */
	if (opts->num == 0) {
		strlcpy(buf, "(no options)", bmax);
		goto done;
	}

	/* Print options into buffer */
	strlcpy(buf, "", bmax);
	for (i = 0; i < opts->num; i++) {
		const struct ppp_fsm_option *const opt = &opts->opts[i];
		const struct ppp_fsm_optdesc *const desc
		    = ppp_fsm_option_desc(optlist, opt);

		if (i > 0)
			strlcat(buf, " ", bmax);	/* separator */
		strlcat(buf, "[", bmax);
		if (desc == NULL) {
			snprintf(buf + strlen(buf), bmax - strlen(buf),
			    "?%u (len=%u)", opt->type, opt->len);
		} else {
			strlcat(buf, desc->name, bmax);
			if (desc->print != NULL) {
				strlcat(buf, " ", bmax);
				(*desc->print)(desc, opt,
				    buf + strlen(buf), bmax - strlen(buf));
			}
		}
		strlcat(buf, "]", bmax);
	}

done:
	/* Clean up */
	ppp_fsm_option_destroy(&opts);
}

/*
 * Find option descriptor in a table.
 */
const struct ppp_fsm_optdesc *
ppp_fsm_option_desc(const struct ppp_fsm_optdesc *optlist,
	const struct ppp_fsm_option *opt)
{
	const struct ppp_fsm_optdesc *desc;

	for (desc = optlist; desc->name != NULL; desc++) {
		if (opt->type == desc->type)
			return (desc);
	}
	return (NULL);
}

/***********************************************************************
		    PACKING/UNPACKING OPTIONS
***********************************************************************/

/*
 * Extract encoded options, stopping at the first malformed option.
 *
 * Returns NULL if there was a system error.
 */
struct ppp_fsm_options *
ppp_fsm_option_unpack(const u_char *data, u_int len)
{
	struct ppp_fsm_options *opts;

	if ((opts = ppp_fsm_option_create()) == NULL)
		return (NULL);
	while (len >= 2) {
		const u_char type = data[0];
		const u_char olen = data[1];

		if (olen < 2 || olen > len)
			break;
		if (ppp_fsm_option_add(opts, type, olen - 2, data + 2) == -1) {
			ppp_fsm_option_destroy(&opts);
			return (NULL);
		}
		data += olen;
		len -= olen;
	}
	return (opts);
}

/*
 * Compute length of packed options.
 */
u_int
ppp_fsm_option_packlen(struct ppp_fsm_options *opts)
{
	u_int len;
	int i;

	for (len = i = 0; i < opts->num; i++)
		len += 2 + opts->opts[i].len;
	return (len);
}

/*
 * Pack options into buffer.
 */
void
ppp_fsm_option_pack(struct ppp_fsm_options *opts, u_char *buf)
{
	int i;

	for (i = 0; i < opts->num; i++) {
		struct ppp_fsm_option *const opt = &opts->opts[i];

		*buf++ = opt->type;
		*buf++ = 2 + opt->len;
		memcpy(buf, opt->data, opt->len);
		buf += opt->len;
	}
}

/***********************************************************************
		BUILT-IN OPTIONS PRINTER FUNCTIONS
***********************************************************************/

#define MAX_BINARY	16

/*
 * Print option as binary data.
 */
void
ppp_fsm_pr_binary(const struct ppp_fsm_optdesc *desc,
	const struct ppp_fsm_option *opt, char *buf, size_t bmax)
{
	int i;

	for (i = 0; i < opt->len; i++) {
		if (i >= MAX_BINARY) {
			snprintf(buf + strlen(buf), bmax - strlen(buf),
			    "...");
			break;
		}
		if (i == 0)
			snprintf(buf, bmax, "%02x", opt->data[0]);
		else
			snprintf(buf + strlen(buf), bmax - strlen(buf),
			    " %02x", opt->data[i]);
	}
}

/*
 * Print option as 32 bit hex value.
 */
void
ppp_fsm_pr_hex32(const struct ppp_fsm_optdesc *desc,
	const struct ppp_fsm_option *opt, char *buf, size_t bmax)
{
	u_int32_t val;

	if (opt->len < 4) {
		snprintf(buf, bmax, "<truncated>");
		return;
	}
	memcpy(&val, opt->data, 4);
	val = ntohl(val);
	snprintf(buf, bmax, "0x%08x", val);
}

/*
 * Print option as a 16 bit hex value.
 */
void
ppp_fsm_pr_int16(const struct ppp_fsm_optdesc *desc,
	const struct ppp_fsm_option *opt, char *buf, size_t bmax)
{
	u_int16_t val;

	if (opt->len < 2) {
		snprintf(buf, bmax, "<truncated>");
		return;
	}
	memcpy(&val, opt->data, 2);
	val = ntohs(val);
	snprintf(buf, bmax, "%u", val);
}


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>