/*
 * Name: kerninetstat 
 * Date: Sun Feb 13 13:16:33 2000
 * Author: pIGpEN [pigpen@s0ftpj.org, deadhead@sikurezza.org]
 *
 * SoftProject Digital Security for Y2K (www.s0ftpj.org)
 * Sikurezza.org Italian Security MailingList (www.sikurezza.org)
 * 
 * COFFEE-WARE LICENSE - This source code is like "THE BEER-WARE LICENSE" by
 * Poul-Henning Kamp <phk@FreeBSD.ORG> but you can give me in return a coffee.
 * 
 * Tested on: FreeBSD 4.0-19990705-CURRENT FreeBSD 4.0-19990705-CURRENT #6 i386
 *
 * This simple source code uses sysctlbyname() to fetch statistics of a protocol
 * you can use them for security purposes or for kernel testing... see also
 * sources of systat or netstat -s...
 *
 * Note: some variables of stat structures can be not present in other kernel
 * versions
 */

/* 
 * knstat is intended to be used as cron job example:
 *
 * knstat -icmp >> icmp_stat.log
 *
 * if you wanna use this tool like a command define WAIT
 *
 * #define WAIT
 *
 */



#include <stdio.h>
#include <string.h>
#include <err.h>
#include <sysexits.h>

#include <sys/types.h>
#include <sys/sysctl.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp_var.h>
#include <netinet/igmp.h>
#include <netinet/igmp_var.h>

#define		Error(s)	err(EX_UNAVAILABLE, s);
#define		E(s)		if(!strcmp(s, arg[1]))

void	usage		__P((char *));
void	ip_stat		__P((void));
void	udp_stat	__P((void));
void	tcp_stat	__P((void));
void	icmp_stat	__P((void));
void	igmp_stat	__P((void));

struct prot {
	char *name;
	void (*funct) (void);
};
	

struct prot protos[] = {
	{ "-ip"  , ip_stat   },
	{ "-udp" , udp_stat  },
	{ "-tcp" , tcp_stat  },
	{ "-icmp", icmp_stat },
	{ "-igmp", igmp_stat },
};

	
int main(int narg, char **arg)
{
	int i;
	int len = sizeof(protos) / sizeof(struct prot);

	
	if(narg != 2){
		usage(arg[0]);
		exit(0);
	}

	/* Think different */
	
	for(i=0; i < len; i++) 
		E(protos[i].name) {	
			(*protos[i].funct) ();	
			return 1;	
		}

	usage(arg[0]);

	return 0; 
}

void usage(char *cmdname)
{
	printf("Usage:\n"
	       "\t%s -option\n\n"
	       "Option:   -ip\n" 
	       "          -icmp\n"
	       "          -igmp\n"
	       "          -tcp\n"
	       "          -udp\n", cmdname);
}	       

/*
 * You can cover printf() with a macro... but I'm fucking about... so
 * I have time to spend... 
 */

void ip_stat(void)
{
 struct ipstat i_stat;
 int len = sizeof(i_stat);

 if(sysctlbyname("net.inet.ip.stats", &i_stat, &len, 0, 0) < 0 )
	 Error("[ip_stat] sysctlbyname");

 printf("IP Statistics\n\n");
 
 printf("\t\treceived         [ %ld ]\n", i_stat.ips_total);
 printf("\t\tbad checksum     [ %ld ]\n", i_stat.ips_badsum);
 printf("\t\tpkts too short   [ %ld ]\n", i_stat.ips_tooshort);
 printf("\t\tno enough data   [ %ld ]\n", i_stat.ips_toosmall);
 printf("\t\tiph len < data   [ %ld ]\n", i_stat.ips_badhlen);
 printf("\t\tip len < iph len [ %ld ]\n", i_stat.ips_badlen);
 printf("\t\tfragments        [ %ld ]\n", i_stat.ips_fragments);
 printf("\t\tfrags dropped    [ %ld ]\n", i_stat.ips_fragdropped);
 printf("\t\tfrags timeout    [ %ld ]\n", i_stat.ips_fragtimeout);
 printf("\t\tforwarded        [ %ld ]\n", i_stat.ips_forward);
 printf("\t\tfast forward     [ %ld ]\n", i_stat.ips_fastforward);
 printf("\t\tcant forward     [ %ld ]\n", i_stat.ips_cantforward);
 printf("\t\tredirect sent    [ %ld ]\n", i_stat.ips_redirectsent);
 printf("\t\tproto unknown    [ %ld ]\n", i_stat.ips_noproto);
 printf("\t\tiplen > maxpksz  [ %ld ]\n", i_stat.ips_toolong);
 printf("\t\tip version != 4  [ %ld ]\n", i_stat.ips_badvers);
 printf("\t\ttotal raw gen    [ %ld ]\n", i_stat.ips_rawout);
 printf("\t\tmcast not memb   [ %ld ]\n", i_stat.ips_notmember);
}

void udp_stat(void)
{
 struct udpstat u_stat;
 int len = sizeof(u_stat);

 if(sysctlbyname("net.inet.udp.stats", &u_stat, &len, 0, 0) < 0) 
	Error("[udp_stat] sysctlbyname");
 
 printf("UDP Statistics\n\n");
 
 printf("Total input  packets:      %ld\n", 	   u_stat.udps_ipackets);
 printf("\t\t\t\tPacket shorter than header: %ld\n", u_stat.udps_hdrops);
 printf("\t\t\t\tChecksum error:             %ld\n", u_stat.udps_badsum);
 printf("\t\t\t\tData len larger than pkt:   %ld\n", u_stat.udps_badlen);
 printf("\t\t\t\tNo socket on port:          %ld\n", u_stat.udps_noport);
 printf("\t\t\t\tArrived as broadcast:       %ld\n", u_stat.udps_noportbcast);
 printf("\t\t\t\tNot delivered:              %ld\n", u_stat.udps_fullsock);
 printf("\t\t\t\tMissing pcb cache:          %ld\n", u_stat.udpps_pcbcachemiss);
 printf("\t\t\t\tNot for hashed pcb:         %ld\n\n",u_stat.udpps_pcbhashmiss);
 printf("Total ouput packets:       %ld\n",	   u_stat.udps_opackets);
 printf("\t\t\t\tFast path:                  %ld\n", u_stat.udps_fastout);
}	

void tcp_stat(void)
{
 struct tcpstat t_stat;
 int len = sizeof(t_stat);

 if(sysctlbyname("net.inet.tcp.stats", &t_stat, &len, 0, 0) < 0)
	 Error("[tcp_stat] sysctlbyname");

 printf("TCP Statistics\n\n");

 printf("Connection:\n");
 printf("\t\tinitiated:               [ %ld ]\n", t_stat.tcps_connattempt);
 printf("\t\taccepted:                [ %ld ]\n", t_stat.tcps_accepts);
 printf("\t\testabilished             [ %ld ]\n", t_stat.tcps_connects);
 printf("\t\tdropped                  [ %ld ]\n", t_stat.tcps_drops);
 printf("\t\tembryonic dropped        [ %ld ]\n", t_stat.tcps_conndrops);
 printf("\t\tkeepalive dropped        [ %ld ]\n", t_stat.tcps_keepdrops);
 printf("\t\tclosed                   [ %ld ]\n", t_stat.tcps_closed);
 printf("\nTimers:\n");
 printf("\t\tsegs timed               [ %ld ]\n", t_stat.tcps_segstimed);
 printf("\t\trtt updated              [ %ld ]\n", t_stat.tcps_rttupdated);
 printf("\t\tdelayed acks sent        [ %ld ]\n", t_stat.tcps_delack);
 printf("\t\tdropped in rxmt timeouts [ %ld ]\n", t_stat.tcps_timeoutdrop);
 printf("\t\tretrasmit timeouts       [ %ld ]\n", t_stat.tcps_rexmttimeo);
 printf("\t\tpersist timeouts         [ %ld ]\n", t_stat.tcps_persisttimeo);
 printf("\t\tkeepalive timeouts       [ %ld ]\n", t_stat.tcps_keeptimeo);
 printf("\t\tkeepalive probes sent    [ %ld ]\n", t_stat.tcps_keepprobe);
#ifdef	WAIT 
 getchar();
#endif 
 printf("\nPackets\n");
 printf("\t\tsent                     [ %ld ]\n", t_stat.tcps_sndtotal);
 printf("\t\tdata pkt                 [ %ld ]\n", t_stat.tcps_sndpack);
 printf("\t\tdata bytes               [ %ld ]\n", t_stat.tcps_sndbyte);
 printf("\t\tdata pkt retrasmitted    [ %ld ]\n", t_stat.tcps_sndrexmitpack);
 printf("\t\tdata bytes retrasmitted  [ %ld ]\n", t_stat.tcps_sndrexmitbyte);
 printf("\t\tack only pkts            [ %ld ]\n", t_stat.tcps_sndacks);
 printf("\t\turg only pkts            [ %ld ]\n", t_stat.tcps_sndurg);
 printf("\t\twin update only pkt      [ %ld ]\n", t_stat.tcps_sndwinup);
 printf("\t\tsyn|fin|rst pkt          [ %ld ]\n", t_stat.tcps_sndctrl);
 printf("\t\twindow probes            [ %ld ]\n", t_stat.tcps_sndprobe);
 printf("\n\n");
#ifdef	WAIT 
 getchar();
#endif 
 printf("\t\treceived                 [ %ld ]\n", t_stat.tcps_rcvtotal);
 printf("\t\tpkt in sequence          [ %ld ]\n", t_stat.tcps_rcvpack);
 printf("\t\tbyte in sequence         [ %ld ]\n", t_stat.tcps_rcvbyte);
 printf("\t\tpkt with checksum errors [ %ld ]\n", t_stat.tcps_rcvbadsum);
 printf("\t\tpkt with bad offset      [ %ld ]\n", t_stat.tcps_rcvbadoff);
 printf("\t\tpkt received too short   [ %ld ]\n", t_stat.tcps_rcvshort);
 printf("\t\tduplicate only pkts      [ %ld ]\n", t_stat.tcps_rcvduppack);
 printf("\t\tduplicate only bytes     [ %ld ]\n", t_stat.tcps_rcvdupbyte);
 printf("\t\tpartial duplicate data   [ %ld ]\n", t_stat.tcps_rcvpartduppack);
 printf("\t\tpartial duplicate bytes  [ %ld ]\n", t_stat.tcps_rcvpartdupbyte);
 printf("\t\tout of order pkts        [ %ld ]\n", t_stat.tcps_rcvoopack);
 printf("\t\tout of order bytes       [ %ld ]\n", t_stat.tcps_rcvoobyte);
 printf("\t\tpkts with data after win [ %ld ]\n", t_stat.tcps_rcvpackafterwin);
 printf("\t\tbytes received after win [ %ld ]\n", t_stat.tcps_rcvbyteafterwin);
 printf("\t\tpkts rcvd after close    [ %ld ]\n", t_stat.tcps_rcvafterclose);
 printf("\t\tpkts rcvd win probe      [ %ld ]\n", t_stat.tcps_rcvwinprobe);
 printf("\t\tduplicate acks           [ %ld ]\n", t_stat.tcps_rcvdupack);
 printf("\t\tacks for unsent data     [ %ld ]\n", t_stat.tcps_rcvacktoomuch);
 printf("\t\tacks packets             [ %ld ]\n", t_stat.tcps_rcvackpack);
 printf("\t\tbytes acked by rcvd acks [ %ld ]\n", t_stat.tcps_rcvwinupd);
 printf("\t\tseg dropped due to PAWS  [ %ld ]\n", t_stat.tcps_pawsdrop);
 printf("\t\tbogus syn                [ %ld ]\n", t_stat.tcps_badsyn);
 printf("\t\tresnd due to MTU discov. [ %ld ]\n", t_stat.tcps_mturesent);
 printf("\t\tlisten queue overflow    [ %ld ]\n", t_stat.tcps_listendrop);
} 

void icmp_stat(void)
{
 struct icmpstat i_stat;
 int len = sizeof i_stat;

 if(sysctlbyname("net.inet.icmp.stats", &i_stat, &len, 0, 0) < 0)
	 Error("[icmp_stat] sysctlbyname");

 printf("ICMP Statistics\n\n");

 printf("\t\tNumber of calls to icmp_error  [ %ld ]\n", i_stat.icps_error);
 printf("\t\ticmp_code out of range         [ %ld ]\n", i_stat.icps_badcode);
 printf("\t\tpkts < ICMP_MINLEN             [ %ld ]\n", i_stat.icps_tooshort);
 printf("\t\tbad checksum                   [ %ld ]\n", i_stat.icps_checksum);
 printf("\t\tbad length                     [ %ld ]\n", i_stat.icps_badlen);
 printf("\t\tnumber of responses            [ %ld ]\n", i_stat.icps_reflect);
 printf("\t\tm/bcast echo requests dropped  [ %ld ]\n", i_stat.icps_bmcastecho);
}

void igmp_stat(void)
{
 struct igmpstat i_stat;
 int len = sizeof i_stat;

 if(sysctlbyname("net.inet.igmp.stats", &i_stat, &len, 0, 0) < 0)
	 Error("[igmp_stat] sysctlbyname");

 printf("IGMP Statistics\n\n");

 printf("\t\tmessages received       [ %d ]\n", i_stat.igps_rcv_total);
 printf("\t\trcvd with too few bytes [ %d ]\n", i_stat.igps_rcv_tooshort);
 printf("\t\trcvd with bad checksum  [ %d ]\n", i_stat.igps_rcv_badsum);
 printf("\t\trcvd membership queries [ %d ]\n", i_stat.igps_rcv_queries);
 printf("\t\trcvd invalid queries    [ %d ]\n", i_stat.igps_rcv_badqueries);
 printf("\t\trcvd membership reports [ %d ]\n", i_stat.igps_rcv_reports);
 printf("\t\trcvd invalid reports    [ %d ]\n", i_stat.igps_rcv_badreports);
 printf("\t\trcvd rep. for our grps  [ %d ]\n", i_stat.igps_rcv_ourreports);
 printf("\t\tsent membership reports [ %d ]\n", i_stat.igps_snd_reports);
}
