/*
 * IGMP/ICMP/IPIP/IDP/RSVP/IPIP/IPPROTO_RAW KERNEL CHECKER
 *
 * These protocols have the same pr_usrreqs... This is a kld to monitor
 * their packets via FreeBSD kernel.
 *
 * idea & code by pIGpEN [pigpen@s0ftpj.org, deadhead@sikurezza.org ]
 *
 * Tested on FreeBSD CURRENT 4.0
 *
 * s0ftpr0ject - digital security for y2k
 * www.s0ftpj.org
 *
 * sikurezza.org - italian security mailing list
 * www.sikurezza.org
 *
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>

#include <vm/vm_zone.h>

#include <net/if.h>
#include <net/route.h>

#define _IP_VHL
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/in_var.h>
#include <netinet/ip_var.h>
#include <netinet/ip_mroute.h>


#define		print_ip(a)	printf("%d.%d.%d.%d\n",			\
					(int)(ntohl(a) >>24) & 0xFF,	\
					(int)(ntohl(a) >>16) & 0xFF,	\
					(int)(ntohl(a) >> 8) & 0xFF,	\
					(int)(ntohl(a))	& 0xFF);


static int	  rip_new_send	  __P((struct socket *, int, struct mbuf *,
				     struct sockaddr *, struct mbuf *,
				     struct proc *));

static int	  (*old_rip_send) __P((struct socket *, int, struct mbuf *,
				     struct sockaddr *, struct mbuf *,
				     struct proc *));

static int	  rip_my_output	  __P((register struct mbuf *, 
				     struct socket *, u_long));

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



/* il controllo si poteva fare direttamente su questa funzione.... non l'ho
   fatto perche' avevo gia' messo la mia rip_my_output nel sorgente :) */


static int
rip_new_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
	 struct mbuf *control, struct proc *p)
{
	struct inpcb *inp = sotoinpcb(so);
	register u_long dst;

	if (so->so_state & SS_ISCONNECTED) {
		if (nam) {
			m_freem(m);
			return EISCONN;
		}
		dst = inp->inp_faddr.s_addr;
	} else {
		if (nam == NULL) {
			m_freem(m);
			return ENOTCONN;
		}
		dst = ((struct sockaddr_in *)nam)->sin_addr.s_addr;
	}
	return rip_my_output(m, so, dst);
}

static int
rip_my_output(m, so, dst)
	register struct mbuf *m;
	struct socket *so;
	u_long dst;
{
	register struct ip *ip;
	register struct inpcb *inp = sotoinpcb(so);
	int flags = (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST;



	switch(inp->inp_ip_p) {
		case IPPROTO_RAW:  printf("RAW");
				   break;
		case IPPROTO_ICMP: printf("ICMP");
				   break;
		case IPPROTO_IGMP: printf("IGMP");
				   break;
		case IPPROTO_RSVP: printf("RSVP");
				   break;
		case IPPROTO_IPIP: printf("IPIP");
				   break;
		case IPPROTO_IDP:  printf("IDP");
				   break;				   
		default:	   printf("#%d", inp->inp_ip_p);
				   break;
	}
	
	printf(" -> ");		   print_ip(dst);
	
	/*
	 * If the user handed us a complete IP packet, use it.
	 * Otherwise, allocate an mbuf for a header and fill it in.
	 */
	if ((inp->inp_flags & INP_HDRINCL) == 0) {
		if (m->m_pkthdr.len + sizeof(struct ip) > IP_MAXPACKET) {
			m_freem(m);
			return(EMSGSIZE);
		}
		M_PREPEND(m, sizeof(struct ip), M_WAIT);
		ip = mtod(m, struct ip *);
		ip->ip_tos = 0;
		ip->ip_off = 0;
		ip->ip_p = inp->inp_ip_p;
		ip->ip_len = m->m_pkthdr.len;
		ip->ip_src = inp->inp_laddr;
		ip->ip_dst.s_addr = dst;
		ip->ip_ttl = MAXTTL;
	} else {
		if (m->m_pkthdr.len > IP_MAXPACKET) {
			m_freem(m);
			return(EMSGSIZE);
		}
		ip = mtod(m, struct ip *);
		/* don't allow both user specified and setsockopt options,
		   and don't allow packet length sizes that will crash */
		if (((IP_VHL_HL(ip->ip_vhl) != (sizeof (*ip) >> 2)) 
		     && inp->inp_options)
		    || (ip->ip_len > m->m_pkthdr.len)
		    || (ip->ip_len < (IP_VHL_HL(ip->ip_vhl) << 2))) {
			m_freem(m);
			return EINVAL;
		}
		if (ip->ip_id == 0)
			ip->ip_id = htons(ip_id++);
		/* XXX prevent ip_output from overwriting header fields */
		flags |= IP_RAWOUTPUT;
		ipstat.ips_rawout++;
	}


	
	return (ip_output(m, inp->inp_options, &inp->inp_route, flags,
			  inp->inp_moptions));
}



extern struct 	protosw 	inetsw[];

static int
s_load (struct module *module, int cmd, void *arg)
{
 int s;

 switch(cmd) {
	case MOD_LOAD:	
	  s=splnet();
	  old_rip_send = inetsw[ip_protox[IPPROTO_ICMP]].pr_usrreqs->pru_send;
	  inetsw[ip_protox[IPPROTO_ICMP]].pr_usrreqs->pru_send = rip_new_send;
  	  splx(s);
	  break;
			
	case MOD_UNLOAD:
	  s=splnet();
	  inetsw[ip_protox[IPPROTO_ICMP]].pr_usrreqs->pru_send = old_rip_send;
 	  splx(s);
	  break;
 }

 return 0;
}

static moduledata_t s_mod_1 = {
	        "raww_mod",
	        s_load,
	        0
};

DECLARE_MODULE(raww_mod, s_mod_1, SI_SUB_PSEUDO, SI_ORDER_ANY);
