#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "ax25.h"
#include "netrom.h"
#include "lapb.h"
#include <ctype.h>

/* Functions for level 3 and 4 net/rom support */

/* Convert a net/rom network header to host format structure
 * Return -1 if error, 0 if OK
 */

int
ntohnr3(hdr,bpp)
register struct nr3hdr *hdr;	/* output structure */
struct mbuf **bpp;
{
	char *getaxaddr();
	char buf[AXALEN];
	unsigned char ttl;

	if (pullup(bpp,buf,AXALEN) < AXALEN)
		return -1;
	getaxaddr(&hdr->source,buf);

	if (pullup(bpp,buf,AXALEN) < AXALEN)
		return -1;
	getaxaddr(&hdr->dest,buf);

	if (pullup(bpp,&ttl,1) != 1)
		return -1;
	hdr->ttl = ttl;

	return 0;
}

/* Convert a host-format net/rom level 3 header into an mbuf ready
 * for transmission.
 */

struct mbuf *
htonnr3(hdr)
register struct nr3hdr *hdr;
{
	struct mbuf *rbuf;
	register char *cp;
	char *putaxaddr();

#ifdef DEBUG
	if (hdr == (struct nr3hdr *) NULL)
		return NULLBUF;
#endif

	/* Allocate space for return buffer */
	if ((rbuf = alloc_mbuf(NR3HLEN)) == NULLBUF)
		return NULLBUF;

	rbuf->cnt = NR3HLEN;

	/* Now convert */
	cp = rbuf->data;

	hdr->source.ssid &= ~E;		/* source E-bit is always off */
	hdr->dest.ssid |= E;		/* destination E-bit always set */

	cp = putaxaddr(cp,&hdr->source);
	cp = putaxaddr(cp,&hdr->dest);
	*cp = hdr->ttl;

	return rbuf;
}

/* Convert a net/rom routing broadcast destination subpacket from
 * network format to a host format structure.  Return -1 if error,
 * 0 if OK.
 */
int ntohnrdest(ds,bpp)
register struct nr3dest *ds;
struct mbuf **bpp;
{
	char buf[AXALEN];
	char quality;

	/* get destination callsign */
	if (pullup(bpp,buf,AXALEN) < AXALEN)
		return -1;
	memcpy(ds->dest.call,buf,ALEN);
	ds->dest.ssid = buf[ALEN];

	/* get destination alias */
	if (pullup(bpp,ds->alias,ALEN) < ALEN)
		return -1;
	ds->alias[ALEN] = '\0';

	/* get best neighbor callsign */
	if (pullup(bpp,buf,AXALEN) < AXALEN)
		return -1;
	memcpy(ds->neighbor.call,buf,ALEN);
	ds->neighbor.ssid = buf[ALEN];

	/* get route quality */
	if (pullup(bpp,&quality,1) < 1)
		return -1;
	ds->quality = uchar(quality);

	return 0;
}

/* Transfer a host-format net/rom destination subpacket into a
 * buffer ready for transmission as part of a route broadcast
 * packet. Returns next value of buffer pointer.
 */
char *
htonnrdest(cp,ds)
register char *cp;
register struct nr3dest *ds;
{
#ifdef DEBUG
	if (ds == (struct nr3dest *) NULL)
		return NULLCHAR;
#endif

	memcpy(cp,ds->dest.call,ALEN);
	cp += ALEN;
	*cp++ = ds->dest.ssid;

	memcpy(cp,ds->alias,ALEN);
	cp += ALEN;

	memcpy(cp,ds->neighbor.call,ALEN);
	cp += ALEN;
	*cp++ = ds->neighbor.ssid;

	*cp++ = uchar(ds->quality);

	return cp;
}

/* Convert a net/rom transport header to host format structure
 * Return -1 if error, 0 if OK
 */

int
ntohnr4(hdr,bpp)
register struct nr4hdr *hdr;	/* output structure */
struct mbuf **bpp;
{
	unsigned char buf[max(NR4HLEN,AXALEN)];

	if (pullup(bpp,buf,NR4HLEN) < NR4HLEN)
		return -1;

	memset(hdr,0,sizeof(struct nr4hdr));
	hdr->opcode = buf[4];

	switch (buf[4] & NR4OMASK) {	/* look at opcode byte, lower 4 bits */
	case NR4PID:			/* network protocol extension */
		hdr->family = buf[0];
		hdr->proto = buf[1];
		break;

	case NR4CONRQ:			/* connect request */
		hdr->my.index = buf[0];
		hdr->my.id = buf[1];

		if (pullup(bpp,buf,1) != 1)
			return -1;
		hdr->window = buf[0];

		if (pullup(bpp,buf,AXALEN) < AXALEN)
			return -1;
		getaxaddr(&hdr->suser,buf);

		if (pullup(bpp,buf,AXALEN) < AXALEN)
			return -1;
		getaxaddr(&hdr->snode,buf);
		break;

	case NR4CONACK:			/* connect acknowledge */
		hdr->your.index = buf[0];
		hdr->your.id = buf[1];
		hdr->my.index = buf[2];
		hdr->my.id = buf[3];
		if (pullup(bpp,buf,1) != 1)
			return -1;
		hdr->window = buf[0];
		break;

	case NR4DISRQ:			/* disconnect request */
	case NR4DISACK:			/* disconnect acknowledge */
		hdr->your.index = buf[0];
		hdr->your.id = buf[1];
		break;

	case NR4INFO:			/* information */
		hdr->your.index = buf[0];
		hdr->your.id = buf[1];
		hdr->txseq = buf[2];
		hdr->rxseq = buf[3];
		break;

	case NR4INFACK:			/* information acknowledge */
		hdr->your.index = buf[0];
		hdr->your.id = buf[1];
		hdr->rxseq = buf[3];
		break;

	default:			/* unknown opcode */
		return -1;
	}
	return 0;
}

/* Convert a host-format net/rom level 4 header into an mbuf ready
 * for transmission.
 */

struct mbuf *
htonnr4(hdr)
register struct nr4hdr *hdr;
{
	register struct mbuf *rbuf;

#ifdef DEBUG
	if (hdr == (struct nr4hdr *) NULL)
		return NULLBUF;
#endif

	/* Allocate space for return buffer */
	/* (one extra for CONRQ/CONACK plus 14 more for CONRQ) */

	if ((rbuf = alloc_mbuf(NR4HLEN + 1 +
			       ((hdr->opcode == NR4CONRQ)? 14:0))) == NULLBUF)
		return NULLBUF;

	rbuf->cnt = NR4HLEN;
	memset(rbuf->data,0,NR4HLEN - 1);
	rbuf->data[4] = hdr->opcode;

	switch (rbuf->data[4] & NR4OMASK) {	/* look at opcode byte, lower 4 bits */
	case NR4PID:			/* network protocol extension */
		rbuf->data[0] = hdr->family;
		rbuf->data[1] = hdr->proto;
		break;

	case NR4CONRQ:			/* connect request */
		rbuf->data[0] = hdr->my.index;
		rbuf->data[1] = hdr->my.id;
		rbuf->data[5] = hdr->window;
		putaxaddr(&rbuf->data[6],&hdr->suser);
		putaxaddr(&rbuf->data[13],&hdr->snode);
		rbuf->cnt += 15;
		break;

	case NR4CONACK:			/* connect acknowledge */
		rbuf->data[0] = hdr->your.index;
		rbuf->data[1] = hdr->your.id;
		rbuf->data[2] = hdr->my.index;
		rbuf->data[3] = hdr->my.id;
		rbuf->data[5] = hdr->window;
		rbuf->cnt++;
		break;

	case NR4DISRQ:			/* disconnect request */
	case NR4DISACK:		/* disconnect acknowledge */
		rbuf->data[0] = hdr->your.index;
		rbuf->data[1] = hdr->your.id;
		break;

	case NR4INFO:			/* information */
		rbuf->data[0] = hdr->your.index;
		rbuf->data[1] = hdr->your.id;
		rbuf->data[2] = hdr->txseq;
		rbuf->data[3] = hdr->rxseq;
		break;

	case NR4INFACK:			/* information acknowledge */
		rbuf->data[0] = hdr->your.index;
		rbuf->data[1] = hdr->your.id;
		rbuf->data[3] = hdr->rxseq;
		break;

	default:			/* unknown opcode */
		return free_p(rbuf);
	}

	return rbuf;
}
