# -*- tab-width: 4 -*- ;; Emacs
# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM
############################################################ IDENT(1)
#
# $Title: dwatch(8) JSON module for network activity $
# $Copyright: 2014-2022 Devin Teske. All rights reserved. $
# $FrauBSD: dwatch-json/json-net-raw 2022-08-19 10:18:35 -0700 freebsdfrau $
# $Version: 0.9 $
#
############################################################ DESCRIPTION
#
# Produce JSON custom log format for network activity
#
############################################################ PROBE

: ${PROBE:=$( echo \
	tcp:::debug-user, \
	tcp:::state-change, \
	udp:::send, \
	syscall::read:return, \
	syscall::recvfrom:return, \
	syscall::recvmsg:return )}

############################################################ EVENT ACTION

EVENT_TEST='this->inet && this->event != ""'

############################################################ ACTIONS

exec 9<<EOF
this uint8_t	inet;
this string	event;
this string	family;
this string	local;
this string	remote;
this u_char	local6;
this u_char	remote6;
this uint16_t	lport;
this uint16_t	rport;
this uint32_t	length;

typedef struct sainfo {
	sa_family_t sa_family;
	uint16_t port;
	string addr;
} sainfo_t;

#pragma D binding "1.13" sa_data_size
inline int sa_data_size = 30; /* 14 if only handling IPv4 */
#pragma D binding "1.13" sa_dummy_data
inline char *sa_dummy_data =
	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

#pragma D binding "1.13" sa_data_addr
inline string sa_data_addr[sa_family_t af, char data[sa_data_size]] =
	af == AF_INET ? inet_ntoa((in_addr_t *)&data[2]) :
	af == AF_INET6 ? inet_ntoa6((struct in6_addr *)&data[6]) :
	"<unknown>";

#pragma D binding "1.13" sa_data_port
inline uint16_t sa_data_port[sa_family_t af, char data[sa_data_size]] =
	af == AF_INET ? ((data[0] & 0xFF) << 8) + (data[1] & 0xFF) :
	af == AF_INET6 ? ((data[0] & 0xFF) << 8) + (data[1] & 0xFF) :
	0;

#pragma D binding "1.13" translator
translator sainfo_t < struct sockaddr *SA > {
	sa_family =	SA == NULL ? 0 : SA->sa_family;
	addr =		SA == NULL ?
	    sa_data_addr[0, sa_dummy_data] :
	    sa_data_addr[SA->sa_family, SA->sa_data];
	port =		SA == NULL ?
	    sa_data_port[0, sa_dummy_data] :
	    sa_data_port[SA->sa_family, SA->sa_data];
};

/****************************** TCP ******************************/

tcp:::send,
tcp:::receive /* probe ID $ID */
{${TRACE:+
	printf("<$ID>");}
	this->length = (uint32_t)args[2]->ip_plength -
                (uint8_t)args[4]->tcp_offset;
}

tcp:::debug-user /* probe ID $(( $ID + 1 )) */
{${TRACE:+
	printf("<$(( $ID + 1 ))>");
}
	/*
	 * tcpsinfo_t *
	 */
	this->local  = args[0]->tcps_laddr;
	this->lport  = args[0]->tcps_lport;
	this->remote = args[0]->tcps_raddr;
	this->rport  = args[0]->tcps_rport;

	/*
	 * IPv6 support
	 */
	this->local6 = strstr(this->local, ":") != NULL ? 1 : 0;
	this->remote6 = strstr(this->remote, ":") != NULL ? 1 : 0;
	this->local = strjoin(strjoin(this->local6 ? "[" : "",
		this->local), this->local6 ? "]" : "");
	this->remote = strjoin(strjoin(this->remote6 ? "[" : "",
		this->remote), this->remote6 ? "]" : "");

	this->family = "tcp";
	this->event = prureq_string[arg1];
	this->inet = 1;
}

tcp:::state-change /* probe ID $(( $ID + 2 )) */
{${TRACE:+
	printf("<$(( $ID + 2 ))>");
}
	/*
	 * tcpsinfo_t *
	 */
	this->local    = args[3]->tcps_laddr;
	this->lport    = (uint16_t)args[3]->tcps_lport;
	this->remote   = args[3]->tcps_raddr;
	this->rport    = (uint16_t)args[3]->tcps_rport;
	this->to_state = (int32_t)args[3]->tcps_state;

	/*
	 * tcplsinfo_t *
	 */
	this->from_state = (int32_t)args[5]->tcps_state;

	/*
	 * IPv6 support
	 */
	this->local6 = strstr(this->local, ":") != NULL ? 1 : 0;
	this->remote6 = strstr(this->remote, ":") != NULL ? 1 : 0;
	this->local = strjoin(strjoin(this->local6 ? "[" : "",
		this->local), this->local6 ? "]" : "");
	this->remote = strjoin(strjoin(this->remote6 ? "[" : "",
		this->remote), this->remote6 ? "]" : "");

	this->family = "tcp";
	this->event = this->to_state == TCPS_CLOSED ? "CLOSE" : "";
	this->length = 0;
	this->inet = 1;
}

/****************************** UDP ******************************/

/*
 * UDP send(2) [syscall] stacks:
 * sys_write -> dofilewrite -> soo_write -> sosend -> sosend_dgram -> udp:send
 * sys_sendto -> sendit -> kern_sendit -> sosend -> sosend_dgram -> udp:send
 * sys_sendmsg -> sendit -> kern_sendit -> sosend -> sosend_dgram -> udp:send
 */

udp:::send /* probe ID $(( $ID + 3 )) */
{${TRACE:+
	printf("<$(( $ID + 3 ))>");
}
	/*
	 * ipinfo_t *
	 */
	this->local  = args[2]->ip_saddr;
	this->remote = args[2]->ip_daddr;

	/*
	 * udpinfo_t *
	 */
	this->length = (uint32_t)args[4]->udp_length - sizeof(struct udphdr);
	this->lport  = args[4]->udp_sport;
	this->rport  = args[4]->udp_dport;

	/*
	 * IPv6 support
	 */
	this->local6 = strstr(this->local, ":") != NULL ? 1 : 0;
	this->remote6 = strstr(this->remote, ":") != NULL ? 1 : 0;
	this->local = strjoin(strjoin(this->local6 ? "[" : "",
		this->local), this->local6 ? "]" : "");
	this->remote = strjoin(strjoin(this->remote6 ? "[" : "",
		this->remote), this->remote6 ? "]" : "");

	this->family = "udp";
	this->event = "SEND";
	this->inet = 1;
}

/*
 * UDP recv(2) [syscall] stacks:
 * sys_read -> dofileread -> soreceive -> soreceive_dgram
 * sys_recvfrom -> kern_recvit -> soreceive -> soreceive_dgram
 * sys_recvmsg -> kern_recvit -> soreceive -> soreceive_dgram
 */

syscall::read:entry,
syscall::recvfrom:entry,
syscall::recvmsg:entry
	/* probe ID $(( $ID + 4 )) */
{${TRACE:+
	printf("<$(( $ID + 4 ))>");
}
	this->inet = 0;
}

fbt:kernel:soreceive_dgram:entry /* probe ID $(( $ID + 5 )) */
{${TRACE:+
	printf("<$(( $ID + 5 ))>");
}
	this->udps = (struct socket *)args[0];
	this->af   = this->udps->so_proto->pr_domain->dom_family;
	this->psa  = (struct sockaddr **)args[1];
	this->sa   = (struct sockaddr *)NULL;

	this->so_pcb = this->udps->so_pcb;
	this->udpsinfo = xlate <udpsinfo_t> ((struct inpcb *)this->so_pcb);

	/* UDP receive local */
	this->local = this->udpsinfo.udps_laddr;
	this->local6 = this->af == AF_INET6;
	this->local = strjoin(strjoin(this->local6 ? "[" : "",
		this->local), this->local6 ? "]" : "");
	this->lport = this->udpsinfo.udps_lport;

	this->family = "udp";
	this->event = "RCVD";
	this->inet = (
		this->af == AF_INET ? 1 :
		this->af == AF_INET6 ? 1 :
		0
	);
}

fbt:kernel:soreceive_dgram:return /this->inet/ /* probe ID $(( $ID + 6 )) */
{${TRACE:+
	printf("<$(( $ID + 6 ))>");
}
	this->sa = this->psa == NULL ? NULL : *this->psa;
	this->sainfo = xlate <sainfo_t> ((struct sockaddr *)this->sa);

	/* UDP receive remote */
	this->remote = this->sainfo.addr;
	this->remote6 = this->sainfo.sa_family == AF_INET6;
	this->remote = strjoin(strjoin(this->remote6 ? "[" : "",
		this->remote), this->remote6 ? "]" : "");
	this->rport = this->sainfo.port;
}

syscall::read:return,
syscall::recvfrom:return,
syscall::recvmsg:return
	/this->inet/ /* probe ID $(( $ID + 7 )) */
{${TRACE:+
	printf("<$(( $ID + 7 ))>");
}
	this->length = (uint32_t)((int)arg0 < 0 ? 0 : arg0);
	this->inet = (this->length > 0);
}
EOF
ACTIONS=$( cat <&9 )
ID=$(( $ID + 8 ))

############################################################ EVENT DETAILS

exec 9<<EOF
	/*
	 * Print event details
	 */
	printf("{\"report_type\":\"${PROFILE%-raw}\",\"epoch\":%u,\"family\":\"%s\",\"local\":\"%s\",\"lport\":%u,\"event\":\"%s\",\"remote\":\"%s\",\"rport\":%u,\"length\":%u}",
		walltimestamp / 1000000000,
		this->family,
		this->local,
		this->lport,
		this->event,
		this->remote,
		this->rport,
		this->length);

	this->event = "";
	this->inet = 0;
EOF
EVENT_DETAILS=$( cat <&9 )

################################################################################
# END
################################################################################
