/*
 * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *	@(#)if_imphost.c	7.9 (Berkeley) 6/29/88
 */

#include "imp.h"
#if NIMP > 0
/*
 * Host table manipulation routines.
 * Only needed when shipping stuff through an IMP.
 *
 * Everything in here is called at splimp from
 * from the IMP protocol code (if_imp.c), or
 * interlocks with the code at splimp.
 */
#include "param.h"
#include "mbuf.h"
#include "socket.h"
#include "syslog.h"

#include "../net/if.h"

#include "../netinet/in.h"
#include "../netinet/in_systm.h"

#include "if_imp.h"
#include "if_imphost.h"

extern struct imp_softc imp_softc[];

/*
 * Given an internet address
 * return a host structure (if it exists).
 */
struct host *
hostlookup(imp, host, unit)
	int imp, host, unit;
{
	register struct host *hp;
	register struct mbuf *m;
	register int hash = HOSTHASH(imp, host);

	for (m = imp_softc[unit].imp_hosts; m; m = m->m_next) {
		hp = &mtod(m, struct hmbuf *)->hm_hosts[hash];
	        if (hp->h_imp == imp && hp->h_host == host) {
			if ((hp->h_flags & HF_INUSE) == 0)
				mtod(dtom(hp), struct hmbuf *)->hm_count++;
			hp->h_flags |= HF_INUSE;
			return (hp);
		}
	}
	return ((struct host *)0);
}

/*
 * Enter a reference to this host's internet
 * address.  If no host structure exists, create
 * one and hook it into the host database.
 */
struct host *
hostenter(imp, host, unit)
	int imp, host, unit;
{
	register struct mbuf *m, **mprev;
	register struct host *hp, *hp0 = 0;
	register int hash = HOSTHASH(imp, host);

	mprev = &imp_softc[unit].imp_hosts;
	while (m = *mprev) {
		mprev = &m->m_next;
		hp = &mtod(m, struct hmbuf *)->hm_hosts[hash];
	        if (hp->h_imp == imp && hp->h_host == host) {
			if ((hp->h_flags & HF_INUSE) == 0)
				mtod(dtom(hp), struct hmbuf *)->hm_count++;
			goto foundhost;
		}
		if ((hp->h_flags & HF_INUSE) == 0) {
			if (hp0 == 0)
				hp0 = hp;
			continue;
		}
	}

	/*
	 * No current host structure, make one.
	 * If our search ran off the end of the
	 * chain of mbuf's, allocate another.
	 */
	if (hp0 == 0) {
		m = m_getclr(M_DONTWAIT, MT_HTABLE);
		if (m == NULL)
			return ((struct host *)0);
		*mprev = m;
		hp0 = &mtod(m, struct hmbuf *)->hm_hosts[hash];
	}
	hp = hp0;
	mtod(dtom(hp), struct hmbuf *)->hm_count++;
	hp->h_imp = imp;
	hp->h_host = host;
	hp->h_timer = 0;
	hp->h_flags = 0;

foundhost:
	hp->h_flags |= HF_INUSE;
	return (hp);
}

/*
 * Reset a given imp unit's host entries.
 * Must be called at splimp.
 */
hostreset(unit)
	int unit;
{
	register struct mbuf *m;
	register struct host *hp, *lp;
	struct hmbuf *hm;

	for (m = imp_softc[unit].imp_hosts; m; m = m->m_next) {
		hm = mtod(m, struct hmbuf *);
		hp = hm->hm_hosts; 
		lp = hp + HPMBUF;
		while (hm->hm_count > 0 && hp < lp) {
			hostrelease(hp);
			hp++;
		}
	}
	hostcompress(unit);
}

/*
 * Remove a host structure and release
 * any resources it's accumulated.
 */
hostrelease(hp)
	register struct host *hp;
{

	if (hp->h_q)
		hostflush(hp);
	hp->h_rfnm = 0;
	if (hp->h_flags & HF_INUSE)
		--mtod(dtom(hp), struct hmbuf *)->hm_count;
	hp->h_flags = 0;
}

/*
 * Flush the message queue for a host.
 */
hostflush(hp)
	register struct host *hp;
{
	register struct mbuf *m;

	/*
	 * Discard any packets left on the waiting q
	 */
	if (m = hp->h_q) {
		register struct mbuf *n;

		do {
			n = m->m_act;
			m_freem(m);
			m = n;
		} while (m != hp->h_q);
		hp->h_q = 0;
		hp->h_qcnt = 0;
	}
}

/*
 * Release mbufs in host table that contain no entries
 * currently in use.  Must be called at splimp.
 */
hostcompress(unit)
	int unit;
{
	register struct mbuf *m, **mprev;
	struct imp_softc *sc = &imp_softc[unit];

	mprev = &sc->imp_hosts;
	sc->imp_hostq = 0;
	while (m = *mprev) {
		if (mtod(m, struct hmbuf *)->hm_count == 0)
			*mprev = m_free(m);
		else
			mprev = &m->m_next;
	}
}

/*
 * Host data base timer routine.
 * Decrement timers on structures which are
 * waiting to be deallocated.  On expiration
 * release resources, possibly deallocating
 * mbuf associated with structure.
 */
hostslowtimo()
{
	register struct mbuf *m;
	register struct host *hp, *lp;
	struct imp_softc *sc;
	struct hmbuf *hm;
	int s = splimp(), unit, any;

	for (unit = 0; unit < NIMP; unit++) {
	    any = 0;
	    sc = &imp_softc[unit];
	    for (m = sc->imp_hosts; m; m = m->m_next) {
		hm = mtod(m, struct hmbuf *);
		hp = hm->hm_hosts; 
		lp = hp + HPMBUF;
		for (; hm->hm_count > 0 && hp < lp; hp++) {
		    if (hp->h_timer && --hp->h_timer == 0) {
			if (hp->h_rfnm) {
				log(LOG_INFO,			/* XXX */
				    "imp%d: host %d/imp %d, lost rfnm\n",
				    unit, hp->h_host, ntohs(hp->h_imp));
				sc->imp_lostrfnm++;
				imprestarthost(sc, hp);
			} else {
				any = 1;
				hostrelease(hp);
				if (sc->imp_hostq == m)
					sc->imp_hostq = 0;
			}
		    }
		}
	    }
	    if (any)
		hostcompress(unit);
	}
	splx(s);
}
#endif
