/* Low level AX.25 frame processing - address header */

#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "iface.h"
#include "timer.h"
#include "arp.h"
#include "slip.h"
#include "ax25.h"
#include "lapb.h"
#include <ctype.h>

/* AX.25 broadcast address: "QST-0" in shifted ascii */
struct ax25_addr ax25_bdcst = {
	'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1,
	('0'<<1) | E,
};
char axbdcst[AXALEN];	/* Same thing, network format */
struct ax25_addr mycall;
int digipeat = 1;	/* Controls digipeating */

int digisent = 0;	/* DG2KK: counts frames to be digipeated */
/* Send IP datagrams across an AX.25 link */
int
ax_send(bp,interface,gateway,precedence,delay,throughput,reliability)
struct mbuf *bp;
struct interface *interface;
int32 gateway;
char precedence;
char delay;
char throughput;
char reliability;
{
	char *hw_addr,*res_arp();
	struct ax25_cb *axp,*find_ax25(),*open_ax25();
	struct ax25 addr;
	struct ax25_addr destaddr;
	struct mbuf *tbp;
	extern int16 axwindow;
	void ax_incom();
	int16 size,bsize,seq;

	if((hw_addr = res_arp(interface,ARP_AX25,gateway,bp)) == NULLCHAR)
		return;	/* Wait for address resolution */

	if(delay || (!reliability && (interface->flags == DATAGRAM_MODE))){
		/* Use UI frame */
		(*interface->output)(interface,hw_addr,
			interface->hwaddr,PID_FIRST|PID_LAST|PID_IP,bp);
		return;
	}
	/* Reliability is needed; use I-frames in AX.25 connection */
	memcpy(destaddr.call,hw_addr,ALEN);
	destaddr.ssid = hw_addr[ALEN];

	if((axp = find_ax25(&destaddr)) == NULLAX25 || axp->state != CONNECTED){
		/* Open a new connection or reinitialize the old one */
		atohax25(&addr,hw_addr,(struct ax25_addr *)interface->hwaddr);
		axp = open_ax25(&addr,axwindow,ax_incom,NULLVFP,NULLVFP,interface,(char *)0);
		if(axp == NULLAX25){
			free_p(bp);
			return;
		}
	}
	/* If datagram is too big for one frame, send all but the last with
	 * the extension PID. Note: the copy to a new buf is necessary because
	 * AX.25 may continue retransmitting the frame after a local TCB has
	 * gone away, and using the buf directly would cause heap garbage to be
	 * transmitted. Besides, nobody would ever use AX.25 anywhere
	 * high performance is needed anyway...
	 */
	bsize = len_mbuf(bp);
	seq = 0;
	while(bsize != 0){
		size = min(bsize,axp->paclen);
		/* Allocate buffer, allowing space for PID */
		if((tbp = alloc_mbuf(size + 1)) == NULLBUF)
			break;		/* out of memory! */
		*tbp->data = PID_IP;
		if(seq++ == 0)
			*tbp->data |= PID_FIRST;	 /* First in sequence */
		if(size == bsize)
			*tbp->data |= PID_LAST;		/* That's all of it */
		/* else more to follow */

		tbp->cnt = 1;
		tbp->cnt += pullup(&bp,tbp->data + 1,size);
		send_ax25(axp,tbp);
		bsize -= size;
	}
	free_p(bp);	/* Shouldn't be necessary */
}
/* Add AX.25 link header and send packet.
 * Note that the calling order here must match ec_output
 * since ARP also uses it.
 */
ax_output(interface,dest,source,pid,bp)
struct interface *interface;
char *dest;		/* Destination AX.25 address (7 bytes, shifted) */
			/* Also includes digipeater string */
char *source;		/* Source AX.25 address (7 bytes, shifted) */
char pid;		/* Protocol ID */
struct mbuf *bp;	/* Data field (follows PID) */
{
	struct mbuf *abp,*cbp,*htonax25();
	struct ax25 addr;
	
	atohax25(&addr,dest,(struct ax25_addr *)source);
	if((abp = htonax25(&addr)) == NULLBUF){
		free_p(bp);
		return;
	}
	/* Allocate mbuf for control and PID fields, and fill in */
	if((cbp = alloc_mbuf((int16)2)) == NULLBUF){
		free_p(abp);
		free_p(bp);
		return;
	}
	cbp->data[0] = UI;
	cbp->data[1] = pid;
	cbp->cnt = 2;

	/* Link everything together and ship it */
	abp->next = cbp;
	cbp->next = bp;
	(*interface->raw)(interface,abp);
}
/* Process incoming AX.25 packets.
 * After optional tracing, the address field is examined. If it is
 * directed to us as a digipeater, repeat it.  If it is addressed to
 * us or to QST-0, kick it upstairs depending on the protocol ID.
 */
int
ax_recv(interface,bp)
struct interface *interface;
struct mbuf *bp;
{
	void ip_route(),arp_input();
	struct ax25_addr *ap;
	struct mbuf *htonax25(),*hbp;
	char multicast = 0;
	char nrnodes = 0;		/* DG2KK */
	char control;
	struct ax25 hdr;
	struct ax25_cb *axp,*find_ax25(),*cr_ax25();
	struct ax25_addr ifcall;
	extern struct ax25_addr nr_nodebc;

	/* we need to use the call for this interface */
	memcpy(ifcall.call,interface->hwaddr,ALEN);
	ifcall.ssid = interface->hwaddr[ALEN];

	/* Pull header off packet and convert to host structure */
	if(ntohax25(&hdr,&bp) < 0){
		/* Something wrong with the header */
		free_p(bp);
		return;
	}
	/* Scan, looking for our call in the repeater fields, if any.
	 * Repeat appropriate packets.
	 */
	for(ap = &hdr.digis[0]; ap < &hdr.digis[hdr.ndigis]; ap++){
		if(ap->ssid & REPEATED)
			continue;	/* Already repeated */
		/* Count frames to be digipeated, even if digipeat is off
		 * (DG2KK)
		 */
		if (addreq(ap,&mycall))
			digisent++;
		/* Check if packet is directed to us as a digipeater */
		if(digipeat && addreq(ap,&mycall)){
			/* Yes, kick it back out */
			ap->ssid |= REPEATED;
			if((hbp = htonax25(&hdr)) != NULLBUF){
				hbp->next = bp;
				if(interface->forw != NULLIF)
					(*interface->forw->raw)(interface->forw,hbp);
				else
					(*interface->raw)(interface,hbp);
				bp = NULLBUF;
			}
		}
		free_p(bp);	/* Dispose if not forwarded */
		return;
	}
	/* Packet has passed all repeaters, now look at destination */
	if(addreq(&hdr.dest,&ax25_bdcst)){
		multicast = 1;	/* Broadcast packet */
	} else if(addreq(&hdr.dest,&mycall)){	/* DG2KK: was: &ifcall */
		multicast = 0;	/* Packet directed at us */
	} else if(addreq(&hdr.dest,&nr_nodebc)) {
		nrnodes = 1;
	} else {
		/* Not for us */
		free_p(bp);
		return;
	}
	if(bp == NULLBUF){
		/* Nothing left */
		return;
	}
	/* Sneak a peek at the control field. This kludge is necessary because
	 * AX.25 lacks a proper protocol ID field between the address and LAPB
	 * sublayers; a control value of UI indicates that LAPB is to be
	 * bypassed.
	 */
	control = *bp->data & ~PF;
	if(control == UI){
		char pid;

		(void) pullchar(&bp);
		if(pullup(&bp,&pid,1) != 1)
			return;		/* No PID */
		/* DG2KK: new from netrom version */
		if (nrnodes) {
			if ((pid & (PID_FIRST | PID_LAST | PID_PID)) ==
				(PID_NETROM | PID_FIRST | PID_LAST))
				nr_nodercv(interface,&hdr.source,bp);
			else	/* regular UI packets to "nodes" aren't for us */
				free_p(bp);
			return;
		}
		else {
		/* Handle packets. Multi-frame messages are not allowed */
		switch(pid & (PID_FIRST | PID_LAST | PID_PID)){
		case (PID_IP | PID_FIRST | PID_LAST):
			ip_route(bp,multicast);
			break;
		case (PID_ARP | PID_FIRST | PID_LAST):
			arp_input(interface,bp);
			break;
		default:
			free_p(bp);
			break;
		}
		return;
		}
	}

	/* If the packet was an I packet not directed to us,
	 * we should drop it here (shouldn't we, Phil?)
	 */
	if (nrnodes || multicast) {
		free_p(bp);
		return;
	}

	/* Find the source address in hash table */
	if((axp = find_ax25(&hdr.source)) == NULLAX25){
		/* Create a new ax25 entry for this guy,
		 * insert into hash table keyed on his address,
		 * and initialize table entries
		 */
		if((axp = cr_ax25(&hdr.source)) == NULLAX25){
			free_p(bp);
			return;
		}
		axp->interface = interface;
		/* Swap source and destination, reverse digi string */
		ASSIGN(axp->addr.dest,hdr.source);
		ASSIGN(axp->addr.source,hdr.dest);
		if(hdr.ndigis > 0){
			int i,j;

			/* Construct reverse digipeater path */
			for(i=hdr.ndigis-1,j=0;i >= 0;i--,j++){
				ASSIGN(axp->addr.digis[j],hdr.digis[i]);
				axp->addr.digis[j].ssid &= ~(E|REPEATED);
			}
			/* Scale timers to account for extra delay */
			axp->t1.start *= hdr.ndigis+1;
			axp->t2.start *= hdr.ndigis+1;
			axp->t3.start *= hdr.ndigis+1;
		}
		axp->addr.ndigis = hdr.ndigis;
	}
	if(hdr.cmdrsp == UNKNOWN)
		axp->proto = V1;	/* Old protocol in use */
	else
		axp->proto = V2;

	lapb_input(axp,hdr.cmdrsp,bp);
}

/* General purpose AX.25 frame output */
sendframe(axp,cmdrsp,ctl,bp)
struct ax25_cb *axp;
char cmdrsp;
char ctl;
struct mbuf *bp;
{
	struct mbuf *hbp,*cbp,*htonax25();

	if(axp == NULL){
		printf("NULL AXP!!\n");
		return;
	}
	if(axp->interface == NULLIF){
		printf("NULL IF PTR!!\n");
		return;
	}
	axp->addr.cmdrsp = cmdrsp;
	/* Create address header */
	hbp = htonax25(&axp->addr);

	/* Add control field */
	if((cbp = alloc_mbuf(1)) == NULLBUF){
		free_p(bp);
		free_p(hbp);
		return;
	}
	*cbp->data = ctl;
	cbp->cnt = 1;

	/* Link together and send */
	cbp->next = bp;
	hbp->next = cbp;
	if(axp->interface->raw != NULL)
		(*axp->interface->raw)(axp->interface,hbp);
	else {
		printf("NULL RAW POINTER!!\n");
		fflush(stdout);
	}
	if(cmdrsp == COMMAND)
		start_timer(&axp->t1);
}
/* Initialize AX.25 entry in arp device table */
axarp()
{
	int psax25(),setpath();

	memcpy(axbdcst,ax25_bdcst.call,ALEN);
	axbdcst[ALEN] = ax25_bdcst.ssid;

	arp_init(ARP_AX25,AXALEN,PID_FIRST|PID_LAST|PID_IP,
	 PID_FIRST|PID_LAST|PID_ARP,axbdcst,psax25,setpath);
}
