www.pudn.com > vxworks_tcpip_code.rar > ip_input.c


/* ip_input.c - internet input routines */

/* Copyright 1984 - 1999 Wind River Systems, Inc. */
#include "copyright_wrs.h"

/*
 * Copyright (c) 1982, 1986, 1988, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)ip_input.c	8.2 (Berkeley) 1/4/94
 */

/*
modification history
--------------------
01r,08nov99,pul  merging T2 cumulative patch 2
01q,04jun99,spm  corrected ip_forward() routine to handle source routing
                 options for hosts (SPR #27727)
01p,28feb99,ham  corrected comment below - (wrong)34040 -> (right)32767.
01o,28feb99,ham  fixed reassembly problem larger ip_off 34040, spr#23250.
01n,09jan98,vin  changed IP_CKSUM_RECV_MASK to IP_DO_CHECKSUM_RCV
01m,13nov97,vin  added HTONS(ip->ip_len) in ip_forward before icmp_error,
		 consolidated all flags into ipCfgFlags.
01l,28oct97,vin  reverted on the fix in version01j, what was vin thinking.
01k,15jul97,spm  made IP checksum calculation configurable (SPR #7836)
01j,05jun97,vin  fixed problem in ip_forward().HTONS(ip_len).
01i,17apr97,vin  added more functionality to the ipFilterHook.
01h,08mar97,vin  added mCastRouteFwdHook to access mcast routing code.
01g,11apr97,rjc  added ipFilter Hook and strongEnded flag processing.
01f,04mar97,vin  added fix to null domain pointer in ip_init SPR 8078.
01e,05feb97,rjc  tos routing changes
01d,31jan97,vin  changed declaration according to prototype decl in protosw.h
01c,07jan96,vin  moved iptime() from ip_icmp, added _icmpErrorHook instead
		 of icmp_error() for scalability, added ipfragttl. 
01b,30oct96,vin  re-worked ipreassembly, made it free from dtom macros.
		 added ipReAssembly, replace m_get(..) with mBufClGet(..)
01a,03mar96,vin  created from BSD4.4 stuff,integrated with 02r of ip_input.c
*/

#include "vxWorks.h"
#include "tickLib.h"
#include "netLib.h"
#include "net/systm.h"
#include "net/mbuf.h"
#include "net/domain.h"
#include "net/protosw.h"
#include "sys/socket.h"
#include "errno.h"

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

#include "netinet/in.h"
#include "netinet/in_pcb.h"
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/ip.h"
#include "netinet/ip_var.h"
#include "netinet/ip_icmp.h"

extern _insque();
extern _remque();
extern sysClkRateGet();
extern VOIDFUNCPTR _icmpErrorHook;
extern FUNCPTR _ipFilterHook;           /* ipFilter hook defined in netLib.c */extern FUNCPTR _mCastRouteFwdHook;	/* WRS mcast forward command hook */

/* globals */

#ifndef	IPFORWARDING
#ifdef GATEWAY
#define	IPFORWARDING	1	/* forward IP packets not for us */
#else /* GATEWAY */
#define	IPFORWARDING	0	/* don't forward IP packets not for us */
#endif /* GATEWAY */
#endif /* IPFORWARDING */
#ifndef	IPSENDREDIRECTS
#define	IPSENDREDIRECTS	1
#endif

#define FORWARD_BROADCASTS

#ifdef FORWARD_BROADCASTS
#define DIRECTED_BROADCAST
FUNCPTR	proxyBroadcastHook = NULL;
#endif

int	_ipCfgFlags;			/* ip configurations, bit field flag */
int	ip_defttl = IPDEFTTL;		/* default IP ttl */
int	ipfragttl = IPFRAGTTL;		/* default IP fragment timeout */
#ifdef DIAGNOSTIC
int	ipprintfs = 0;
#endif
int     ipStrongEnded = FALSE;             /* default ip ended'ness */

extern	struct domain inetdomain;
extern	struct protosw inetsw[];
u_char	ip_protox[IPPROTO_MAX];
int	ipqmaxlen = IFQ_MAXLEN;
struct	in_ifaddr *in_ifaddr;		/* first inet address */
struct	ifqueue ipintrq;
struct	ipstat	ipstat;
struct	ipq	ipq;			/* ip reass. queue */
u_short	ip_id;				/* ip packet ctr, for ids */

/*
 * We need to save the IP options in case a protocol wants to respond
 * to an incoming packet over the same route if the packet got here
 * using IP source routing.  This allows connection establishment and
 * maintenance when the remote end is on a network that is not known
 * to us.
 */
int	ip_nhops = 0;
static	struct ip_srcrt {
	struct	in_addr dst;			/* final destination */
	char	nop;				/* one NOP to align */
	char	srcopt[IPOPT_OFFSET + 1];	/* OPTVAL, OLEN and OFFSET */
	struct	in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
} ip_srcrt;

/* forward declarations */

static void save_rte (u_char *, struct in_addr);
LOCAL struct mbuf * ipReAssemble (FAST struct mbuf * pMbuf, 
				  FAST struct ipq *  pIpFragQueue); 

/*
 * IP initialization: fill in IP protocol switch table.
 * All protocols not implemented in kernel go to raw IP protocol handler.
 */
void
ip_init()
{
	register struct protosw *pr;
	register int i;

	pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
	if (pr == 0)
		panic("ip_init");
	for (i = 0; i < IPPROTO_MAX; i++)
		ip_protox[i] = pr - inetsw;
	for (pr = inetdomain.dom_protosw;
	    pr < inetdomain.dom_protoswNPROTOSW; pr++)
                if ((pr->pr_domain != NULL) &&
                    pr->pr_domain->dom_family == PF_INET &&
                    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
                    ip_protox[pr->pr_protocol] = pr - inetsw;
	ipq.next = ipq.prev = &ipq;
	ip_id = iptime() & 0xffff;
	ipintrq.ifq_maxlen = ipqmaxlen;
	return;
}

struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
struct	route ipforward_rt;

/*
 * Ip input routine.  Checksum and byte swap header.  If fragmented
 * try to reassemble.  Process options.  Pass to next level.
 */
void
ipintr()
{
	register struct ip *ip;
	register struct mbuf *m;
	register struct ipq *fp;
	register struct in_ifaddr *ia;
	int hlen, s;

next:
	/*
	 * Get next datagram off input queue and get IP header
	 * in first mbuf.
	 */
	s = splimp();
	IF_DEQUEUE(&ipintrq, m);
	splx(s);
	if (m == 0)
		return;
#ifdef	DIAGNOSTIC
	if ((m->m_flags & M_PKTHDR) == 0)
		panic("ipintr no HDR");
#endif
	/*
	 * If no IP addresses have been set yet but the interfaces
	 * are receiving, can't do anything with incoming packets yet.
	 */
	if (in_ifaddr == NULL)
		goto bad;
	ipstat.ips_total++;
	if (m->m_len < sizeof (struct ip) &&
	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
		ipstat.ips_toosmall++;
		goto next;
	}
	ip = mtod(m, struct ip *);
	if (ip->ip_v != IPVERSION) {
		ipstat.ips_badvers++;
		goto bad;
	}
	hlen = ip->ip_hl << 2;
	if (hlen < sizeof(struct ip)) {	/* minimum header length */
		ipstat.ips_badhlen++;
		goto bad;
	}
	if (hlen > m->m_len) {
		if ((m = m_pullup(m, hlen)) == 0) {
			ipstat.ips_badhlen++;
			goto next;
		}
		ip = mtod(m, struct ip *);
	}

        if (_ipCfgFlags & IP_DO_CHECKSUM_RCV)
            {
	    if ( (ip->ip_sum = in_cksum(m, hlen)) != 0) 
                {
		ipstat.ips_badsum++;
		goto bad;
	        }
            }

	/*
	 * Convert fields to host representation.
	 */
	NTOHS(ip->ip_len);
	if (ip->ip_len < hlen) {
		ipstat.ips_badlen++;
		goto bad;
	}
	NTOHS(ip->ip_id);
	NTOHS(ip->ip_off);

	/*
	 * Check that the amount of data in the buffers
	 * is as at least much as the IP header would have us expect.
	 * Trim mbufs if longer than we expect.
	 * Drop packet if shorter than we expect.
	 */
	if (m->m_pkthdr.len < ip->ip_len) {
		ipstat.ips_tooshort++;
		goto bad;
	}
	if (m->m_pkthdr.len > ip->ip_len) {
		if (m->m_len == m->m_pkthdr.len) {
			m->m_len = ip->ip_len;
			m->m_pkthdr.len = ip->ip_len;
		} else
			m_adj(m, ip->ip_len - m->m_pkthdr.len);
	}
	
	/* ipFilterHook example fireWall Hook processing */

	if (_ipFilterHook != NULL)
	    {
	    /* process the packet if the hook returns other than TRUE */

            if ((*_ipFilterHook) (m->m_pkthdr.rcvif, &m, &ip, hlen) == TRUE)
                goto next;	/* the hook should free the mbuf */
            }

	/*
	 * Process options and, if not destined for us,
	 * ship it on.  ip_dooptions returns 1 when an
	 * error was detected (causing an icmp message
	 * to be sent and the original packet to be freed).
	 */
	ip_nhops = 0;		/* for source routed packets */
	if (hlen > sizeof (struct ip) && ip_dooptions(m))
		goto next;

	/*
	 * Check our list of addresses, to see if the packet is for us.
	 */
	for (ia = in_ifaddr; ia; ia = ia->ia_next) {
#define	satosin(sa)	((struct sockaddr_in *)(sa))
		/* if ip is strong ended we will accept packets on  an interface
		 * only if destination in ip hdr matches ip  address on the
		 * receiving interface and not just any interface. 
		 */
                if ((ipStrongEnded == TRUE) && 
		    (ia->ia_ifp != m->m_pkthdr.rcvif))
		    continue;

		if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
			goto ours;
		if (
#ifdef	DIRECTED_BROADCAST
		    ia->ia_ifp == m->m_pkthdr.rcvif &&
#endif
		    (ia->ia_ifp->if_flags & IFF_BROADCAST)) {
			u_long t;

			if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
			    ip->ip_dst.s_addr)
				goto ours;
			if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr)
				goto ours;
			/*
			 * Look for all-0's host part (old broadcast addr),
			 * either for subnet or net.
			 */
			t = ntohl(ip->ip_dst.s_addr);
			if (t == ia->ia_subnet)
				goto ours;
			if (t == ia->ia_net)
				goto ours;
		}
	}
	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
		struct in_multi *inm;
		if (_mCastRouteFwdHook != NULL) {
			/*
			 * If we are acting as a multicast router, all
			 * incoming multicast packets are passed to the
			 * kernel-level multicast forwarding function.
			 * The packet is returned (relatively) intact; if
			 * ip_mforward() returns a non-zero value, the packet
			 * must be discarded, else it may be accepted below.
			 *
			 * (The IP ident field is put in the same byte order
			 * as expected when ip_mforward() is called from
			 * ip_output().)
			 */
			ip->ip_id = htons(ip->ip_id);
                        if ((*_mCastRouteFwdHook)(m, m->m_pkthdr.rcvif) != 0) {
				ipstat.ips_cantforward++;
				m_freem(m);
				goto next;
			}
			ip->ip_id = ntohs(ip->ip_id);

			/*
			 * The process-level routing demon needs to receive
			 * all multicast IGMP packets, whether or not this
			 * host belongs to their destination groups.
			 */
			if (ip->ip_p == IPPROTO_IGMP)
				goto ours;
			ipstat.ips_forward++;
		}
		/*
		 * See if we belong to the destination multicast group on the
		 * arrival interface.
		 */
		IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
		if (inm == NULL) {
			ipstat.ips_cantforward++;
			m_freem(m);
			goto next;
		}
		goto ours;
	}
	if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
		goto ours;
	if (ip->ip_dst.s_addr == INADDR_ANY)
		goto ours;

	/*
	 * Not for us; forward if possible and desirable.
	 */
	if ((_ipCfgFlags & IP_DO_FORWARDING) == 0) {
		ipstat.ips_cantforward++;
		m_freem(m);
	} else
		ip_forward(m, 0);
	goto next;

ours:
	/*
	 * If offset or IP_MF are set, must reassemble.
	 * Otherwise, nothing need be done.
	 * (We could look in the reassembly queue to see
	 * if the packet was previously fragmented,
	 * but it's not worth the time; just let them time out.)
	 */
	if (ip->ip_off &~ IP_DF) {
		/*
		 * Look for queue of fragments
		 * of this datagram.
		 */
		for (fp = ipq.next; fp != &ipq; fp = fp->next)
			if (ip->ip_id == fp->ipq_id &&
			    ip->ip_src.s_addr == fp->ipq_src.s_addr &&
			    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
			    ip->ip_p == fp->ipq_p)
				goto found;
		fp = 0;
found:

		/*
		 * Adjust ip_len to not reflect header,
		 * set ip_mff if more fragments are expected,
		 * convert offset of this to bytes.
		 */
		ip->ip_len -= hlen;
		((struct ipasfrag *)ip)->ipf_mff &= ~1;
		if (ip->ip_off & IP_MF)
			((struct ipasfrag *)ip)->ipf_mff |= 1;
		ip->ip_off <<= 3;

		/*
		 * If datagram marked as having more fragments
		 * or if this is not the first fragment,
		 * attempt reassembly; if it succeeds, proceed.
		 */
		if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) {
			ipstat.ips_fragments++;
			if ((m = ipReAssemble (m, fp)) == NULL)
				goto next;
			ipstat.ips_reassembled++;
		} else
			if (fp)
				ip_freef(fp);
	} else
		ip->ip_len -= hlen;

#ifdef  FORWARD_BROADCASTS
        if ((m->m_flags & M_BCAST) && (proxyBroadcastHook != NULL))
            (* proxyBroadcastHook) (m, m->m_pkthdr.rcvif);
#endif
	/*
	 * Switch out to protocol's input routine.
	 */
	ipstat.ips_delivered++;
	(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
	goto next;
bad:
	m_freem(m);
	goto next;
}

/*******************************************************************************
*
* ipReAssemble - reassemble ip fragments
*
* This function reassembles the ip fragments and returns the pointer to the
* mbuf chain if reassembly is complete orelse returns null.
*
* RETURNS: pMbuf/NULL
*
* SEE ALSO: ,
*
* NOMANUAL
*/

LOCAL struct mbuf * ipReAssemble
    (
    FAST struct mbuf *	pMbuf,		/* pointer to mbuf chain fragment */
    FAST struct ipq *	pIpFragQueue	/* pointer to ip fragment queue */
    )
    {
    FAST struct mbuf ** 	pPtrMbuf; 	   /* pointer to ptr to mbuf */
    FAST struct mbuf * 		pMbPktFrag = NULL; /* pointer to the packet */
    FAST struct ip * 		pIpHdr;   	   /* pointer to ip header */
    FAST struct ipasfrag *	pIpHdrFrag;	   /* ipfragment header */
    FAST int		        len; 
    FAST struct mbuf *		pMbufTmp;	   /* pointer to mbuf */
    
    pIpHdr = mtod (pMbuf, struct ip *); 
    pMbuf->m_nextpkt = NULL; 
    /*
     * If first fragment to arrive, create a reassembly queue.
     */
    if (pIpFragQueue == 0)
	{
	if ((pMbufTmp = mBufClGet (M_DONTWAIT, MT_FTABLE, sizeof(struct ipq),
				   TRUE)) == NULL)
	    goto dropFrag;
	pIpFragQueue = mtod(pMbufTmp, struct ipq *);
	insque(pIpFragQueue, &ipq);
	pIpFragQueue->ipq_ttl = ipfragttl;	/* configuration parameter */
	pIpFragQueue->ipq_p = pIpHdr->ip_p;
	pIpFragQueue->ipq_id = pIpHdr->ip_id;
	pIpFragQueue->ipq_src = ((struct ip *)pIpHdr)->ip_src;
	pIpFragQueue->ipq_dst = ((struct ip *)pIpHdr)->ip_dst;
	pIpFragQueue->pMbufHdr   = pMbufTmp; 	/* back pointer to mbuf */
	pIpFragQueue->pMbufPkt = pMbuf; 	/* first fragment received */
	goto ipChkReAssembly; 
	}

    for (pPtrMbuf = &(pIpFragQueue->pMbufPkt); *pPtrMbuf != NULL;
	 pPtrMbuf = &(*pPtrMbuf)->m_nextpkt)
	{
	pMbPktFrag = *pPtrMbuf; 
	pIpHdrFrag = mtod(pMbPktFrag, struct ipasfrag *); 

	if ((USHORT)pIpHdr->ip_off > (USHORT)pIpHdrFrag->ip_off)
	    {
	    if ((len = (signed int)((USHORT)pIpHdrFrag->ip_off +
				    pIpHdrFrag->ip_len -
				    (USHORT)pIpHdr->ip_off)) > 0)
		{
		if (len >= pIpHdr->ip_len)
		    goto dropFrag;		/* drop the fragment */
		pIpHdrFrag->ip_len -= len; 
		m_adj(pMbPktFrag, -len); 	/* trim from the tail */
		}
	    if (pMbPktFrag->m_nextpkt == NULL)
		{ 	/* this is the likely case most of the time */
		pMbPktFrag->m_nextpkt = pMbuf; 	/* insert the fragment */
		pMbuf->m_nextpkt = NULL;
		break;
		}
	    }
	else 		/* if pIpHdr->ip_off <= pIpHdrFrag->ip_off */
	    {
            /* if complete overlap */
            while (((USHORT)pIpHdr->ip_off + pIpHdr->ip_len) >=
                   ((USHORT)pIpHdrFrag->ip_off + pIpHdrFrag->ip_len))
                {
                *pPtrMbuf = (*pPtrMbuf)->m_nextpkt;
                pMbPktFrag->m_nextpkt = NULL;
                m_freem (pMbPktFrag);           /* drop the fragment */
                pMbPktFrag = *pPtrMbuf;
                if (pMbPktFrag == NULL)
                    break;
                pIpHdrFrag = mtod(pMbPktFrag, struct ipasfrag *);
                }
            /* if partial overlap trim my data at the end*/
            if ((pMbPktFrag != NULL) &&
                ((len = (((USHORT)pIpHdr->ip_off + pIpHdr->ip_len) -
                             pIpHdrFrag->ip_off)) > 0))
                {
                pIpHdr->ip_len -= len;
                m_adj (pMbuf, -len);        	/* trim from the tail */
                }
	    pMbuf->m_nextpkt = pMbPktFrag; 
	    *pPtrMbuf = pMbuf; 	/* insert the current fragment */	    
	    break; 
	    }
	}

    ipChkReAssembly:
    len = 0; 
    for (pMbPktFrag = pIpFragQueue->pMbufPkt; pMbPktFrag != NULL;
	 pMbPktFrag = pMbPktFrag->m_nextpkt)
	{
	pIpHdrFrag = mtod(pMbPktFrag, struct ipasfrag *); 
	if ((USHORT)pIpHdrFrag->ip_off != len)
	    return (NULL); 
	len += pIpHdrFrag->ip_len; 
	}    
    if (pIpHdrFrag->ipf_mff & 1)	/* last fragment's mff bit still set */
	return (NULL); 

    /* reassemble and concatenate all fragments */
    pMbuf = pIpFragQueue->pMbufPkt; 
    pMbufTmp = pMbuf->m_nextpkt; 
    pMbuf->m_nextpkt = NULL; 

    while (pMbufTmp != NULL)
	{
	pMbPktFrag = pMbufTmp; 
	pIpHdrFrag = mtod(pMbPktFrag, struct ipasfrag *); 
	pMbPktFrag->m_data += pIpHdrFrag->ip_hl << 2;	/* remove the header */
	pMbPktFrag->m_len  -= pIpHdrFrag->ip_hl << 2;
	pMbufTmp = pMbPktFrag->m_nextpkt; 
	pMbPktFrag->m_nextpkt = NULL ; 
	m_cat (pMbuf, pMbPktFrag); 	/* concatenate the fragment */
	}

    pIpHdrFrag = mtod(pMbuf, struct ipasfrag *); 
    pIpHdrFrag->ip_len = len; 		/* length of the ip packet */
    pIpHdrFrag->ipf_mff &= ~1;
    remque(pIpFragQueue);
    (void) m_free (pIpFragQueue->pMbufHdr); 

    /* some debugging cruft by sklower, below, will go away soon */
    if (pMbuf->m_flags & M_PKTHDR)
	{ /* XXX this should be done elsewhere */
	len = 0; 
	for (pMbufTmp = pMbuf; pMbufTmp; pMbufTmp = pMbufTmp->m_next)
	    len += pMbufTmp->m_len;
	pMbuf->m_pkthdr.len = len;
	}

    return (pMbuf);			/* return the assembled packet */

    dropFrag:
    ipstat.ips_fragdropped++;
    m_freem (pMbuf);			/* free the fragment */
    return (NULL);
    }

/*
 * Free a fragment reassembly header and all
 * associated datagrams.
 */
void
ip_freef(fp)
	struct ipq *fp;
{
	struct mbuf **  pPtrMbuf; 
	struct mbuf *  pMbuf;
	
	/* free all fragments */
	pPtrMbuf = &(fp->pMbufPkt); 
	while (*pPtrMbuf)
	    {
	    pMbuf = (*pPtrMbuf)->m_nextpkt; 
	    m_freem (*pPtrMbuf); 
	    *pPtrMbuf = pMbuf; 
	    }
	remque(fp);
	(void) m_free(fp->pMbufHdr);
}

n_time
iptime()
{
	u_long t;

	/* t is time in milliseconds (i hope) */

	t = ((int) tickGet () * 1000) / sysClkRateGet ();

	return (htonl(t));
}

/*
 * IP timer processing;
 * if a timer expires on a reassembly
 * queue, discard it.
 */
void
ip_slowtimo()
{
	register struct ipq *fp;
	int s = splnet();

	fp = ipq.next;
	if (fp == 0) {
		splx(s);
		return;
	}
	while (fp != &ipq) {
		--fp->ipq_ttl;
		fp = fp->next;
		if (fp->prev->ipq_ttl == 0) {
			ipstat.ips_fragtimeout++;
			ip_freef(fp->prev);
		}
	}
	splx(s);
	return;
}

/*
 * Drain off all datagram fragments.
 */
void
ip_drain()
{

	while (ipq.next != &ipq) {
		ipstat.ips_fragdropped++;
		ip_freef(ipq.next);
	}
	return;
}

/*
 * Do option processing on a datagram,
 * possibly discarding it if bad options are encountered,
 * or forwarding it if source-routed.
 * Returns 1 if packet has been forwarded/freed,
 * 0 if the packet should be processed further.
 */
int
ip_dooptions(m)
	struct mbuf *m;
{
	register struct ip *ip = mtod(m, struct ip *);
	register u_char *cp;
	register struct ip_timestamp *ipt;
	register struct in_ifaddr *ia;
	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
	struct in_addr *sin, dst;
	n_time ntime;

	dst = ip->ip_dst;
	cp = (u_char *)(ip + 1);
	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
	for (; cnt > 0; cnt -= optlen, cp += optlen) {
		opt = cp[IPOPT_OPTVAL];
		if (opt == IPOPT_EOL)
			break;
		if (opt == IPOPT_NOP)
			optlen = 1;
		else {
			optlen = cp[IPOPT_OLEN];
			if (optlen <= 0 || optlen > cnt) {
				code = &cp[IPOPT_OLEN] - (u_char *)ip;
				goto bad;
			}
		}
		switch (opt) {

		default:
			break;

		/*
		 * Source routing with record.
		 * Find interface with current destination address.
		 * If none on this machine then drop if strictly routed,
		 * or do nothing if loosely routed.
		 * Record interface address and bring up next address
		 * component.  If strictly routed make sure next
		 * address is on directly accessible net.
		 */
		case IPOPT_LSRR:
		case IPOPT_SSRR:
			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
				goto bad;
			}
			ipaddr.sin_addr = ip->ip_dst;
			TOS_SET (&ipaddr, ip->ip_tos);  /* tos routing */

			ia = (struct in_ifaddr *)
				ifa_ifwithaddr((struct sockaddr *)&ipaddr);
			if (ia == 0) {
				if (opt == IPOPT_SSRR) {
					type = ICMP_UNREACH;
					code = ICMP_UNREACH_SRCFAIL;
					goto bad;
				}
				/*
				 * Loose routing, and not at next destination
				 * yet; nothing to do except forward.
				 */
				break;
			}
			off--;			/* 0 origin */
			if (off > optlen - sizeof(struct in_addr)) {
				/*
				 * End of source route.  Should be for us.
				 */
				save_rte(cp, ip->ip_src);
				break;
			}
			/*
			 * locate outgoing interface
			 */
			bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
			    sizeof(ipaddr.sin_addr));
			if (opt == IPOPT_SSRR) {
#define	INA	struct in_ifaddr *
#define	SA	struct sockaddr *
			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
			} else
				ia = ip_rtaddr(ipaddr.sin_addr);
			if (ia == 0) {
				type = ICMP_UNREACH;
				code = ICMP_UNREACH_SRCFAIL;
				goto bad;
			}
			ip->ip_dst = ipaddr.sin_addr;
			bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
			    (caddr_t)(cp + off), sizeof(struct in_addr));
			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
			/*
			 * Let ip_intr's mcast routing check handle mcast pkts
			 */
			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
			break;

		case IPOPT_RR:
			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
				goto bad;
			}
			/*
			 * If no space remains, ignore.
			 */
			off--;			/* 0 origin */
			if (off > optlen - sizeof(struct in_addr))
				break;
			bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
			    sizeof(ipaddr.sin_addr));
			/*
			 * locate outgoing interface; if we're the destination,
			 * use the incoming interface (should be same).
			 */
			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
				type = ICMP_UNREACH;
				code = ICMP_UNREACH_HOST;
				goto bad;
			}
			bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
			    (caddr_t)(cp + off), sizeof(struct in_addr));
			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
			break;

		case IPOPT_TS:
			code = cp - (u_char *)ip;
			ipt = (struct ip_timestamp *)cp;
			if (ipt->ipt_len < 5)
				goto bad;
			if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) {
				if (++ipt->ipt_oflw == 0)
					goto bad;
				break;
			}
			sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
			switch (ipt->ipt_flg) {

			case IPOPT_TS_TSONLY:
				break;

			case IPOPT_TS_TSANDADDR:
				if (ipt->ipt_ptr + sizeof(n_time) +
				    sizeof(struct in_addr) > ipt->ipt_len)
					goto bad;
				ipaddr.sin_addr = dst;
				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
							    m->m_pkthdr.rcvif);
				if (ia == 0)
					continue;
				bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
				    (caddr_t)sin, sizeof(struct in_addr));
				ipt->ipt_ptr += sizeof(struct in_addr);
				break;

			case IPOPT_TS_PRESPEC:
				if (ipt->ipt_ptr + sizeof(n_time) +
				    sizeof(struct in_addr) > ipt->ipt_len)
					goto bad;
				bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
				    sizeof(struct in_addr));
				if (ifa_ifwithaddr((SA)&ipaddr) == 0)
					continue;
				ipt->ipt_ptr += sizeof(struct in_addr);
				break;

			default:
				goto bad;
			}
			ntime = iptime();
			bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
			    sizeof(n_time));
			ipt->ipt_ptr += sizeof(n_time);
		}
	}
	if (forward) {
		ip_forward(m, 1);
		return (1);
	}
	return (0);
bad:
	ip->ip_len -= ip->ip_hl << 2;   /* XXX icmp_error adds in hdr length */
	if (_icmpErrorHook != NULL)
	    (*_icmpErrorHook) (m, type, code, 0, 0); 
	ipstat.ips_badoptions++;
	return (1);
}

/*
 * Given address of next destination (final or next hop),
 * return internet address info of interface to be used to get there.
 */
struct in_ifaddr *
ip_rtaddr(dst)
	 struct in_addr dst;
{
	register struct sockaddr_in *sin;

	sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;

	if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
		if (ipforward_rt.ro_rt) {
			RTFREE(ipforward_rt.ro_rt);
			ipforward_rt.ro_rt = 0;
		}
		sin->sin_family = AF_INET;
		sin->sin_len = sizeof(*sin);
		sin->sin_addr = dst;

		rtalloc(&ipforward_rt);
	}
	if (ipforward_rt.ro_rt == 0)
		return ((struct in_ifaddr *)0);
	return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
}

/*
 * Save incoming source route for use in replies,
 * to be picked up later by ip_srcroute if the receiver is interested.
 */
void
save_rte(option, dst)
	u_char *option;
	struct in_addr dst;
{
	unsigned olen;

	olen = option[IPOPT_OLEN];
#ifdef DIAGNOSTIC
	if (ipprintfs)
		printf("save_rte: olen %d\n", olen);
#endif
	if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
		return;
	bcopy((caddr_t)option, (caddr_t)ip_srcrt.srcopt, olen);
	ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
	ip_srcrt.dst = dst;
}

/*
 * Retrieve incoming source route for use in replies,
 * in the same form used by setsockopt.
 * The first hop is placed before the options, will be removed later.
 */
struct mbuf *
ip_srcroute()
{
	register struct in_addr *p, *q;
	register struct mbuf *m;

	if (ip_nhops == 0)
		return ((struct mbuf *)0);
	m= mBufClGet(M_DONTWAIT, MT_SOOPTS, CL_SIZE_128, TRUE);
	if (m == 0)
		return ((struct mbuf *)0);

#define OPTSIZ	(sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))

	/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
	m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
	    OPTSIZ;
#ifdef DIAGNOSTIC
	if (ipprintfs)
		printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
#endif

	/*
	 * First save first hop for return route
	 */
	p = &ip_srcrt.route[ip_nhops - 1];
	*(mtod(m, struct in_addr *)) = *p--;
#ifdef DIAGNOSTIC
	if (ipprintfs)
		printf(" hops %lx", ntohl(mtod(m, struct in_addr *)->s_addr));
#endif

	/*
	 * Copy option fields and padding (nop) to mbuf.
	 */
	ip_srcrt.nop = IPOPT_NOP;
	ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
	bcopy((caddr_t)&ip_srcrt.nop,
	    mtod(m, caddr_t) + sizeof(struct in_addr), OPTSIZ);
	q = (struct in_addr *)(mtod(m, caddr_t) +
	    sizeof(struct in_addr) + OPTSIZ);
#undef OPTSIZ
	/*
	 * Record return path as an IP source route,
	 * reversing the path (pointers are now aligned).
	 */
	while (p >= ip_srcrt.route) {
#ifdef DIAGNOSTIC
		if (ipprintfs)
			printf(" %lx", ntohl(q->s_addr));
#endif
		*q++ = *p--;
	}
	/*
	 * Last hop goes to final destination.
	 */
	*q = ip_srcrt.dst;
#ifdef DIAGNOSTIC
	if (ipprintfs)
		printf(" %lx\n", ntohl(q->s_addr));
#endif
	return (m);
}

/*
 * Strip out IP options, at higher
 * level protocol in the kernel.
 * Second argument is buffer to which options
 * will be moved, and return value is their length.
 * XXX should be deleted; last arg currently ignored.
 */
void
ip_stripoptions(m, mopt)
	register struct mbuf *m;
	struct mbuf *mopt;
{
	register int i;
	struct ip *ip = mtod(m, struct ip *);
	register caddr_t opts;
	int olen;

	olen = (ip->ip_hl<<2) - sizeof (struct ip);
	opts = (caddr_t)(ip + 1);
	i = m->m_len - (sizeof (struct ip) + olen);
	bcopy(opts  + olen, opts, (unsigned)i);
	m->m_len -= olen;
	if (m->m_flags & M_PKTHDR)
		m->m_pkthdr.len -= olen;
	ip->ip_hl = sizeof(struct ip) >> 2;
}

u_char inetctlerrmap[PRC_NCMDS] = {
	0,		0,		0,		0,
	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
	EMSGSIZE,	EHOSTUNREACH,	0,		0,
	0,		0,		0,		0,
	ENOPROTOOPT
};

/* Added to allow the user to determine if this packet should be forwarded. */
int (*inCanForwardHook)( struct in_addr in );

/*
 * Forward a packet.  If some error occurs return the sender
 * an icmp packet.  Note we can't always generate a meaningful
 * icmp message because icmp doesn't have a large enough repertoire
 * of codes and types.
 *
 * If not forwarding, just drop the packet.  This could be confusing
 * if (_ipCfgFlags & IP_DO_FORWARDING) was zero but some routing protocol was
 * advancing us as a gateway to somewhere.  However, we must let the routing
 * protocol deal with that.
 *
 * The srcrt parameter indicates whether the packet is being forwarded
 * via a source route.
 */
void
ip_forward(m, srcrt)
	struct mbuf *m;
	int srcrt;
{
	register struct ip *ip = mtod(m, struct ip *);
	register struct sockaddr_in *sin;
	register struct rtentry *rt;
	int error, type = 0, code = 0;
	struct mbuf *mcopy;
	n_long dest;
	struct ifnet *destifp;

	dest = 0;
#ifdef DIAGNOSTIC
	if (ipprintfs)
		printf("forward: src %x dst %x ttl %x\n", ip->ip_src,
			ip->ip_dst, ip->ip_ttl);
#endif
	if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) {
		ipstat.ips_cantforward++;
		m_freem(m);
		return;
	}

    /* Added to allow the user access to filter packets. */
    if ( inCanForwardHook && (inCanForwardHook( ip->ip_dst ) == 0) ) {
        ipstat.ips_cantforward++;
        m_freem(m);
        return;
    }

	HTONS(ip->ip_id);
	if (ip->ip_ttl <= IPTTLDEC) {
		if (_icmpErrorHook != NULL)
                    {
                    HTONS(ip->ip_len);
                    (*_icmpErrorHook)(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS,
                                      dest, 0);
                    }
		return;
	}
	ip->ip_ttl -= IPTTLDEC;

	sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
	if ((rt = ipforward_rt.ro_rt) == 0 ||
	    ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
		if (ipforward_rt.ro_rt) {
			RTFREE(ipforward_rt.ro_rt);
			ipforward_rt.ro_rt = 0;
		}
		sin->sin_family = AF_INET;
		sin->sin_len = sizeof(*sin);
		sin->sin_addr = ip->ip_dst;

		rtalloc(&ipforward_rt);
		if (ipforward_rt.ro_rt == 0) {
			if (_icmpErrorHook != NULL)
			    (*_icmpErrorHook)(m, ICMP_UNREACH, 
					     ICMP_UNREACH_HOST, dest, 0);
			return;
		}
		rt = ipforward_rt.ro_rt;
	}

	/*
	 * Save at most 64 bytes of the packet in case
	 * we need to generate an ICMP message to the src.
	 */
	mcopy = m_copy(m, 0, min((int)ip->ip_len, 64));

	/*
	 * If forwarding packet using same interface that it came in on,
	 * perhaps should send a redirect to sender to shortcut a hop.
	 * Only send redirect if source is sending directly to us,
	 * and if packet was not source routed (or has any options).
	 * Also, don't send redirect if forwarding using a default route
	 * or a route modified by a redirect.
	 */
#define	satosin(sa)	((struct sockaddr_in *)(sa))
	if (rt->rt_ifp == m->m_pkthdr.rcvif &&
	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
	    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
	    (_ipCfgFlags & IP_DO_REDIRECT) && !srcrt) {
#define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
		u_long src = ntohl(ip->ip_src.s_addr);

		if (RTA(rt) &&
		    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
		    if (rt->rt_flags & RTF_GATEWAY)
			dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
		    else
			dest = ip->ip_dst.s_addr;
		    /* Router requirements says to only send host redirects */
		    type = ICMP_REDIRECT;
		    code = ICMP_REDIRECT_HOST;
#ifdef DIAGNOSTIC
		    if (ipprintfs)
		        printf("redirect (%d) to %lx\n", code, (u_long)dest);
#endif
		}
	}

     /* set the M_FORWARD flag, to suggest route is to be entered into
      * fast path.
      */

    m->m_flags |= M_FORWARD;

	error = ip_output(m, (struct mbuf *)0, &ipforward_rt, IP_FORWARDING
#ifdef DIRECTED_BROADCAST
			    | IP_ALLOWBROADCAST
#endif
                          , 0);
	if (error)
		ipstat.ips_cantforward++;
	else {
		ipstat.ips_forward++;
		if (type)
			ipstat.ips_redirectsent++;
		else {
			if (mcopy)
				m_freem(mcopy);
			return;
		}
	}
	if (mcopy == NULL)
		return;
	destifp = NULL;

	switch (error) {

	case 0:				/* forwarded, but need redirect */
		/* type, code set above */
		break;

	case ENETUNREACH:		/* shouldn't happen, checked above */
	case EHOSTUNREACH:
	case ENETDOWN:
	case EHOSTDOWN:
	default:
		type = ICMP_UNREACH;
		code = ICMP_UNREACH_HOST;
		break;

	case EMSGSIZE:
		type = ICMP_UNREACH;
		code = ICMP_UNREACH_NEEDFRAG;
		if (ipforward_rt.ro_rt)
			destifp = ipforward_rt.ro_rt->rt_ifp;
		ipstat.ips_cantfrag++;
		break;

	case ENOBUFS:
		type = ICMP_SOURCEQUENCH;
		code = 0;
		break;
	}
	if (_icmpErrorHook != NULL)
	    (*_icmpErrorHook)(mcopy, type, code, dest, destifp);
}

#ifdef SYSCTL_SUPPORT
int
ip_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
	int *name;
	u_int namelen;
	void *oldp;
	size_t *oldlenp;
	void *newp;
	size_t newlen;
{
	/* All sysctl names at this level are terminal. */
	if (namelen != 1)
		return (ENOTDIR);

	switch (name[0]) {
	case IPCTL_FORWARDING:
		return (sysctl_int(oldp, oldlenp, newp, newlen, &ipforwarding));
	case IPCTL_SENDREDIRECTS:
		return (sysctl_int(oldp, oldlenp, newp, newlen,
			&ipsendredirects));
	case IPCTL_DEFTTL:
		return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_defttl));
#ifdef notyet
	case IPCTL_DEFMTU:
		return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_mtu));
#endif
	default:
		return (EOPNOTSUPP);
	}
	/* NOTREACHED */
}
#endif /* SYSCTL_SUPPORT */