# $Id: icmp.bro,v 1.4 2003/08/31 00:40:21 vern Exp $

@load hot

redef capture_filters += { ["icmp"] = "icmp" };

type icmp_flow_id: record {
	orig_h: addr;
	resp_h: addr;
	id: count;
};

type icmp_flow_info: record {
	start_time: time;
	last_time: time;
	orig_bytes: count;
	resp_bytes: count;
};

const icmp_names: table[count] of string = {
	[0] = "echo_reply",
	[3] = "unreach",
	[4] = "quench",
	[5] = "redirect",
	[8] = "echo_req",
	[9] = "router_adv",
	[10] = "router_sol",
	[11] = "time_xcd",
	[12] = "param_prob",
	[13] = "tstamp_req",
	[14] = "tstamp_reply",
	[15] = "info_req",
	[16] = "info_reply",
	[17] = "mask_req",
	[18] = "mask_reply",
} &default = function(n: count): string { return fmt("icmp-%d", n); };


global icmp_flows: table[icmp_flow_id] of icmp_flow_info;

event icmp_sent(icmp: icmp_conn)
	{
	print icmp_names[icmp$itype], icmp;
	}

event icmp_flow_summary(flow: icmp_flow_id, last_time: time)
	{
	if ( flow !in icmp_flows )
		return;

	local fi = icmp_flows[flow];

	if ( fi$last_time == last_time )
		{
		local local_init = is_local_addr(flow$orig_h);
		local local_addr = local_init ? flow$orig_h : flow$resp_h;
		local remote_addr = local_init ? flow$resp_h : flow$orig_h;
		local flags = local_init ? "L" : "";

		local state: string;
		if ( fi$orig_bytes > 0 )
			{
			if ( fi$resp_bytes > 0 )
				state = "SF";
			else
				state = "SH";
			}
		else if ( fi$resp_bytes > 0 )
			state = "SHR";
		else
			state = "OTH";

		print fmt("%.6f %.6f %s %s %s %s %s %s %s",
			fi$start_time, fi$last_time - fi$start_time, "icmp_echo",
			fi$orig_bytes, fi$resp_bytes,
			local_addr, remote_addr, state, flags);

		delete icmp_flows[flow];
		}
	}

function update_icmp_flow(icmp: icmp_conn, id: count, is_orig: bool, payload: string): bool
	{
	local flow_id: icmp_flow_id;
	flow_id$orig_h = is_orig ? icmp$orig_h : icmp$resp_h;
	flow_id$resp_h = is_orig ? icmp$resp_h : icmp$orig_h;
	flow_id$id = id;

	if ( flow_id !in icmp_flows )
		{
		local info: icmp_flow_info;
		info$start_time = network_time();
		info$orig_bytes = info$resp_bytes = 0;
		icmp_flows[flow_id] = info;
		}

	local fi = icmp_flows[flow_id];

	fi$last_time = network_time();

	if ( is_orig )
		fi$orig_bytes = fi$orig_bytes + byte_len(payload);
	else
		fi$resp_bytes = fi$resp_bytes + byte_len(payload);

	schedule +30sec { icmp_flow_summary(flow_id, fi$last_time) };
	}

event icmp_echo_request(icmp: icmp_conn, id: count, seq: count, payload: string)
	{
	update_icmp_flow(icmp, id, T, payload);
	}

event icmp_echo_reply(icmp: icmp_conn, id: count, seq: count, payload: string)
	{
	update_icmp_flow(icmp, id, F, payload);
	}

event icmp_unreachable(icmp: icmp_conn, code: count, context: icmp_context)
	{
	if ( active_connection(context$id) )
		{
		local c = connection_record(context$id);
		local os = c$orig$state;
		local rs = c$resp$state;
		local is_attempt =
			is_tcp_port(c$id$orig_p) ?
				(os == TCP_SYN_SENT && rs == TCP_INACTIVE) :
				(os == UDP_ACTIVE && rs == UDP_INACTIVE);
		}

### here
# 	print fmt("%.6f %.6f %s %s %s %s %s %s %s",
# 		fi$start_time, fi$last_time - fi$start_time, "icmp_echo",
# 		fi$orig_bytes, fi$resp_bytes,
# 		local_addr, remote_addr, state, flags);

	print "unreach", icmp, code, context;
	}
