/*
 * This kld gives you an example of how you can modify 
 * the output function of an Ethernet Interface....
 * 
 * 
 * Note: Don't use it for loopback, ppp or other no eth interfaces !
 *
 * pigpen [pigpen@s0ftpj.org, deadhead@sikurezza.org]
 * 
 * SoftProject NoProfit 
 * Italian Security Organization
 * www.s0ftpj.org
 *
 * Sikurezza.org
 * Italian Security MailingList
 * www.sikurezza.org
 *
 */

/* 
 * pay attention... this kld can change in future...
 * 
 * uname -a
 *
 * FreeBSD storpio.cameretta.pig 4.0-19990705-CURRENT FreeBSD 4.0-19990705-
 * CURRENT #4 ..... i386
 *
 * If you wanna a porting of this code and you have no time to do that 
 * write me at: deadhead@sikurezza.org with subject "PORTING A KLD"
 * 
 */

#define INTERFACE		"ed"
#define INTERFACE_NUM		0


#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>

#include <net/netisr.h>
#include <net/route.h>
#include <net/if_types.h>

#include <netinet/in_var.h>
#include <netinet/if_ether.h>




#define		IFP2AC(IFP)	((struct arpcom *) IFP)
#define		senderr(e)	do { error = (e); goto bad;} while (0)

int	my_eth_output			__P((register struct ifnet *,
					     struct mbuf *, struct sockaddr *,
					     struct rtentry *));

static int	module_handler		__P((struct module *, int, void *));

/*
 * Ethernet output routine.
 * Encapsulate a packet of type family for the local net.
 * Use trailer local net encapsulation if enough data in first
 * packet leaves a multiple of 512 bytes of data in remainder.
 * Assumes that ifp is actually pointer to arpcom structure.
 */
int
my_eth_output(ifp, m0, dst, rt0)
	register struct ifnet *ifp;
	struct mbuf *m0;
	struct sockaddr *dst;
	struct rtentry *rt0;
{
	short type;
	int s, error = 0;
 	u_char edst[6];
	register struct mbuf *m = m0;
	register struct rtentry *rt;
	register struct ether_header *eh;
	int off, len = m->m_pkthdr.len, loop_copy = 0;
	int hlen;	/* link layer header lenght */
	struct arpcom *ac = IFP2AC(ifp);

	
	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
		senderr(ENETDOWN);
	rt = rt0;
	if (rt) {
		if ((rt->rt_flags & RTF_UP) == 0) {
			rt0 = rt = rtalloc1(dst, 1, 0UL);
			if (rt0)
				rt->rt_refcnt--;
			else
				senderr(EHOSTUNREACH);
		}
		if (rt->rt_flags & RTF_GATEWAY) {
			if (rt->rt_gwroute == 0)
				goto lookup;
			if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
				rtfree(rt); rt = rt0;
			lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1,
							  0UL);
				if ((rt = rt->rt_gwroute) == 0)
					senderr(EHOSTUNREACH);
			}
		}
		if (rt->rt_flags & RTF_REJECT)
			if (rt->rt_rmx.rmx_expire == 0 ||
			    time_second < rt->rt_rmx.rmx_expire)
				senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
	}
	hlen = ETHER_HDR_LEN;
	switch (dst->sa_family) {
	case AF_INET:
		if (!arpresolve(ac, rt, m, dst, edst, rt0))
			return (0);	/* if not yet resolved */
		off = m->m_pkthdr.len - m->m_len;
		type = htons(ETHERTYPE_IP);
		break;
	case AF_UNSPEC:
		loop_copy = -1; /* if this is for us, don't do it */
		eh = (struct ether_header *)dst->sa_data;
 		(void)memcpy(edst, eh->ether_dhost, sizeof (edst));
		type = eh->ether_type;
		break;

	default:
		printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
			dst->sa_family);
		senderr(EAFNOSUPPORT);
	}

	/*
	 * Add local net header.  If no space in first mbuf,
	 * allocate another.
	 */
	M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT);
	if (m == 0)
		senderr(ENOBUFS);
	eh = mtod(m, struct ether_header *);
	(void)memcpy(&eh->ether_type, &type,
		sizeof(eh->ether_type));
 	(void)memcpy(eh->ether_dhost, edst, sizeof (edst));
 	(void)memcpy(eh->ether_shost, ac->ac_enaddr,
	    sizeof(eh->ether_shost));

	/*
	 * If a simplex interface, and the packet is being sent to our
	 * Ethernet address or a broadcast address, loopback a copy.
	 * XXX To make a simplex device behave exactly like a duplex
	 * device, we should copy in the case of sending to our own
	 * ethernet address (thus letting the original actually appear
	 * on the wire). However, we don't do that here for security
	 * reasons and compatibility with the original behavior.
	 */
	if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
		if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
			struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);

			(void) if_simloop(ifp, n, dst, hlen);
		} else if (bcmp(eh->ether_dhost,
		    eh->ether_shost, ETHER_ADDR_LEN) == 0) {
			(void) if_simloop(ifp, m, dst, hlen);
			return (0);	/* XXX */
		}
	}
/*#ifdef BRIDGE
	if (do_bridge) {
		struct mbuf *m0 = m ;

		if (m->m_pkthdr.rcvif)
			m->m_pkthdr.rcvif = NULL ;
		ifp = bridge_dst_lookup(m);
		bdg_forward(&m0, ifp);
		if (m0)
			m_freem(m0);
		return (0);
	}
#endif*/
	s = splimp();
	/*
	 * Queue message on interface, and start output if interface
	 * not yet active.
	 */
	if (IF_QFULL(&ifp->if_snd)) {
		IF_DROP(&ifp->if_snd);
		splx(s);
		senderr(ENOBUFS);
	}
	IF_ENQUEUE(&ifp->if_snd, m);
	if ((ifp->if_flags & IFF_OACTIVE) == 0)
		(*ifp->if_start)(ifp);
	splx(s);
	ifp->if_obytes += len + sizeof (struct ether_header);
	if (m->m_flags & M_MCAST)
		ifp->if_omcasts++;
	return (error);

bad:
	if (m)
		m_freem(m);
	return (error);
}



static int
module_handler(struct module *module, int cmd, void *arg) {
	int s;
	struct ifnet *ifp;
	
	switch(cmd) {
		case MOD_LOAD:
		 s = splimp();
		 for(ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) {
		  /* pig: sys/queue.h-> TAILQ_FOREACH(ifp, &ifnet, if_link) */
			printf("%s%d -> ", ifp->if_name, ifp->if_unit);
			if(!strcmp(ifp->if_name,INTERFACE) &&
					ifp->if_unit == INTERFACE_NUM) {
				ifp->if_output = my_eth_output;
				printf("MODIFIED");
			} else
				printf("no");
			printf("\n");
		 }
		 splx(s);
		 break;
		 
		case MOD_UNLOAD:
		 s = splimp();
		 for(ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) {
		  if( !strcmp(ifp->if_name,INTERFACE) &&
			     ifp->if_unit == INTERFACE_NUM) {
		   ifp->if_output = ether_output;
		   printf("%s%d output funct: Updated\n",ifp->if_name, 
				   		         ifp->if_unit);
		  }
		 }
		 splx(s);
		 break;
	}

	return 0;
}

static moduledata_t mymod = {
	"eth_out",
	module_handler,
	0
};

DECLARE_MODULE(eth_out, mymod, SI_SUB_PSEUDO, SI_ORDER_ANY);
