/* Copyright 1984 by the Massachusetts Institute of Technology */

/*
 *------------------------------------------------------------------
 *
 * $Source: /u1/jis/gw/cgw/src/gw/in/RCS/infrg.c,v $
 * $Revision: 1.4 $
 * $Date: 88/10/31 01:33:08 $
 * $State: Exp $
 * $Author: jon $
 * $Locker: jon $
 *
 * $Log:	infrg.c,v $
 * Revision 1.4  88/10/31  01:33:08  jon
 * make the last packet have to correct bytes requested value (breq)
 * 
 * Revision 1.3  88/10/30  20:53:14  jon
 * add rcs header info
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_infrg_c = "$Header: infrg.c,v 1.4 88/10/31 01:33:08 jon Locked $";
#endif	lint

#include	<types.h>
#include	<sys.h>
#include	"../src/const.h"
#include	"../src/param.h"
#include	"../src/defs.h"
#include	"../src/macs.h"
#include	"../src/net.h"
#include	"../src/ext.h"
#include	"in.h"

#define INHDLEN(pkt)	(((pkt)->i_ihlver & INHLENM) * INBPHW)

static char	innfrg[] = "IN can't fragment from %d.%d.%d.%d\n";
ext char *indsc;

log_frag_pkt (pkt)
     inpkt *pkt;
{
  niflog (L_INFU)
    dolog ("IN outgoing fragment : src %d.%d.%d.%d, dst %d.%d.%d.%d\n",
	   mksina (&pkt->i_src), mksina (&pkt->i_dst));
  niflog (L_INFU)
    dolog ("\tlen %d, prot %d, frgoff %d, MF %d, chksum %d\n", 
	   ip2s (pkt->i_len), pkt->i_prot, 
	   (ip2s (pkt->i_fofflg) & INFOFFM) << 3,
	   ((ip2s(pkt->i_fofflg) & INMFRG) != 0), ip2s (pkt->i_cksum));
}

#ifdef TESTGW
int test_mtu = 1006;
#endif TESTGW

int in_fragment_pkt(iob, netp, hst)
reg	iorb	*iob;
xreg	net	*netp;
reg inaddr *hst;
{
	xreg	inpkt	*pkt;
	reg	inpkt	*fpkt;
	reg	iorb	*fiob;
		byte	*ptr;
		int	hdlen;
		unss	morefrags;
		int	frgsiz;
		int	nfrags;
	        int     n, d;
	        word    code;

	pkt = mkin(iob->i_addr);
	if (swab (pkt->i_fofflg) & INDNTF) {
		niflog(L_ERRU)
			dolog(innfrg, mksina(&pkt->i_src));
		freebuf(iob);
		return(-1);
	}

	log_frag_pkt (pkt);

	/* For the moment I am going to administratively prohibit
	   fragmenting fragments, as an effort to keep sanity in the world.
	   I have visions of being called upon to fragment fragments of 8k
	   NFS requests via the Arpanet and that just plain isn't going to 
	   work.  The spec doesn't allow this, but too bad. */
	if (pkt->i_fofflg) { /* if offset *or* MF is set this is a fragment */
	/* if (swab (pkt->i_fofflg) & INMFRG)) */
	  morefrags = INMFRG;  
	  niflog(L_ERRU) 
	    dolog (
		   "IN won't fragment a fragment from %d.%d.%d.%d to %d.%d.%d.%d\n",
		   mksina(&pkt->i_src), mksina (&pkt->i_dst));
	  freebuf(iob);
	  return (-1);
	}
	else morefrags = 0;
	hdlen = INHDLEN(pkt);

	/* break this up ... the compiler loses otherwise (UGH!) */
	n = (swab(pkt->i_len) - hdlen);
#ifdef TESTGW
	d = (test_mtu - hdlen )& ~(INFRGQ - 1);
#else
	d = (netp->n_max - hdlen )& ~(INFRGQ - 1);
#endif
	nfrags = 1 + (n / d);
	frgsiz = (((swab(pkt->i_len) - hdlen) / nfrags) + (INFRGQ - 1))
		 & ~(INFRGQ - 1);

#ifdef TESTGW
	niflog (L_TRCO)
	  dolog ("IN (frag) nfrags %d, frgsiz %d\n", nfrags, frgsiz);
#endif

	ptr = iob->i_addr + hdlen + frgsiz;

	if (nfrags > 2) {
	  niflog (L_ERRU)
	    dolog ("IN More than 2 frags required.  If MTUs are right fix infrg.c");
	  return (-1);
	}
#ifdef notyet
	while (ptr + frgsiz < iob->i_addr + iob->i_breq) {
	  /* This loses.  But at the moment we don't have an interface with
	     an MTU that should require more than two fragments, so I'm
	     going to punt.  Sigh. */
	  fiob = mkiorb(getbuf(bufsiz));
	  fiob->i_addr = (byte *)fiob + pktoff;
	  fpkt = mkin(fiob->i_addr);
	  copy(pkt, fpkt, hdlen);
	  fiob->i_breq = hdlen;
	  copy(ptr, fpkt + hdlen, frgsiz);
	  fiob->i_breq += frgsiz;
	  fpkt->i_fofflg = swab((ptr - iob->i_addr) / INFRGQ);
	  fpkt->i_fofflg |= swab(INMFRG);
	  fpkt->i_len = swab(hdlen + frgsiz);
	  fpkt->i_cksum = 0;
	  fpkt->i_cksum = ~cksum(fpkt, INMINH * INWPHW, 0);
	  dolog ("IN (frag loop) ptr + frgsize %x, addr + breq %x\n",
		 ptr + frgsiz, iob->i_addr + iob->i_breq);
	  log_frag_pkt (fpkt);
	  if ((code = (*netp->n_send)(fiob, netp, P_IN, hst)) != D_OK) {
	    niflog(L_TRCO)
	      dolog(indsc, pkt->i_prot, mksina(&pkt->i_src),
		    mksina(&pkt->i_dst), code);
	    freebuf(fiob);
	    return(-1);
	    pkt += frgsiz;
	}
#endif /* notyet */

	fiob = mkiorb(getbuf(bufsiz));
	fiob->i_addr = (byte *)fiob + pktoff;
	fpkt = mkin(fiob->i_addr);
	copy(pkt, fpkt, hdlen);
	fiob->i_breq = hdlen;
	copy(ptr, fpkt + hdlen, iob->i_addr + iob->i_breq - ptr);
	fiob->i_breq += iob->i_addr + iob->i_breq - ptr;
	fpkt->i_fofflg = swab(((ptr - iob->i_addr) - hdlen) / INFRGQ);
	fpkt->i_fofflg |= swab(morefrags);
	fpkt->i_len = swab(hdlen + iob->i_addr + iob->i_breq - ptr);
	fpkt->i_cksum = 0;
	fpkt->i_cksum = ~cksum(fpkt, INMINH * INWPHW, 0);
	log_frag_pkt (fpkt);
#ifdef TESTGW
	dolog ("IN frag breq = %d\n", fiob->i_breq);
#endif TESTGW
	if ((code = (*netp->n_send)(fiob, netp, P_IN, hst)) != D_OK) {
	  niflog(L_TRCO)
	    dolog(indsc, pkt->i_prot, mksina(&pkt->i_src),
		  mksina(&pkt->i_dst), code);
	  freebuf(fiob);
	  return(-1);
	}

	pkt->i_fofflg = swab(INMFRG);
	pkt->i_len = swab(hdlen + frgsiz);
	pkt->i_cksum = 0;
	pkt->i_cksum = ~cksum(pkt, INMINH * INWPHW, 0);
	iob->i_breq = hdlen + frgsiz;  /* first packet is full sized */
	log_frag_pkt (pkt);
#ifdef TESTGW
	dolog ("IN frag breq = %d\n", iob->i_breq);
#endif TESTGW
	/* Return this up and let infwd send it.  infwd will also send a
	   redirect if needed.  (This packet can't be for us since we are
	   only called from inhop if the destination MTU is too small.) */
	return(0);
}
