/* This file is bootstrapped from the RX release cleared by Transarc legal
   in 1994, available from ftp.dementia.org/pub/rx 
 */

#include <afsconfig.h>
#include "../afs/param.h"

RCSID("$Header: /cvs/openafs/src/rx/HPUX/rx_knet.c,v 1.8 2001/08/08 00:03:59 shadow Exp $");

#include "../h/types.h"
#include "../h/param.h"
#include "../rx/rx_kcommon.h"
#include "../h/user.h"
#include "../h/tihdr.h"
#include <xti.h>

/* Define this here, used externally */
#ifdef RX_ENABLE_LOCKS
lock_t *rx_sleepLock;
#endif

/* rx_kern.c */
/*
 * XXX The following is needed since the include file that contains them (<net/cko.h>)
 * isn't part of a binary distribution!! XXXX
 */

/* CKO_PSEUDO : Account for pseudo_header in the checksum 		*/
#ifndef	CKO_SUM_MASK
#define	CKO_SUM_MASK	    0xffff	/* sum is in lower 16 bits 	*/
#endif

#ifndef	CKO_PSEUDO
#define CKO_PSEUDO(sum, m, hdr) {					\
	(sum) = 0xffff ^ in_3sum( ((struct ipovly *)(hdr))->ih_src.s_addr, \
		((struct ipovly *)(hdr))->ih_dst.s_addr, 		\
		((struct ipovly *)(hdr))->ih_len 			\
		+ (((struct ipovly *)(hdr))->ih_pr<<16) 		\
		+ ((m)->m_quad[MQ_CKO_IN] & CKO_SUM_MASK));		\
}
#endif

static struct protosw parent_proto;	/* udp proto switch */

/* rx_kern.c */
static int rxk_fasttimo (void)
{
    int code;
    int (*tproc)();
    struct clock temp;

    SPLVAR;
    NETPRI;
    /* do rx fasttimo processing here */
    rxevent_RaiseEvents(&temp);
    USERPRI; /* should this be after the call to tproc? */
    if (tproc = parent_proto.pr_fasttimo) code = (*tproc)();
    else code = 0;
    return code;
}

/* hybrid of IRIX/knet.c and rx_kern.c */
/* XXX should this be listener or !listener? */
#if !defined(RXK_LISTENER_ENV)
/* start intercepting basic calls */
int
rxk_init() {
    register struct protosw *tpro, *last;
    if (rxk_initDone) return 0;

    last = inetdomain.dom_protoswNPROTOSW;
    for (tpro = inetdomain.dom_protosw; tpro < last; tpro++) {
	if (tpro->pr_protocol == IPPROTO_UDP) {
	    /* force UDP checksumming on for AFS        */
	    int udpcksum;
	    udpcksum = 1;
	    memcpy(&parent_proto, tpro, sizeof(parent_proto));
	    tpro->pr_input = rxk_input;
	    tpro->pr_fasttimo = rxk_fasttimo;
	    rxk_initDone = 1;
	    return 0;
	}
    }
    osi_Panic("inet:no udp");
}

/* basic packet handling routine called at splnet from softnet loop */
static struct mbuf *rxk_input (register struct mbuf *am, struct ifnet *aif)
{
    int (*tproc)();
    register unsigned short *tsp;
    int hdr;
    struct udphdr *tu;
    register struct ip *ti;
    struct udpiphdr *tvu;
    register int i;
    char *phandle;
    afs_int32 code;
    struct sockaddr_in taddr;
    int tlen;
    short port;
    int data_len;
    unsigned int comp_sum;

    SPLVAR;
    NETPRI;
    /* make sure we have base ip and udp headers in first mbuf */
    if (am->m_off > MMAXOFF || am->m_len < 28) {
	am = m_pullup(am, 28);
	USERPRI;
	if (!am) return (struct mbuf *)0;
    }
    hdr = (mtod(am, struct ip *))->ip_hl;
    if (hdr > 5) {
	/* pull up more, the IP hdr is bigger than usual */
	if (am->m_len < (8 + (hdr<<2))) {
	    am = m_pullup(am, 8+(hdr<<2));
	    USERPRI;
	    if (!am) return (struct mbuf *)0;
	}
	ti = mtod(am, struct ip *); /* recompute, since m_pullup allocates new mbuf */
	tu = (struct udphdr *)(((char *)ti) + (hdr<<2)); /* skip ip hdr */
    }
    else {
	ti = mtod(am, struct ip *);
	tu = (struct udphdr *)(((char *)ti) + 20);      /* skip basic ip hdr */
    }
    /* now read the port out */
    port = tu->uh_dport;
    
    if (port) {
	for(tsp=ports, i=0; i<MAXRXPORTS;i++) {
	    if (*tsp++ == port) {
		/* checksum the packet */
		ip_stripoptions(ti,     (struct mbuf *) 0); /* get rid of anything we don't need */
		/* deliver packet to rx */
		taddr.sin_family = AF_INET;         /* compute source address */
		taddr.sin_port = tu->uh_sport;
		taddr.sin_addr.s_addr = ti->ip_src.s_addr;
		
		/* handle the checksum.  Note that this code damages the actual ip
		   header (replacing it with the virtual one, which is the same 
		   size), so we must ensure we get everything out we need, first */
		tvu = (struct udpiphdr *) ti;   /* virtual udp structure, for ck
						   sum */
		if ( tu->uh_sum != 0) {
		    /* if the checksum is there, always check it. It's crazy not
		     * to, unless you can really be sure that your
		     * underlying network (and interfaces and drivers and
		     * DMA hardware, etc!) is error-free. First, fill
		     * in entire virtual ip header. */
		    tvu->ui_next = 0;
		    tvu->ui_prev = 0;
		    tvu->ui_x1 = 0;
		    tvu->ui_len = tvu->ui_ulen;
		    tlen = ntohs((unsigned short)(tvu->ui_ulen));
		    /* HP: checksum assist for cksum offloading drivers */
		    if (am->m_flags & MF_CKO_IN) {
			CKO_PSEUDO(comp_sum, am, tvu);
		    } else {
			struct mbuf *m1;
			
			comp_sum = in_cksum(am, sizeof(struct ip) + tlen);
			for (m1 = am; m1; m1 = m1->m_next)
			    m1->m_flags &= ~MF_NOACC;
		    }
		    if (comp_sum) {
			/* checksum, including cksum field, doesn't come out 0, so
			   this packet is bad */
			m_freem(am);
			USERPRI;
			return((struct mbuf *)0);
		    }
		}
		/*
		 * 28 is IP (20) + UDP (8) header.  ulen includes
		 * udp header, and we *don't* tell RX about udp
		 * header either.  So, we remove those 8 as well.
		 */
		data_len = ntohs(tu->uh_ulen);
		data_len -= 8;
                  
		if (!(*rxk_GetPacketProc)(&phandle, data_len)) {
		    if (rx_mb_to_packet(am, m_freem, 28, data_len, phandle)) {
			/* XXX should just increment counter here.. */
			printf("rx: truncated UDP packet\n");
			rxi_FreePacket(phandle);
		    }
		    else 
			(*rxk_PacketArrivalProc)(phandle, &taddr,
						 portRocks[i], data_len);
		}
		else m_freem(am);
		USERPRI;
		return((struct mbuf *)0);
	    }
	}
    }

    /* if we get here, try to deliver packet to udp */
    if (tproc = parent_proto.pr_input) {
      code = (*tproc)(am,aif);
      USERPRI;
      return code;
    }
    USERPRI;
    return((struct mbuf *)0);
}
#endif /* ! RXK_LISTENER_ENV */

/* steal decl from sgi_65 */
int
osi_NetSend(asocket, addr, dvec, nvec, asize, istack)
     register struct socket *asocket;
     struct iovec *dvec;
     int nvec;
     register afs_int32 asize;
     struct sockaddr_in *addr;
     int istack;
{
    struct uio uio;
    MBLKP bp;
    struct iovec temp[RX_MAXWVECS];
    int code;
    int size = sizeof(struct sockaddr_in);

    /* Guess based on rxk_NewSocket */
    bp = allocb((size+SO_MSGOFFSET+1), BPRI_MED);
    if (!bp) return ENOBUFS;        
    memcpy((caddr_t)bp->b_rptr+SO_MSGOFFSET, (caddr_t)addr, size);
    bp->b_wptr = bp->b_rptr + (size+SO_MSGOFFSET+1);

    memcpy((caddr_t)temp, (caddr_t)dvec, nvec * sizeof(struct iovec));
    
    /* devresource.hp.com/Drivers/Docs/Refs/11i/ddRef/Chap02R.pdf has details
       on use of uio */
    memset((caddr_t)&uio, 0, sizeof(uio));
    uio.uio_resid   = asize;
    uio.uio_iov     = temp;         
    uio.uio_iovcnt  = nvec;
    uio.uio_seg     = UIOSEG_KERNEL;

    code = sosend(asocket, bp, &uio, 0, 0, 0, size);
    return code;
}

/* pattern from IRIX */
#if defined(RXK_LISTENER_ENV)
int osi_NetReceive(struct socket *so, struct sockaddr_in *from, 
                   struct iovec *iov, int iovcnt, int *lengthp)
{
    int          code;
    struct uio   tuio;
    struct iovec tmpvec[RX_MAXWVECS+2];
    int          flags = 0;
    MBLKP bp, sp;

    if (iovcnt > RX_MAXWVECS+2) {
        osi_Panic("Too many (%d) iovecs passed to osi_NetReceive\n", iovcnt);
    }
    memcpy(tmpvec, (char*)iov, iovcnt/*(RX_MAXWVECS+1)*/ * sizeof(struct iovec));
    tuio.uio_iov     = tmpvec;
    tuio.uio_iovcnt  = iovcnt;
    tuio.uio_fpflags = 0;
    tuio.uio_offset  = 0;
    tuio.uio_seg     = UIOSEG_KERNEL;
    tuio.uio_resid   = *lengthp;

    code = soreceive(so, &bp, &tuio, &flags, &sp, (MBLKPP)NULL);
    if (!code) {
        *lengthp = *lengthp - tuio.uio_resid;
        if (bp) {
            memcpy((char*)from, (char*)bp->b_rptr, sizeof(struct sockaddr_in));
        } else {
            code = -1;
        }
    } else {
        so->so_error = 0;
    }

    if (bp) freeb(bp); 
    if (sp) freeb(sp);
    return code;
}
#endif
