#include <u.h>
#include <libc.h>
#include <ip.h>
#include "dat.h"
#include "protos.h"

typedef struct Hdr	Hdr;
struct Hdr
{
	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */
	uchar	length[2];	/* packet length */
	uchar	id[2];		/* ip->identification */
	uchar	frag[2];	/* Fragment information */
	uchar	ttl;		/* Time to live */
	uchar	proto;		/* Protocol */
	uchar	cksum[2];	/* Header checksum */
	uchar	src[4];		/* IP source */
	uchar	dst[4];		/* IP destination */
};

enum
{
	IPHDR		= 20,		/* sizeof(Iphdr) */
	IP_VER		= 0x40,		/* Using IP version 4 */
	IP_DF		= 0x4000,	/* Don't fragment */
	IP_MF		= 0x2000,	/* More fragments */
};

static Mux p_mux[] =
{
	{ "icmp", 1, },
	{ "igmp", 2, },
	{ "ggp", 3, },
	{ "ip", 4, },
	{ "st", 5, },
	{ "tcp", 6, },
	{ "ucl", 7, },
	{ "egp", 8, },
	{ "igp", 9, },
	{ "bbn-rcc-mon", 10, },
	{ "nvp-ii", 11, },
	{ "pup", 12, },
	{ "argus", 13, },
	{ "emcon", 14, },
	{ "xnet", 15, },
	{ "chaos", 16, },
	{ "udp", 17, },
	{ "mux", 18, },
	{ "dcn-meas", 19, },
	{ "hmp", 20, },
	{ "prm", 21, },
	{ "xns-idp", 22, },
	{ "trunk-1", 23, },
	{ "trunk-2", 24, },
	{ "leaf-1", 25, },
	{ "leaf-2", 26, },
	{ "rdp", 27, },
	{ "irtp", 28, },
	{ "iso-tp4", 29, },
	{ "netblt", 30, },
	{ "mfe-nsp", 31, },
	{ "merit-inp", 32, },
	{ "sep", 33, },
	{ "3pc", 34, },
	{ "idpr", 35, },
	{ "xtp", 36, },
	{ "ddp", 37, },
	{ "idpr-cmtp", 38, },
	{ "tp++", 39, },
	{ "il", 40, },
	{ "sip", 41, },
	{ "sdrp", 42, },
	{ "sip-sr", 43, },
	{ "sip-frag", 44, },
	{ "idrp", 45, },
	{ "rsvp", 46, },
	{ "gre", 47, },
	{ "mhrp", 48, },
	{ "bna", 49, },
	{ "sipp-esp", 50, },
	{ "sipp-ah", 51, },
	{ "i-nlsp", 52, },
	{ "swipe", 53, },
	{ "nhrp", 54, },
	{ "any", 61, },
	{ "cftp", 62, },
	{ "any", 63, },
	{ "sat-expak", 64, },
	{ "kryptolan", 65, },
	{ "rvd", 66, },
	{ "ippc", 67, },
	{ "any", 68, },
	{ "sat-mon", 69, },
	{ "visa", 70, },
	{ "ipcv", 71, },
	{ "cpnx", 72, },
	{ "cphb", 73, },
	{ "wsn", 74, },
	{ "pvp", 75, },
	{ "br-sat-mon", 76, },
	{ "sun-nd", 77, },
	{ "wb-mon", 78, },
	{ "wb-expak", 79, },
	{ "iso-ip", 80, },
	{ "vmtp", 81, },
	{ "secure-vmtp", 82, },
	{ "vines", 83, },
	{ "ttp", 84, },
	{ "nsfnet-igp", 85, },
	{ "dgp", 86, },
	{ "tcf", 87, },
	{ "igrp", 88, },
	{ "ospf", 89, },
	{ "sprite-rpc", 90, },
	{ "larp", 91, },
	{ "mtp", 92, },
	{ "ax.25", 93, },
	{ "ipip", 94, },
	{ "micp", 95, },
	{ "scc-sp", 96, },
	{ "etherip", 97, },
	{ "encap", 98, },
	{ "any", 99, },
	{ "gmtp", 100, },
	{ "rudp", 254, },
	{ 0 }
};

enum
{
	Os,	/* source */
	Od,	/* destination */
	Osd,	/* source or destination */
	Ot,	/* type */
};

static Field p_fields[] = 
{
	{"s",	Fv4ip,	Os,	"source address",	} ,
	{"d",	Fv4ip,	Od,	"destination address",	} ,
	{"a",	Fv4ip,	Osd,	"source|destination address",} ,
	{"sd",	Fv4ip,	Osd,	"source|destination address",} ,
	{"t",	Fnum,	Ot,	"sub protocol number",	} ,
	{0}
};

static void
p_compile(Filter *f)
{
	Mux *m;

	if(f->op == '='){
		compile_cmp(ip.name, f, p_fields);
		return;
	}
	for(m = p_mux; m->name != nil; m++)
		if(strcmp(f->s, m->name) == 0){
			f->pr = m->pr;
			f->ulv = m->val;
			f->subop = Ot;
			return;
		}
	sysfatal("unknown ip field or protocol: %s", f->s);
}

static int
p_filter(Filter *f, Msg *m)
{
	Hdr *h;

	if(m->pe - m->ps < IPHDR)
		return 0;

	h = (Hdr*)m->ps;
	m->ps += ((h->vihl&0xf)<<2);

	switch(f->subop){
	case Os:
		return NetL(h->src) == f->ulv;
	case Od:
		return NetL(h->dst) == f->ulv;
	case Osd:
		return NetL(h->src) == f->ulv || NetL(h->dst) == f->ulv;
	case Ot:
		return h->proto == f->ulv;
	}
	return 0;
}

static int
p_seprint(Msg *m)
{
	Hdr *h;
	int f;
	int len;

	if(m->pe - m->ps < IPHDR)
		return -1;
	h = (Hdr*)m->ps;

	/* next protocol, just dump unless this is the first fragment */
	m->pr = &dump;
	f = NetS(h->frag);
	if((f & ~(IP_DF|IP_MF)) == 0)
		demux(p_mux, h->proto, h->proto, m, &dump);

	/* truncate the message if there's extra */
	len = NetS(h->length);
	if(len < m->pe - m->ps)
		m->pe = m->ps + len;

	/* next header */
	m->ps += ((h->vihl&0xf)<<2);

	m->p = seprint(m->p, m->e, "s=%V d=%V id=%4.4ux frag=%4.4ux ttl=%3d pr=%d ln=%d",
			h->src, h->dst,
			NetS(h->id),
			NetS(h->frag),
			h->ttl,
			h->proto,
			NetS(h->length)
			);
	return 0;
}

Proto ip =
{
	"ip",
	p_compile,
	p_filter,
	p_seprint,
	p_mux,
	"%lud",
	p_fields,
	defaultframer
};
