#include <sys/param.h>
#include <sys/protosw.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <netinet/ip.h>
#if INET6
#include <netinet/ip6.h>
#endif
#if !TCPDEBUG
#define TCPSTATES
#endif
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_log.h>
SYSCTL_NODE(_net_inet_tcp, OID_AUTO, log, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
"TCP logs");
static int tcp_log_level_info = 0;
SYSCTL_INT(_net_inet_tcp_log, OID_AUTO, level_info,
CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_log_level_info, 0, "");
#if (DEVELOPMENT || DEBUG)
#if defined(XNU_TARGET_OS_OSX)
#define TCP_LOG_ENABLE_DEFAULT \
(TLEF_CONNECTION | TLEF_DST_LOCAL | TLEF_DST_GW | \
TLEF_DROP_NECP)
#else
#define TCP_LOG_ENABLE_DEFAULT \
(TLEF_CONNECTION | TLEF_DST_LOCAL | TLEF_DST_GW | \
TLEF_DROP_NECP | TLEF_DROP_PCB | TLEF_DROP_PKT | TLEF_THF_SYN)
#endif
#else
#define TCP_LOG_ENABLE_DEFAULT 0
#endif
uint32_t tcp_log_enable_flags = TCP_LOG_ENABLE_DEFAULT;
SYSCTL_UINT(_net_inet_tcp_log, OID_AUTO, enable,
CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_log_enable_flags, 0, "");
#define X(name, value, description, ...) #description ":" #value " "
SYSCTL_STRING(_net_inet_tcp_log, OID_AUTO, enable_usage, CTLFLAG_RD | CTLFLAG_LOCKED,
TCP_ENABLE_FLAG_LIST, 0, "");
#undef X
uint32_t tcp_log_port = 0;
SYSCTL_UINT(_net_inet_tcp_log, OID_AUTO, rtt_port, CTLFLAG_RW | CTLFLAG_LOCKED,
&tcp_log_port, 0, "");
#if (DEVELOPMENT || DEBUG)
#define TCP_LOG_THFLAGS_IF_FAMILY_DEFAULT IFNET_FAMILY_IPSEC
#else
#define TCP_LOG_THFLAGS_IF_FAMILY_DEFAULT 0
#endif
static uint32_t tcp_log_thflags_if_family = TCP_LOG_THFLAGS_IF_FAMILY_DEFAULT;
SYSCTL_UINT(_net_inet_tcp_log, OID_AUTO, thflags_if_family,
CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_log_thflags_if_family, 0, "");
#if (DEVELOPMENT || DEBUG)
#define TCP_LOG_PRIVACY_DEFAULT 0
#else
#define TCP_LOG_PRIVACY_DEFAULT 1
#endif
int tcp_log_privacy = TCP_LOG_PRIVACY_DEFAULT;
SYSCTL_INT(_net_inet_tcp_log, OID_AUTO, privacy,
CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_log_privacy, 0, "");
#define TCP_LOG_RATE_LIMIT 600
static unsigned int tcp_log_rate_limit = TCP_LOG_RATE_LIMIT;
SYSCTL_UINT(_net_inet_tcp_log, OID_AUTO, rate_limit,
CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_log_rate_limit, 0, "");
#define TCP_LOG_RATE_DURATION 60
static unsigned int tcp_log_rate_duration = TCP_LOG_RATE_DURATION;
SYSCTL_UINT(_net_inet_tcp_log, OID_AUTO, rate_duration,
CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_log_rate_duration, 0, "");
static unsigned long tcp_log_rate_max = 0;
SYSCTL_ULONG(_net_inet_tcp_log, OID_AUTO, rate_max,
CTLFLAG_RD | CTLFLAG_LOCKED, &tcp_log_rate_max, "");
static unsigned long tcp_log_rate_exceeded_total = 0;
SYSCTL_ULONG(_net_inet_tcp_log, OID_AUTO, rate_exceeded_total,
CTLFLAG_RD | CTLFLAG_LOCKED, &tcp_log_rate_exceeded_total, "");
static unsigned long tcp_log_rate_current = 0;
SYSCTL_ULONG(_net_inet_tcp_log, OID_AUTO, rate_current,
CTLFLAG_RD | CTLFLAG_LOCKED, &tcp_log_rate_current, "");
static bool tcp_log_rate_exceeded_logged = false;
static uint64_t tcp_log_current_period = 0;
#define ADDRESS_STR_LEN (MAX_IPv6_STR_LEN + 6)
#define TCP_LOG_COMMON_FMT \
"[%s:%u<->%s:%u] " \
"interface: %s " \
"(skipped: %lu)\n"
#define TCP_LOG_COMMON_ARGS \
laddr_buf, ntohs(local_port), faddr_buf, ntohs(foreign_port), \
ifp != NULL ? if_name(ifp) : "", \
tcp_log_rate_exceeded_total
#define TCP_LOG_COMMON_PCB_FMT \
TCP_LOG_COMMON_FMT \
"t_state: %s " \
"process: %s:%u "
#define TCP_LOG_COMMON_PCB_ARGS \
TCP_LOG_COMMON_ARGS, \
tcpstates[tp->t_state], \
inp->inp_last_proc_name, so->last_pid
static bool
tcp_log_is_rate_limited(void)
{
uint64_t current_net_period = net_uptime();
if (tcp_log_rate_duration == 0) {
tcp_log_rate_duration = TCP_LOG_RATE_DURATION;
}
if (tcp_log_rate_limit == 0) {
tcp_log_rate_duration = TCP_LOG_RATE_LIMIT;
}
if (current_net_period > tcp_log_current_period + tcp_log_rate_duration) {
if (tcp_log_rate_current > tcp_log_rate_max) {
tcp_log_rate_max = tcp_log_rate_current;
}
tcp_log_current_period = current_net_period;
tcp_log_rate_current = 0;
tcp_log_rate_exceeded_logged = false;
}
tcp_log_rate_current += 1;
if (tcp_log_rate_current > (unsigned long) tcp_log_rate_limit) {
tcp_log_rate_exceeded_total += 1;
return true;
}
return false;
}
static void
tcp_log_inp_addresses(struct inpcb *inp, char *lbuf, size_t lbuflen, char *fbuf, size_t fbuflen)
{
if (tcp_log_privacy != 0) {
if (inp->inp_vflag & INP_IPV6) {
strlcpy(lbuf, "<IPv6-redacted>", lbuflen);
strlcpy(fbuf, "<IPv6-redacted>", fbuflen);
} else {
strlcpy(lbuf, "<IPv4-redacted>", lbuflen);
strlcpy(fbuf, "<IPv4-redacted>", fbuflen);
}
} else if (inp->inp_vflag & INP_IPV6) {
inet_ntop(AF_INET6, (void *)&inp->in6p_laddr, lbuf, lbuflen);
inet_ntop(AF_INET6, (void *)&inp->in6p_faddr, fbuf, fbuflen);
} else {
inet_ntop(AF_INET, (void *)&inp->inp_laddr.s_addr, lbuf, lbuflen);
inet_ntop(AF_INET, (void *)&inp->inp_faddr.s_addr, fbuf, fbuflen);
}
}
void
tcp_log_rtt_info(const char *func_name, int line_no, struct tcpcb *tp)
{
struct inpcb *inp = tp->t_inpcb;
struct socket *so = inp->inp_socket;
struct ifnet *ifp;
char laddr_buf[ADDRESS_STR_LEN];
char faddr_buf[ADDRESS_STR_LEN];
in_port_t local_port = inp->inp_lport;
in_port_t foreign_port = inp->inp_fport;
if (tcp_log_is_rate_limited()) {
return;
}
ifp = inp->inp_last_outifp != NULL ? inp->inp_last_outifp :
inp->inp_boundifp != NULL ? inp->inp_boundifp : NULL;
tcp_log_inp_addresses(inp, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf));
os_log(OS_LOG_DEFAULT,
"tcp_rtt_info (%s:%d) "
TCP_LOG_COMMON_PCB_FMT
"rttcur: %u ms srtt: %u ms rttvar: %u ms rttmin: %u ms rxtcur: %u rxtshift: %u",
func_name, line_no,
TCP_LOG_COMMON_PCB_ARGS,
tp->t_rttcur, tp->t_srtt >> TCP_RTT_SHIFT,
tp->t_rttvar >> TCP_RTTVAR_SHIFT,
tp->t_rttmin, tp->t_rxtcur, tp->t_rxtshift);
}
void
tcp_log_rt_rtt(const char *func_name, int line_no, struct tcpcb *tp,
struct rtentry *rt)
{
struct inpcb *inp = tp->t_inpcb;
struct socket *so = inp->inp_socket;
struct ifnet *ifp;
char laddr_buf[ADDRESS_STR_LEN];
char faddr_buf[ADDRESS_STR_LEN];
in_port_t local_port = inp->inp_lport;
in_port_t foreign_port = inp->inp_fport;
if (tcp_log_is_rate_limited()) {
return;
}
ifp = inp->inp_last_outifp != NULL ? inp->inp_last_outifp :
inp->inp_boundifp != NULL ? inp->inp_boundifp : NULL;
tcp_log_inp_addresses(inp, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf));
os_log(OS_LOG_DEFAULT,
"tcp_rt_rtt (%s:%d) "
TCP_LOG_COMMON_PCB_FMT
"rt_rmx: RTV_RTT: %d ms rtt: %u ms rttvar: %u ms",
func_name, line_no,
TCP_LOG_COMMON_PCB_ARGS,
(rt->rt_rmx.rmx_locks & RTV_RTT),
rt->rt_rmx.rmx_rtt / (RTM_RTTUNIT / TCP_RETRANSHZ),
rt->rt_rmx.rmx_rttvar / (RTM_RTTUNIT / TCP_RETRANSHZ));
}
void
tcp_log_rtt_change(const char *func_name, int line_no, struct tcpcb *tp,
int old_srtt, int old_rttvar)
{
int srtt_diff;
int rttvar_diff;
srtt_diff = ABS(tp->t_srtt - old_srtt) >> TCP_RTT_SHIFT;
rttvar_diff =
ABS((tp->t_rttvar - old_rttvar) >> TCP_RTTVAR_SHIFT);
if (srtt_diff >= 1000 || rttvar_diff >= 500) {
struct inpcb *inp = tp->t_inpcb;
struct socket *so = inp->inp_socket;
struct ifnet *ifp;
char laddr_buf[ADDRESS_STR_LEN];
char faddr_buf[ADDRESS_STR_LEN];
in_port_t local_port = inp->inp_lport;
in_port_t foreign_port = inp->inp_fport;
if (tcp_log_is_rate_limited()) {
return;
}
ifp = inp->inp_last_outifp != NULL ? inp->inp_last_outifp :
inp->inp_boundifp != NULL ? inp->inp_boundifp : NULL;
tcp_log_inp_addresses(inp, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf));
os_log(OS_LOG_DEFAULT,
"tcp_rtt_change (%s:%d) "
TCP_LOG_COMMON_PCB_FMT
"srtt: %u ms old_rtt: %u ms "
"rttvar: %u old_rttvar: %u ms ",
func_name, line_no,
TCP_LOG_COMMON_PCB_ARGS,
tp->t_srtt >> TCP_RTT_SHIFT,
old_srtt >> TCP_RTT_SHIFT,
tp->t_rttvar >> TCP_RTTVAR_SHIFT,
old_rttvar >> TCP_RTTVAR_SHIFT);
}
}
void
tcp_log_keepalive(const char *func_name, int line_no, struct tcpcb *tp,
int32_t idle_time)
{
struct inpcb *inp = tp->t_inpcb;
struct socket *so = inp->inp_socket;
struct ifnet *ifp;
char laddr_buf[ADDRESS_STR_LEN];
char faddr_buf[ADDRESS_STR_LEN];
in_port_t local_port = inp->inp_lport;
in_port_t foreign_port = inp->inp_fport;
if (tcp_log_is_rate_limited()) {
return;
}
ifp = inp->inp_last_outifp != NULL ? inp->inp_last_outifp :
inp->inp_boundifp != NULL ? inp->inp_boundifp : NULL;
tcp_log_inp_addresses(inp, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf));
os_log(OS_LOG_DEFAULT,
"tcp_keepalive (%s:%d) "
TCP_LOG_COMMON_PCB_FMT
"snd_una: %u snd_max: %u "
"SO_KA: %d RSTALL: %d TFOPRB: %d idle_time: %u "
"KIDLE: %d KINTV: %d KCNT: %d",
func_name, line_no,
TCP_LOG_COMMON_PCB_ARGS,
tp->snd_una, tp->snd_max,
tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE,
tp->t_flagsext & TF_DETECT_READSTALL,
tp->t_tfo_probe_state == TFO_PROBE_PROBING,
idle_time,
TCP_CONN_KEEPIDLE(tp), TCP_CONN_KEEPINTVL(tp),
TCP_CONN_KEEPCNT(tp));
}
void
tcp_log_connection(struct tcpcb *tp, const char *event, int error)
{
struct inpcb *inp;
struct socket *so;
struct ifnet *ifp;
char laddr_buf[ADDRESS_STR_LEN];
char faddr_buf[ADDRESS_STR_LEN];
in_port_t local_port;
in_port_t foreign_port;
if (tp == NULL || tp->t_inpcb == NULL || tp->t_inpcb->inp_socket == NULL || event == NULL) {
return;
}
if (tcp_log_is_rate_limited()) {
return;
}
inp = tp->t_inpcb;
so = inp->inp_socket;
local_port = inp->inp_lport;
foreign_port = inp->inp_fport;
ifp = inp->inp_last_outifp != NULL ? inp->inp_last_outifp :
inp->inp_boundifp != NULL ? inp->inp_boundifp : NULL;
tcp_log_inp_addresses(inp, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf));
#define TCP_LOG_CONNECT_FMT \
"tcp %s: " \
TCP_LOG_COMMON_PCB_FMT \
"rtt: %u.%u ms " \
"rttvar: %u.%u ms " \
"error: %d " \
"so_error: %d " \
"svc/tc: %u"
#define TCP_LOG_CONNECT_ARGS \
event, \
TCP_LOG_COMMON_PCB_ARGS, \
tp->t_srtt >> TCP_RTT_SHIFT, tp->t_srtt - ((tp->t_srtt >> TCP_RTT_SHIFT) << TCP_RTT_SHIFT), \
tp->t_rttvar >> TCP_RTTVAR_SHIFT, tp->t_rttvar - ((tp->t_rttvar >> TCP_RTTVAR_SHIFT) << TCP_RTTVAR_SHIFT), \
error, \
so->so_error, \
(so->so_flags1 & SOF1_TC_NET_SERV_TYPE) ? so->so_netsvctype : so->so_traffic_class
if (so->so_head == NULL) {
if (tcp_log_level_info == 0) {
os_log(OS_LOG_DEFAULT, TCP_LOG_CONNECT_FMT,
TCP_LOG_CONNECT_ARGS);
} else {
os_log_info(OS_LOG_DEFAULT, TCP_LOG_CONNECT_FMT,
TCP_LOG_CONNECT_ARGS);
}
} else {
#define TCP_LOG_CONN_Q_FMT \
"so_qlimit: %d "\
"so_qlen: %d "\
"so_incqlen: %d "
#define TCP_LOG_CONN_Q_ARGS \
so->so_head->so_qlimit, \
so->so_head->so_qlen, \
so->so_head->so_incqlen
if (tcp_log_level_info == 0) {
os_log(OS_LOG_DEFAULT, TCP_LOG_CONNECT_FMT "\n" TCP_LOG_CONN_Q_FMT,
TCP_LOG_CONNECT_ARGS, TCP_LOG_CONN_Q_ARGS);
} else {
os_log_info(OS_LOG_DEFAULT, TCP_LOG_CONNECT_FMT "\n" TCP_LOG_CONN_Q_FMT,
TCP_LOG_CONNECT_ARGS, TCP_LOG_CONN_Q_ARGS);
}
#undef TCP_LOG_CONN_Q_FMT
#undef TCP_LOG_CONN_Q_ARGS
}
#undef TCP_LOG_CONNECT_FMT
#undef TCP_LOG_CONNECT_ARGS
}
void
tcp_log_listen(struct tcpcb *tp, int error)
{
struct inpcb *inp = tp->t_inpcb;
struct socket *so = inp->inp_socket;
struct ifnet *ifp;
char laddr_buf[ADDRESS_STR_LEN];
char faddr_buf[ADDRESS_STR_LEN];
in_port_t local_port;
in_port_t foreign_port;
if (tp == NULL || tp->t_inpcb == NULL || tp->t_inpcb->inp_socket == NULL) {
return;
}
if (tcp_log_is_rate_limited()) {
return;
}
inp = tp->t_inpcb;
so = inp->inp_socket;
local_port = inp->inp_lport;
foreign_port = inp->inp_fport;
ifp = inp->inp_last_outifp != NULL ? inp->inp_last_outifp :
inp->inp_boundifp != NULL ? inp->inp_boundifp : NULL;
tcp_log_inp_addresses(inp, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf));
#define TCP_LOG_LISTEN_FMT \
"tcp listen: " \
TCP_LOG_COMMON_PCB_FMT \
"so_qlimit: %d "\
"error: %d " \
"so_error: %d " \
"svc/tc: %u"
#define TCP_LOG_LISTEN_ARGS \
TCP_LOG_COMMON_PCB_ARGS, \
so->so_qlimit, \
error, \
so->so_error, \
(so->so_flags1 & SOF1_TC_NET_SERV_TYPE) ? so->so_netsvctype : so->so_traffic_class
if (tcp_log_level_info == 0) {
os_log(OS_LOG_DEFAULT, TCP_LOG_LISTEN_FMT,
TCP_LOG_LISTEN_ARGS);
} else {
os_log_info(OS_LOG_DEFAULT, TCP_LOG_LISTEN_FMT,
TCP_LOG_LISTEN_ARGS);
}
#undef TCP_LOG_LISTEN_FMT
#undef TCP_LOG_LISTEN_ARGS
}
void
tcp_log_connection_summary(struct tcpcb *tp)
{
struct inpcb *inp;
struct socket *so;
struct ifnet *ifp;
uint32_t conntime = 0;
uint32_t duration = 0;
char laddr_buf[ADDRESS_STR_LEN];
char faddr_buf[ADDRESS_STR_LEN];
in_port_t local_port;
in_port_t foreign_port;
if (tp == NULL || tp->t_inpcb == NULL || tp->t_inpcb->inp_socket == NULL) {
return;
}
if (tcp_log_is_rate_limited()) {
return;
}
inp = tp->t_inpcb;
so = inp->inp_socket;
local_port = inp->inp_lport;
foreign_port = inp->inp_fport;
if (tp->t_flagsext & TF_LOGGED_CONN_SUMMARY) {
return;
}
tp->t_flagsext |= TF_LOGGED_CONN_SUMMARY;
if (tp->t_connect_time > 0) {
duration = tcp_now - tp->t_connect_time;
if (tp->t_starttime > 0) {
conntime = tp->t_starttime - tp->t_connect_time;
}
}
ifp = inp->inp_last_outifp != NULL ? inp->inp_last_outifp :
inp->inp_boundifp != NULL ? inp->inp_boundifp : NULL;
tcp_log_inp_addresses(inp, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf));
#define TCP_LOG_CONNECTION_SUMMARY_FMT \
"tcp_connection_summary " \
TCP_LOG_COMMON_PCB_FMT \
"Duration: %u.%u sec " \
"Conn_Time: %u.%u sec " \
"syn rxmit: %u\n" \
"bytes in/out: %llu/%llu " \
"pkts in/out: %llu/%llu " \
"rtt: %u.%u ms " \
"rttvar: %u.%u ms " \
"pkt rxmit: %u " \
"ooo pkts: %u dup bytes in: %u " \
"so_error: %d " \
"svc/tc: %u"
#define TCP_LOG_CONNECTION_SUMMARY_ARGS \
TCP_LOG_COMMON_PCB_ARGS, \
duration / TCP_RETRANSHZ, duration % TCP_RETRANSHZ, \
conntime / TCP_RETRANSHZ, conntime % TCP_RETRANSHZ, \
tp->t_stat.synrxtshift, \
inp->inp_stat->rxbytes, inp->inp_stat->txbytes, \
inp->inp_stat->rxpackets, inp->inp_stat->txpackets, \
tp->t_srtt >> TCP_RTT_SHIFT, tp->t_srtt - ((tp->t_srtt >> TCP_RTT_SHIFT) << TCP_RTT_SHIFT), \
tp->t_rttvar >> TCP_RTTVAR_SHIFT, tp->t_rttvar - ((tp->t_rttvar >> TCP_RTTVAR_SHIFT) << TCP_RTTVAR_SHIFT), \
tp->t_stat.rxmitpkts, \
tp->t_rcvoopack, tp->t_stat.rxduplicatebytes, \
so->so_error, \
(so->so_flags1 & SOF1_TC_NET_SERV_TYPE) ? so->so_netsvctype : so->so_traffic_class
if (tcp_log_level_info == 0) {
os_log(OS_LOG_DEFAULT, TCP_LOG_CONNECTION_SUMMARY_FMT,
TCP_LOG_CONNECTION_SUMMARY_ARGS);
} else {
os_log_info(OS_LOG_DEFAULT, TCP_LOG_CONNECTION_SUMMARY_FMT,
TCP_LOG_CONNECTION_SUMMARY_ARGS);
}
#undef TCP_LOG_CONNECTION_SUMMARY_FMT
#undef TCP_LOG_CONNECTION_SUMMARY_ARGS
}
static bool
tcp_log_pkt_addresses(void *hdr, struct tcphdr *th, bool outgoing,
char *lbuf, size_t lbuflen, char *fbuf, size_t fbuflen)
{
bool isipv6;
uint8_t thflags;
isipv6 = (((struct ip *)hdr)->ip_v == 6);
thflags = th->th_flags;
if (isipv6) {
struct ip6_hdr *ip6 = (struct ip6_hdr *)hdr;
if (memcmp(&ip6->ip6_src, &in6addr_loopback, sizeof(struct in6_addr)) == 0 ||
memcmp(&ip6->ip6_dst, &in6addr_loopback, sizeof(struct in6_addr)) == 0) {
if (!(tcp_log_enable_flags & TLEF_DST_LOOPBACK)) {
return false;
}
}
if (tcp_log_privacy != 0) {
strlcpy(lbuf, "<IPv6-redacted>", lbuflen);
strlcpy(fbuf, "<IPv6-redacted>", fbuflen);
} else if (outgoing) {
inet_ntop(AF_INET6, &ip6->ip6_src, lbuf, lbuflen);
inet_ntop(AF_INET6, &ip6->ip6_dst, fbuf, fbuflen);
} else {
inet_ntop(AF_INET6, &ip6->ip6_dst, lbuf, lbuflen);
inet_ntop(AF_INET6, &ip6->ip6_src, fbuf, fbuflen);
}
} else {
struct ip *ip = (struct ip *)hdr;
if (ntohl(ip->ip_src.s_addr) == INADDR_LOOPBACK ||
ntohl(ip->ip_dst.s_addr) == INADDR_LOOPBACK) {
if (!(tcp_log_enable_flags & TLEF_DST_LOOPBACK)) {
return false;
}
}
if (tcp_log_privacy != 0) {
strlcpy(lbuf, "<IPv4-redacted>", lbuflen);
strlcpy(fbuf, "<IPv4-redacted>", fbuflen);
} else if (outgoing) {
inet_ntop(AF_INET, (void *)&ip->ip_src.s_addr, lbuf, lbuflen);
inet_ntop(AF_INET, (void *)&ip->ip_dst.s_addr, fbuf, fbuflen);
} else {
inet_ntop(AF_INET, (void *)&ip->ip_dst.s_addr, lbuf, lbuflen);
inet_ntop(AF_INET, (void *)&ip->ip_src.s_addr, fbuf, fbuflen);
}
}
return true;
}
void
tcp_log_drop_pcb(void *hdr, struct tcphdr *th, struct tcpcb *tp, bool outgoing, const char *reason)
{
struct inpcb *inp = tp->t_inpcb;
struct socket *so = inp->inp_socket;
struct ifnet *ifp;
char laddr_buf[ADDRESS_STR_LEN];
char faddr_buf[ADDRESS_STR_LEN];
in_port_t local_port;
in_port_t foreign_port;
if (tp == NULL) {
return;
}
if (tcp_log_is_rate_limited()) {
return;
}
if (hdr != NULL && th != NULL) {
if (outgoing) {
local_port = th->th_sport;
foreign_port = th->th_dport;
} else {
local_port = th->th_dport;
foreign_port = th->th_sport;
}
(void) tcp_log_pkt_addresses(hdr, th, outgoing, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf));
} else {
local_port = inp->inp_lport;
foreign_port = inp->inp_fport;
tcp_log_inp_addresses(inp, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf));
}
ifp = inp->inp_last_outifp != NULL ? inp->inp_last_outifp :
inp->inp_boundifp != NULL ? inp->inp_boundifp : NULL;
#define TCP_LOG_DROP_PCB_FMT \
"tcp drop %s " \
TCP_LOG_COMMON_PCB_FMT \
"t_state: %s " \
"so_error: %d " \
"reason: %s"
#define TCP_LOG_DROP_PCB_ARGS \
outgoing ? "outgoing" : "incoming", \
TCP_LOG_COMMON_PCB_ARGS, \
tcpstates[tp->t_state], \
so->so_error, \
reason
if (tcp_log_level_info == 0) {
os_log(OS_LOG_DEFAULT, TCP_LOG_DROP_PCB_FMT,
TCP_LOG_DROP_PCB_ARGS);
} else {
os_log_info(OS_LOG_DEFAULT, TCP_LOG_DROP_PCB_FMT,
TCP_LOG_DROP_PCB_ARGS);
}
#undef TCP_LOG_DROP_PCB_FMT
#undef TCP_LOG_DROP_PCB_ARGS
}
#define TCP_LOG_TH_FLAGS_COMMON_FMT \
"tcp control %s " \
"%s" \
"%s" \
"%s" \
"%s" \
TCP_LOG_COMMON_FMT
#define TCP_LOG_TH_FLAGS_COMMON_ARGS \
outgoing ? "outgoing" : "incoming", \
thflags & TH_SYN ? "SYN " : "", \
thflags & TH_FIN ? "FIN " : "", \
thflags & TH_RST ? "RST " : "", \
thflags & TH_ACK ? "ACK " : "", \
TCP_LOG_COMMON_ARGS
void
tcp_log_th_flags(void *hdr, struct tcphdr *th, struct tcpcb *tp, bool outgoing, struct ifnet *ifp)
{
struct socket *so = tp->t_inpcb != NULL ? tp->t_inpcb->inp_socket : NULL;
char laddr_buf[ADDRESS_STR_LEN];
char faddr_buf[ADDRESS_STR_LEN];
in_port_t local_port;
in_port_t foreign_port;
uint8_t thflags;
if (hdr == NULL || th == NULL) {
return;
}
if (outgoing) {
local_port = th->th_sport;
foreign_port = th->th_dport;
} else {
local_port = th->th_dport;
foreign_port = th->th_sport;
}
thflags = th->th_flags;
if ((((thflags & TH_SYN) && (tcp_log_enable_flags & TLEF_THF_SYN)) ||
((thflags & TH_FIN) && (tcp_log_enable_flags & TLEF_THF_FIN)) ||
((thflags & TH_RST) && (tcp_log_enable_flags & TLEF_THF_RST))) == false) {
return;
}
if (ifp != NULL && tcp_log_thflags_if_family != 0 && ifp->if_family != tcp_log_thflags_if_family) {
return;
}
if (!tcp_log_pkt_addresses(hdr, th, outgoing, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf))) {
return;
}
if (tcp_log_is_rate_limited()) {
return;
}
if (tp == NULL || so == NULL) {
if (tcp_log_level_info == 0) {
os_log(OS_LOG_DEFAULT, TCP_LOG_TH_FLAGS_COMMON_FMT " no pcb",
TCP_LOG_TH_FLAGS_COMMON_ARGS);
} else {
os_log_info(OS_LOG_DEFAULT, TCP_LOG_TH_FLAGS_COMMON_FMT,
TCP_LOG_TH_FLAGS_COMMON_ARGS);
}
} else {
#define TCP_LOG_TH_FLAGS_PCB_FMT \
TCP_LOG_TH_FLAGS_COMMON_FMT \
"rtt: %u.%u ms " \
"rttvar: %u.%u ms " \
"syn rxmit: %u " \
"pkt rxmit: %u " \
"so_error: %d " \
"svc/tc: %u "
#define TCP_LOG_TH_FLAGS_PCB_ARGS \
TCP_LOG_TH_FLAGS_COMMON_ARGS, \
tp->t_srtt >> TCP_RTT_SHIFT, tp->t_srtt - ((tp->t_srtt >> TCP_RTT_SHIFT) << TCP_RTT_SHIFT), \
tp->t_rttvar >> TCP_RTTVAR_SHIFT, tp->t_rttvar - ((tp->t_rttvar >> TCP_RTTVAR_SHIFT) << TCP_RTTVAR_SHIFT), \
tp->t_stat.synrxtshift, \
tp->t_stat.rxmitpkts, \
so->so_error, \
(so->so_flags1 & SOF1_TC_NET_SERV_TYPE) ? \
so->so_netsvctype : so->so_traffic_class
if (tcp_log_level_info == 0) {
os_log(OS_LOG_DEFAULT, TCP_LOG_TH_FLAGS_PCB_FMT,
TCP_LOG_TH_FLAGS_PCB_ARGS);
} else {
os_log_info(OS_LOG_DEFAULT, TCP_LOG_TH_FLAGS_PCB_FMT,
TCP_LOG_TH_FLAGS_PCB_ARGS);
}
#undef TCP_LOG_TH_FLAGS_PCB_FMT
#undef TCP_LOG_TH_FLAGS_PCB_ARGS
}
}
void
tcp_log_drop_pkt(void *hdr, struct tcphdr *th, struct ifnet *ifp, const char *reason)
{
char laddr_buf[ADDRESS_STR_LEN];
char faddr_buf[ADDRESS_STR_LEN];
in_port_t local_port;
in_port_t foreign_port;
uint8_t thflags;
bool outgoing = false;
if (hdr == NULL || th == NULL) {
return;
}
local_port = th->th_dport;
foreign_port = th->th_sport;
thflags = th->th_flags;
if ((((thflags & TH_SYN) && (tcp_log_enable_flags & TLEF_THF_SYN)) ||
((thflags & TH_FIN) && (tcp_log_enable_flags & TLEF_THF_FIN)) ||
((thflags & TH_RST) && (tcp_log_enable_flags & TLEF_THF_RST))) == false) {
return;
}
if (ifp != NULL && tcp_log_thflags_if_family != 0 && ifp->if_family != tcp_log_thflags_if_family) {
return;
}
if (!tcp_log_pkt_addresses(hdr, th, outgoing, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf))) {
return;
}
if (tcp_log_is_rate_limited()) {
return;
}
#define TCP_LOG_DROP_PKT_FMT \
"tcp drop incoming control packet " \
TCP_LOG_TH_FLAGS_COMMON_FMT \
"reason: %s"
#define TCP_LOG_DROP_PKT_ARGS \
TCP_LOG_TH_FLAGS_COMMON_ARGS, \
reason != NULL ? reason : ""
if (tcp_log_level_info == 0) {
os_log(OS_LOG_DEFAULT, TCP_LOG_DROP_PKT_FMT,
TCP_LOG_DROP_PKT_ARGS);
} else {
os_log_info(OS_LOG_DEFAULT, TCP_LOG_DROP_PKT_FMT,
TCP_LOG_DROP_PKT_ARGS);
}
#undef TCP_LOG_DROP_PKT_FMT
#undef TCP_LOG_DROP_PKT_ARGS
}
void
tcp_log_message(const char *func_name, int line_no, struct tcpcb *tp, const char *format, ...)
{
struct inpcb *inp;
struct socket *so;
struct ifnet *ifp;
char laddr_buf[ADDRESS_STR_LEN];
char faddr_buf[ADDRESS_STR_LEN];
in_port_t local_port;
in_port_t foreign_port;
char message[256];
if (tp == NULL || tp->t_inpcb == NULL || tp->t_inpcb->inp_socket == NULL) {
return;
}
if (tcp_log_is_rate_limited()) {
return;
}
inp = tp->t_inpcb;
so = inp->inp_socket;
local_port = inp->inp_lport;
foreign_port = inp->inp_fport;
ifp = inp->inp_last_outifp != NULL ? inp->inp_last_outifp :
inp->inp_boundifp != NULL ? inp->inp_boundifp : NULL;
tcp_log_inp_addresses(inp, laddr_buf, sizeof(laddr_buf), faddr_buf, sizeof(faddr_buf));
va_list ap;
va_start(ap, format);
vsnprintf(message, sizeof(message), format, ap);
va_end(ap);
#define TCP_LOG_MESSAGE_FMT \
"tcp (%s:%d) " \
TCP_LOG_COMMON_PCB_FMT \
"%s"
#define TCP_LOG_MESSAGE_ARGS \
func_name, line_no, \
TCP_LOG_COMMON_PCB_ARGS, \
message
if (tcp_log_level_info == 0) {
os_log(OS_LOG_DEFAULT, TCP_LOG_MESSAGE_FMT,
TCP_LOG_MESSAGE_ARGS);
} else {
os_log_info(OS_LOG_DEFAULT, TCP_LOG_MESSAGE_FMT,
TCP_LOG_MESSAGE_ARGS);
}
#undef TCP_LOG_MESSAGE_FMT
#undef TCP_LOG_MESSAGE_ARGS
}