/* AX25 header conversion routines
 * Copyright 1991 Phil Karn, KA9Q
 */
#include "global.h"
#include "mbuf.h"
#include "ax25.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: ax25hdr.c,v 1.7 1996/08/29 12:11:16 root Exp $";
#endif


/* Convert a host-format AX.25 header into a mbuf ready for transmission */
struct mbuf *
htonax25 (hdr, data)
register struct ax25 *hdr;
struct mbuf *data;
{
struct mbuf *bp;
register unsigned char *cp;
register int16 i;

	if (hdr == (struct ax25 *) NULL || hdr->ndigis > MAXDIGIS)
		return NULLBUF;

	/* Allocate space for return buffer */
	i = (int16) (AXALEN * (2 + hdr->ndigis));
	if ((bp = pushdown (data, i)) == NULLBUF)
		return NULLBUF;

	/* Now convert */
	cp = bp->data;		/* cp -> dest field */

	/* Generate destination field */
	memcpy (cp, hdr->dest, AXALEN);
	if (hdr->cmdrsp == LAPB_COMMAND)
		cp[ALEN] |= C;	/* Command frame sets C bit in dest */
	else
		cp[ALEN] &= ~C;
	cp[ALEN] &= ~E;		/* Dest E-bit is always off */

	cp += AXALEN;		/* cp -> source field */

	/* Generate source field */
	memcpy (cp, hdr->source, AXALEN);
	if (hdr->cmdrsp == LAPB_RESPONSE)
		cp[ALEN] |= C;
	else
		cp[ALEN] &= ~C;
	/* Set E bit on source address if no digis */
	if (hdr->ndigis == 0) {
		cp[ALEN] |= E;
		return bp;
	}
	cp += AXALEN;		/* cp -> first digi field */

	/* All but last digi get copied with E bit off */
	for (i = 0; i < hdr->ndigis; i++) {
		memcpy (cp, hdr->digis[i], AXALEN);
		if (i < hdr->ndigis - 1)
			cp[ALEN] &= ~E;
		else
			cp[ALEN] |= E;	/* Last digipeater has E bit set */
		if (i < hdr->nextdigi) {
			cp[ALEN] |= REPEATED;
		} else
			cp[ALEN] &= ~REPEATED;
		cp += AXALEN;	/* cp -> next digi field */
	}
	return bp;
}


/* Convert a network-format AX.25 header into a host format structure
 * Return -1 if error, number of addresses if OK
 */
int
ntohax25 (hdr, bpp)
register struct ax25 *hdr;	/* Output structure */
struct mbuf **bpp;
{
register char *axp;

	if (pullup (bpp, (unsigned char *) hdr->dest, AXALEN) < AXALEN)
		return -1;

	if (pullup (bpp, (unsigned char *) hdr->source, AXALEN) < AXALEN)
		return -1;

	/* Process C bits to get command/response indication */
	if ((hdr->source[ALEN] & C) == (hdr->dest[ALEN] & C))
		hdr->cmdrsp = LAPB_UNKNOWN;
	else if (hdr->source[ALEN] & C)
		hdr->cmdrsp = LAPB_RESPONSE;
	else
		hdr->cmdrsp = LAPB_COMMAND;

	hdr->ndigis = 0;
	hdr->nextdigi = 0;
	if (hdr->source[ALEN] & E)
		return 2;	/* No digis */

	/* Count and process the digipeaters */
	axp = hdr->digis[0];
	while (hdr->ndigis < MAXDIGIS && pullup (bpp, (unsigned char *) axp, AXALEN) == AXALEN) {
		hdr->ndigis++;
		if (axp[ALEN] & REPEATED)
			hdr->nextdigi++;
		if (axp[ALEN] & E)	/* Last one */
			return hdr->ndigis + 2;
		axp += AXALEN;
	}
	return -1;		/* Too many digis */
}
