/* Low level AX.25 code:
 *  incoming frame processing (including digipeating)
 *  IP encapsulation
 *  digipeater routing
 *
 * Copyright 1991 Phil Karn, KA9Q
 */
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "iface.h"
#include "arp.h"
#include "slip.h"
#include "ax25.h"
#include "lapb.h"
#include "netrom.h"
#include "ip.h"
#include <ctype.h>

static int axsend __ARGS((struct iface *iface,char *dest,char *source,
	int cmdrsp,int ctl,struct mbuf *data));

/* AX.25 broadcast address: "QST-0" in shifted ascii */
char Ax25_bdcst[AXALEN] = {
	'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1, '0'<<1,
};
char Mycall[AXALEN];
struct ax_route *Ax_routes[NHASH];	/* Routing table header */
int Digipeat = 1;	/* Controls digipeating */

/* Send IP datagrams across an AX.25 link */
int
ax_send(bp,iface,gateway,prec,del,tput,rel)
struct mbuf *bp;
struct iface *iface;
int32 gateway;
int prec;
int del;
int tput;
int rel;
{
	char *hw_addr;
	struct ax25_cb *axp;
	struct mbuf *tbp;

	if((hw_addr = res_arp(iface,ARP_AX25,gateway,bp)) == NULLCHAR)
		return 0;	/* Wait for address resolution */

	/* UI frames are used for any one of the following three conditions:
	 * 1. The "low delay" bit is set in the type-of-service field.
	 * 2. The "reliability" TOS bit is NOT set and the interface is in
	 *    datagram mode.
	 * 3. The destination is the broadcast address (this is helpful
	 *    when broadcasting on an interface that's in connected mode).
	 */
	if(del || (!rel && (iface->flags == DATAGRAM_MODE))
	 || addreq(hw_addr,Ax25_bdcst)){
		/* Use UI frame */
		return (*iface->output)(iface,hw_addr,iface->hwaddr,PID_IP,bp);
	}
	/* Reliability is needed; use I-frames in AX.25 connection */
	if((axp = find_ax25(hw_addr)) == NULLAX25){
		/* Open a new connection */
		axp = open_ax25(iface,iface->hwaddr,hw_addr,
		 AX_ACTIVE,Axwindow,s_arcall,s_atcall,s_ascall,-1);
		if(axp == NULLAX25){
			free_p(bp);
			return -1;
		}
	}
	if(axp->state == LAPB_DISCONNECTED){
		est_link(axp);
		lapbstate(axp,LAPB_SETUP);
	}
	/* Insert the PID */
	if((tbp = pushdown(bp,1)) == NULLBUF){
		free_p(bp);
		return -1;
	}
	bp = tbp;
	bp->data[0] = PID_IP;
	if((tbp = segmenter(bp,axp->paclen)) == NULLBUF){
		free_p(bp);
		return -1;
	}
	return send_ax25(axp,tbp,-1);
}
/* Add header and send connectionless (UI) AX.25 packet.
 * Note that the calling order here must match enet_output
 * since ARP also uses it.
 */
int
ax_output(iface,dest,source,pid,data)
struct iface *iface;	/* Interface to use; overrides routing table */
char *dest;		/* Destination AX.25 address (7 bytes, shifted) */
char *source;		/* Source AX.25 address (7 bytes, shifted) */
int16 pid;		/* Protocol ID */
struct mbuf *data;	/* Data field (follows PID) */
{
	struct mbuf *bp;

	/* Prepend pid to data */
	bp = pushdown(data,1);
	if(bp == NULLBUF){
		free_p(data);
		return -1;
	}
	bp->data[0] = (char)pid;
	return axsend(iface,dest,source,LAPB_COMMAND,UI,bp);
}
/* Common subroutine for sendframe() and ax_output() */
static int
axsend(iface,dest,source,cmdrsp,ctl,data)
struct iface *iface;	/* Interface to use; overrides routing table */
char *dest;		/* Destination AX.25 address (7 bytes, shifted) */
char *source;		/* Source AX.25 address (7 bytes, shifted) */
int cmdrsp;		/* Command/response indication */
int ctl;		/* Control field */
struct mbuf *data;	/* Data field (includes PID) */
{
	struct mbuf *cbp;
	struct ax25 addr;
	struct ax_route *axr;
	int rval;

	/* If the source addr is unspecified, use the interface address */
	if(source[0] == '\0')
		source = iface->hwaddr;

	/* If there's a digipeater route, get it */
	axr = ax_lookup(dest);

	memcpy(addr.dest,dest,AXALEN);
	memcpy(addr.source,source,AXALEN);
	addr.cmdrsp = cmdrsp;

	if(axr != NULLAXR){
		memcpy(addr.digis,axr->digis,axr->ndigis*AXALEN);
		addr.ndigis = axr->ndigis;
	} else
		addr.ndigis = 0;

	addr.nextdigi = 0;

	/* Allocate mbuf for control field, and fill in */
	if((cbp = pushdown(data,1)) == NULLBUF){
		free_p(data);
		return -1;
	}
	cbp->data[0] = ctl;

	if((data = htonax25(&addr,cbp)) == NULLBUF){
		free_p(cbp);	/* Also frees data */
		return -1;
	}
	/* This shouldn't be necessary because redirection has already been
	 * done at the IP router layer, but just to be safe...
	 */
	if(iface->forw != NULLIF){
		rval = (*iface->forw->raw)(iface->forw,data);
	} else {
		rval = (*iface->raw)(iface,data);
	}
	return rval;
}
/* 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.
 */
void
ax_recv(iface,bp)
struct iface *iface;
struct mbuf *bp;
{
	struct mbuf *hbp;
	char control;
	struct ax25 hdr;
	struct ax25_cb *axp;
	struct ax_route *axr;
	char **mpp;
	int mcast;

	/* Pull header off packet and convert to host structure */
	if(ntohax25(&hdr,&bp) < 0){
		/* Something wrong with the header */
		free_p(bp);
		return;
	}
	/* If there were digis in this packet, then the last digi was the
	 * actual transmitter. Otherwise the source is the transmitter.
	 */
	if(hdr.ndigis != 0)
		logaddr(iface,hdr.digis[hdr.nextdigi-1]);
	else
		logaddr(iface,hdr.source);

	if(hdr.nextdigi < hdr.ndigis){
		/* Packet hasn't passed all digipeaters yet. See if
		 * we have to repeat it.
		 */
		if(Digipeat && addreq(hdr.digis[hdr.nextdigi],iface->hwaddr)){
			/* Yes, kick it back out. htonax25 will set the
			 * repeated bit.
			 */
			hdr.nextdigi++;
			if((hbp = htonax25(&hdr,bp)) != NULLBUF){
				if(iface->forw != NULLIF){
					(*iface->forw->raw)(iface->forw,hbp);
				} else {
					(*iface->raw)(iface,hbp);
				}
				bp = NULLBUF;
			}
		}
		free_p(bp);	/* Dispose if not forwarded */
		return;
	}
	/* If we reach this point, then the packet has passed all digis,
	 * but it is not necessarily for us.
	 */
	if(bp == NULLBUF){
		/* Nothing left */
		return;
	}
	/* Examine destination to see if it's either addressed to us or
	 * a multicast.
	 */
	mcast = 0;
	for(mpp = Axmulti;*mpp != NULLCHAR;mpp++){
		if(addreq(hdr.dest,*mpp)){
			mcast = 1;
			break;
		}
	}
	if(!mcast && !addreq(hdr.dest,iface->hwaddr)){
		/* Not a broadcast, and not addressed to us; ignore */
		free_p(bp);
		return;
	}
	/* If there's no locally-set entry in the routing table and
	 * this packet has digipeaters, create or update it. Leave
	 * local routes alone.
	 */
	if(((axr = ax_lookup(hdr.source)) == NULLAXR || axr->type == AX_AUTO)
	 && hdr.ndigis > 0){
		char digis[MAXDIGIS][AXALEN];
		int i,j;

		/* Construct reverse digipeater path */
		for(i=hdr.ndigis-1,j=0;i >= 0;i--,j++){
			memcpy(digis[j],hdr.digis[i],AXALEN);
			digis[j][ALEN] &= ~(E|REPEATED);
		}
		ax_add(hdr.source,AX_AUTO,digis,hdr.ndigis);
	}
	/* 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(uchar(control) == UI){
		int pid;
		struct axlink *ipp;

		(void) PULLCHAR(&bp);
		if((pid = PULLCHAR(&bp)) == -1)
			return;		/* No PID */
		/* Find network level protocol and hand it off */
		for(ipp = Axlink;ipp->funct != NULL;ipp++){
			if(ipp->pid == pid)
				break;
		}
		if(ipp->funct != NULL)
			(*ipp->funct)(iface,NULLAX25,hdr.source,hdr.dest,bp,mcast);
		else
			free_p(bp);
		return;
	}
	/* Everything from here down is connected-mode LAPB, so ignore
	 * multicasts
	 */
	if(mcast){
		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;
		}
		/* Swap source and destination */
		memcpy(axp->remote,hdr.source,AXALEN);
		memcpy(axp->local,hdr.dest,AXALEN);
		axp->iface = iface;
	}
	if(hdr.cmdrsp == LAPB_UNKNOWN)
		axp->proto = V1;	/* Old protocol in use */

	lapb_input(axp,hdr.cmdrsp,bp);
}
/* General purpose AX.25 frame output */
int
sendframe(axp,cmdrsp,ctl,data)
struct ax25_cb *axp;
int cmdrsp;
int ctl;
struct mbuf *data;
{
	return axsend(axp->iface,axp->remote,axp->local,cmdrsp,ctl,data);
}
/* Find a route for an AX.25 address */
struct ax_route *
ax_lookup(target)
char *target;
{
	register struct ax_route *axr;

	for(axr = Ax_routes[ax25hash(target)]; axr != NULLAXR; axr = axr->next){
		if(addreq(axr->target,target))
			break;
	}
	return axr;
}
/* Add an entry to the AX.25 routing table */
struct ax_route *
ax_add(target,type,digis,ndigis)
char *target;
int type;
char digis[][AXALEN];
int ndigis;
{
	int16 hval;
	register struct ax_route *axr;

	if(ndigis < 0 || ndigis > MAXDIGIS)
		return NULLAXR;

	if((axr = ax_lookup(target)) == NULLAXR){
		axr = (struct ax_route *)callocw(1,sizeof(struct ax_route));
		hval = ax25hash(target);
		axr->prev = NULLAXR;
		axr->next = Ax_routes[hval];
		if(axr->next != NULLAXR)
			axr->next->prev = axr;
		Ax_routes[hval] = axr;
		memcpy(axr->target,target,AXALEN);
		axr->ndigis = ndigis;
	}
	axr->type = type;
	if(axr->ndigis != ndigis)
		axr->ndigis = ndigis;

	memcpy(axr->digis,digis[0],ndigis*AXALEN);
	return axr;
}
int
ax_drop(target)
char *target;
{
	register struct ax_route *axr;

	if((axr = ax_lookup(target)) == NULLAXR)
		return -1;

	if(axr->next != NULLAXR)
		axr->next->prev = axr->prev;
	if(axr->prev != NULLAXR)
		axr->prev->next = axr->next;
	else
		Ax_routes[ax25hash(target)] = axr->next;
	free((char *)axr);
	return 0;
}
/* Handle ordinary incoming data (no network protocol) */
void
axnl3(iface,axp,src,dest,bp,mcast)
struct iface *iface;
struct ax25_cb *axp;
char *src;
char *dest;
struct mbuf *bp;
int mcast;
{
	if(axp == NULLAX25){
		beac_input(iface,src,bp);
	} else {
		append(&axp->rxq,bp);
		if(axp->r_upcall != NULLVFP)
			(*axp->r_upcall)(axp,len_p(axp->rxq));
	}
}
