/*
 * Copyright (C) 2000, 2003 by Darren Reed.
 *
 * See the IPFILTER.LICENCE file for details on licencing.
 */

#ifndef __hpux
#pragma ident "@(#)$Id: pkt.c,v 1.12 2004/01/15 11:25:21 darrenr Exp $"
#endif

#include <sys/types.h>
#include <sys/stream.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <sys/rwlock.h>
#include <sys/socket.h>

#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#if SOLARIS2 >= 8
# include <netinet/ip6.h>
#else
# include <net/if_dl.h>
#endif

#undef IPOPT_EOL
#undef IPOPT_NOP
#undef IPOPT_RR
#undef IPOPT_LSRR
#undef IPOPT_SSRR
#include <inet/common.h>
#include <inet/ip.h>
#include <inet/ip_ire.h>
#include <inet/ip_if.h>

#include "compat.h"
#include "qif.h"


extern krwlock_t pfil_rw;


/* ------------------------------------------------------------------------ */
/* Function:    pfil_sendbuf                                                */
/* Returns:     int  - 0 == success, 1 == failure                           */
/* Parameters:  m(I) - pointer to streams message                           */
/*                                                                          */
/* Output an IPv4 packet to whichever interface has the correct route.      */
/* ------------------------------------------------------------------------ */
int pfil_sendbuf(m)
mblk_t *m;
{
	queue_t *q = NULL;
	struct ip *ip;
	size_t hlen;
	ire_t *dir;
	u_char *s;
	ill_t *il;

	ip = (struct ip *)m->b_rptr;

#ifdef	MATCH_IRE_DSTONLY
	dir = ire_route_lookup(ip->ip_dst.s_addr, 0xffffffff, 0, 0,
				NULL, NULL, NULL,
				MATCH_IRE_DSTONLY|MATCH_IRE_DEFAULT|
				MATCH_IRE_RECURSIVE);
#else
	dir = ire_lookup(ip->ip_dst.s_addr);
#endif

	if (dir) {
#if SOLARIS2 >= 8
		if (!dir->ire_fp_mp || !dir->ire_dlureq_mp)
#else
		if (!dir->ire_ll_hdr_mp || !dir->ire_ll_hdr_length)
#endif
			return 2;
	}

	if (dir) {
		mblk_t *mp, *mp2;

		il = ire_to_ill(dir);
		if (!il)
			return 2;
#if SOLARIS2 < 8
		mp = dir->ire_ll_hdr_mp;
		hlen = dir->ire_ll_hdr_length;
#else
		mp = dir->ire_fp_mp;
		hlen = mp ? mp->b_wptr - mp->b_rptr : 0;
		mp = dir->ire_dlureq_mp;
#endif
		s = (u_char *)ip;

		if (hlen &&
#ifdef	ICK_M_CTL_MAGIC
		    (il->ill_ick.ick_magic != ICK_M_CTL_MAGIC) &&
#endif
		    (s - m->b_datap->db_base) >= hlen) {
			s -= hlen;
			m->b_rptr = (u_char *)s;
			bcopy((char *)mp->b_rptr, (char *)s, hlen);
		} else {
			mp2 = copyb(mp);
			if (!mp2)
				goto bad_nexthop;
			mp2->b_cont = m;
			m = mp2;
		}

		if (dir->ire_stq)
			q = dir->ire_stq;
		else if (dir->ire_rfq)
			q = WR(dir->ire_rfq);
		if (q)
			q = q->q_next;
		if (q) {
			RW_EXIT(&pfil_rw);
			putnext(q, m);
			READ_ENTER(&pfil_rw);
			return 0;
		}
	}
bad_nexthop:
	freemsg(m);
	return 1;
}
