| /* | 
 |  * Copyright (c) 2016 Fabien Siron <fabien.siron@epita.fr> | 
 |  * Copyright (c) 2017 JingPiao Chen <chenjingpiao@gmail.com> | 
 |  * Copyright (c) 2017-2018 The strace developers. | 
 |  * All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in the | 
 |  *    documentation and/or other materials provided with the distribution. | 
 |  * 3. The name of the author may not be used to endorse or promote products | 
 |  *    derived from this software without specific prior written permission. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 
 |  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
 |  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
 |  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
 |  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
 |  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
 |  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
 |  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
 |  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include "defs.h" | 
 | #include "netlink.h" | 
 | #include "netlink_sock_diag.h" | 
 | #include "nlattr.h" | 
 | #include "print_fields.h" | 
 |  | 
 | #include <arpa/inet.h> | 
 |  | 
 | #include <linux/sock_diag.h> | 
 | #include <linux/inet_diag.h> | 
 |  | 
 | #include "xlat/inet_diag_attrs.h" | 
 | #include "xlat/inet_diag_bytecodes.h" | 
 | #include "xlat/inet_diag_extended_flags.h" | 
 | #include "xlat/inet_diag_req_attrs.h" | 
 |  | 
 | #include "xlat/tcp_states.h" | 
 | #include "xlat/tcp_state_flags.h" | 
 |  | 
 | void | 
 | print_inet_diag_sockid(const struct inet_diag_sockid *id, const uint8_t family) | 
 | { | 
 | 	PRINT_FIELD_NET_PORT("{", *id, idiag_sport); | 
 | 	PRINT_FIELD_NET_PORT(", ", *id, idiag_dport); | 
 | 	PRINT_FIELD_INET_ADDR(", ", *id, idiag_src, family); | 
 | 	PRINT_FIELD_INET_ADDR(", ", *id, idiag_dst, family); | 
 | 	PRINT_FIELD_IFINDEX(", ", *id, idiag_if); | 
 | 	PRINT_FIELD_COOKIE(", ", *id, idiag_cookie); | 
 | 	tprints("}"); | 
 | } | 
 |  | 
 | static void | 
 | decode_inet_diag_hostcond(struct tcb *const tcp, | 
 | 			  const kernel_ulong_t addr, | 
 | 			  const unsigned int len) | 
 | { | 
 | 	struct inet_diag_hostcond cond; | 
 |  | 
 | 	if (len < sizeof(cond)) { | 
 | 		printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX); | 
 | 		return; | 
 | 	} | 
 | 	if (umove_or_printaddr(tcp, addr, &cond)) | 
 | 		return; | 
 |  | 
 | 	PRINT_FIELD_XVAL("{", cond, family, addrfams, "AF_???"); | 
 | 	PRINT_FIELD_U(", ", cond, prefix_len); | 
 | 	PRINT_FIELD_U(", ", cond, port); | 
 |  | 
 | 	if (len > sizeof(cond)) { | 
 | 		tprints(", "); | 
 | 		decode_inet_addr(tcp, addr + sizeof(cond), | 
 | 				 len - sizeof(cond), cond.family, "addr"); | 
 | 	} | 
 | 	tprints("}"); | 
 | } | 
 |  | 
 | static void | 
 | print_inet_diag_bc_op(const struct inet_diag_bc_op *const op) | 
 | { | 
 | 	PRINT_FIELD_XVAL("{", *op, code, inet_diag_bytecodes, | 
 | 			 "INET_DIAG_BC_???"); | 
 | 	PRINT_FIELD_U(", ", *op, yes); | 
 | 	PRINT_FIELD_U(", ", *op, no); | 
 | 	tprints("}"); | 
 | } | 
 |  | 
 | static void | 
 | decode_inet_diag_markcond(struct tcb *const tcp, | 
 | 			  const kernel_ulong_t addr, | 
 | 			  const unsigned int len) | 
 | { | 
 | 	struct inet_diag_markcond markcond; | 
 |  | 
 | 	if (len < sizeof(markcond)) | 
 | 		printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX); | 
 | 	else if (!umove_or_printaddr(tcp, addr, &markcond)) { | 
 | 		PRINT_FIELD_U("{", markcond, mark); | 
 | 		PRINT_FIELD_U(", ", markcond, mask); | 
 | 		tprints("}"); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | decode_bytecode_data(struct tcb *const tcp, | 
 | 		     const kernel_ulong_t addr, | 
 | 		     const unsigned int len, | 
 | 		     const unsigned char code) | 
 | { | 
 | 	switch (code) { | 
 | 	case INET_DIAG_BC_S_COND: | 
 | 	case INET_DIAG_BC_D_COND: | 
 | 		decode_inet_diag_hostcond(tcp, addr, len); | 
 | 		break; | 
 | 	case INET_DIAG_BC_DEV_COND: { | 
 | 		uint32_t ifindex; | 
 |  | 
 | 		if (len < sizeof(ifindex)) | 
 | 			printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX); | 
 | 		else if (!umove_or_printaddr(tcp, addr, &ifindex)) | 
 | 			print_ifindex(ifindex); | 
 | 		break; | 
 | 	} | 
 | 	case INET_DIAG_BC_S_GE: | 
 | 	case INET_DIAG_BC_S_LE: | 
 | 	case INET_DIAG_BC_D_GE: | 
 | 	case INET_DIAG_BC_D_LE: { | 
 | 		struct inet_diag_bc_op op; | 
 |  | 
 | 		if (len < sizeof(op)) | 
 | 			printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX); | 
 | 		else if (!umove_or_printaddr(tcp, addr, &op)) | 
 | 			print_inet_diag_bc_op(&op); | 
 | 		break; | 
 | 	} | 
 | 	case INET_DIAG_BC_MARK_COND: | 
 | 		decode_inet_diag_markcond(tcp, addr, len); | 
 | 		break; | 
 | 	case INET_DIAG_BC_AUTO: | 
 | 	case INET_DIAG_BC_JMP: | 
 | 	case INET_DIAG_BC_NOP: | 
 | 	default: | 
 | 		printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX); | 
 | 		break; | 
 | 	} | 
 | } | 
 |  | 
 | static bool | 
 | decode_inet_diag_bc_op(struct tcb *const tcp, | 
 | 		       const kernel_ulong_t addr, | 
 | 		       const unsigned int len, | 
 | 		       const void *const opaque_data) | 
 | { | 
 | 	struct inet_diag_bc_op op; | 
 |  | 
 | 	if (len < sizeof(op)) | 
 | 		return false; | 
 | 	if (umove_or_printaddr(tcp, addr, &op)) | 
 | 		return true; | 
 |  | 
 | 	if (len > sizeof(op)) | 
 | 		tprints("{"); | 
 |  | 
 | 	print_inet_diag_bc_op(&op); | 
 |  | 
 | 	if (len > sizeof(op)) { | 
 | 		tprints(", "); | 
 | 		decode_bytecode_data(tcp, addr + sizeof(op), | 
 | 				     len - sizeof(op), op.code); | 
 | 		tprints("}"); | 
 | 	} | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | static const nla_decoder_t inet_diag_req_nla_decoders[] = { | 
 | 	[INET_DIAG_REQ_BYTECODE] = decode_inet_diag_bc_op | 
 | }; | 
 |  | 
 | static void | 
 | decode_inet_diag_req_compat(struct tcb *const tcp, | 
 | 			    const struct nlmsghdr *const nlmsghdr, | 
 | 			    const uint8_t family, | 
 | 			    const kernel_ulong_t addr, | 
 | 			    const unsigned int len) | 
 | { | 
 | 	struct inet_diag_req req = { .idiag_family = family }; | 
 | 	size_t offset = sizeof(req.idiag_family); | 
 | 	bool decode_nla = false; | 
 |  | 
 | 	PRINT_FIELD_XVAL("{", req, idiag_family, addrfams, "AF_???"); | 
 | 	tprints(", "); | 
 | 	if (len >= sizeof(req)) { | 
 | 		if (!umoven_or_printaddr(tcp, addr + offset, | 
 | 					 sizeof(req) - offset, | 
 | 					 (char *) &req + offset)) { | 
 | 			PRINT_FIELD_U("", req, idiag_src_len); | 
 | 			PRINT_FIELD_U(", ", req, idiag_dst_len); | 
 | 			PRINT_FIELD_FLAGS(", ", req, idiag_ext, | 
 | 					  inet_diag_extended_flags, | 
 | 					  "1<<INET_DIAG_\?\?\?-1"); | 
 | 			PRINT_FIELD_INET_DIAG_SOCKID(", ", req, id, | 
 | 						     req.idiag_family); | 
 | 			PRINT_FIELD_FLAGS(", ", req, idiag_states, | 
 | 					  tcp_state_flags, "1<<TCP_???"); | 
 | 			PRINT_FIELD_U(", ", req, idiag_dbs); | 
 | 			decode_nla = true; | 
 | 		} | 
 | 	} else | 
 | 		tprints("..."); | 
 | 	tprints("}"); | 
 |  | 
 | 	offset = NLMSG_ALIGN(sizeof(req)); | 
 | 	if (decode_nla && len > offset) { | 
 | 		tprints(", "); | 
 | 		decode_nlattr(tcp, addr + offset, len - offset, | 
 | 			      inet_diag_req_attrs, "INET_DIAG_REQ_???", | 
 | 			      inet_diag_req_nla_decoders, | 
 | 			      ARRAY_SIZE(inet_diag_req_nla_decoders), NULL); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | decode_inet_diag_req_v2(struct tcb *const tcp, | 
 | 			const struct nlmsghdr *const nlmsghdr, | 
 | 			const uint8_t family, | 
 | 			const kernel_ulong_t addr, | 
 | 			const unsigned int len) | 
 | { | 
 | 	struct inet_diag_req_v2 req = { .sdiag_family = family }; | 
 | 	size_t offset = sizeof(req.sdiag_family); | 
 | 	bool decode_nla = false; | 
 |  | 
 | 	PRINT_FIELD_XVAL("{", req, sdiag_family, addrfams, "AF_???"); | 
 | 	tprints(", "); | 
 | 	if (len >= sizeof(req)) { | 
 | 		if (!umoven_or_printaddr(tcp, addr + offset, | 
 | 					 sizeof(req) - offset, | 
 | 					 (char *) &req + offset)) { | 
 | 			PRINT_FIELD_XVAL("", req, sdiag_protocol, | 
 | 					 inet_protocols, "IPPROTO_???"); | 
 | 			PRINT_FIELD_FLAGS(", ", req, idiag_ext, | 
 | 					  inet_diag_extended_flags, | 
 | 					  "1<<INET_DIAG_\?\?\?-1"); | 
 | 			PRINT_FIELD_FLAGS(", ", req, idiag_states, | 
 | 					  tcp_state_flags, "1<<TCP_???"); | 
 | 			PRINT_FIELD_INET_DIAG_SOCKID(", ", req, id, | 
 | 						     req.sdiag_family); | 
 | 			decode_nla = true; | 
 | 		} | 
 | 	} else | 
 | 		tprints("..."); | 
 | 	tprints("}"); | 
 |  | 
 | 	offset = NLMSG_ALIGN(sizeof(req)); | 
 | 	if (decode_nla && len > offset) { | 
 | 		tprints(", "); | 
 | 		decode_nlattr(tcp, addr + offset, len - offset, | 
 | 			      inet_diag_req_attrs, "INET_DIAG_REQ_???", | 
 | 			      inet_diag_req_nla_decoders, | 
 | 			      ARRAY_SIZE(inet_diag_req_nla_decoders), NULL); | 
 | 	} | 
 | } | 
 |  | 
 | DECL_NETLINK_DIAG_DECODER(decode_inet_diag_req) | 
 | { | 
 | 	if (nlmsghdr->nlmsg_type == TCPDIAG_GETSOCK | 
 | 	    || nlmsghdr->nlmsg_type == DCCPDIAG_GETSOCK) | 
 | 		decode_inet_diag_req_compat(tcp, nlmsghdr, family, addr, len); | 
 | 	else | 
 | 		decode_inet_diag_req_v2(tcp, nlmsghdr, family, addr, len); | 
 | } | 
 |  | 
 | static bool | 
 | decode_inet_diag_meminfo(struct tcb *const tcp, | 
 | 			 const kernel_ulong_t addr, | 
 | 			 const unsigned int len, | 
 | 			 const void *const opaque_data) | 
 | { | 
 | 	struct inet_diag_meminfo minfo; | 
 |  | 
 | 	if (len < sizeof(minfo)) | 
 | 		return false; | 
 | 	if (umove_or_printaddr(tcp, addr, &minfo)) | 
 | 		return true; | 
 |  | 
 | 	PRINT_FIELD_U("{", minfo, idiag_rmem); | 
 | 	PRINT_FIELD_U(", ", minfo, idiag_wmem); | 
 | 	PRINT_FIELD_U(", ", minfo, idiag_fmem); | 
 | 	PRINT_FIELD_U(", ", minfo, idiag_tmem); | 
 | 	tprints("}"); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | static bool | 
 | decode_tcpvegas_info(struct tcb *const tcp, | 
 | 		     const kernel_ulong_t addr, | 
 | 		     const unsigned int len, | 
 | 		     const void *const opaque_data) | 
 | { | 
 | 	struct tcpvegas_info vegas; | 
 |  | 
 | 	if (len < sizeof(vegas)) | 
 | 		return false; | 
 | 	if (umove_or_printaddr(tcp, addr, &vegas)) | 
 | 		return true; | 
 |  | 
 | 	PRINT_FIELD_U("{", vegas, tcpv_enabled); | 
 | 	PRINT_FIELD_U(", ", vegas, tcpv_rttcnt); | 
 | 	PRINT_FIELD_U(", ", vegas, tcpv_rtt); | 
 | 	PRINT_FIELD_U(", ", vegas, tcpv_minrtt); | 
 | 	tprints("}"); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | static bool | 
 | decode_tcp_dctcp_info(struct tcb *const tcp, | 
 | 		      const kernel_ulong_t addr, | 
 | 		      const unsigned int len, | 
 | 		      const void *const opaque_data) | 
 | { | 
 | 	struct tcp_dctcp_info dctcp; | 
 |  | 
 | 	if (len < sizeof(dctcp)) | 
 | 		return false; | 
 | 	if (umove_or_printaddr(tcp, addr, &dctcp)) | 
 | 		return true; | 
 |  | 
 | 	PRINT_FIELD_U("{", dctcp, dctcp_enabled); | 
 | 	PRINT_FIELD_U(", ", dctcp, dctcp_ce_state); | 
 | 	PRINT_FIELD_U(", ", dctcp, dctcp_alpha); | 
 | 	PRINT_FIELD_U(", ", dctcp, dctcp_ab_ecn); | 
 | 	PRINT_FIELD_U(", ", dctcp, dctcp_ab_tot); | 
 | 	tprints("}"); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | static bool | 
 | decode_tcp_bbr_info(struct tcb *const tcp, | 
 | 		    const kernel_ulong_t addr, | 
 | 		    const unsigned int len, | 
 | 		    const void *const opaque_data) | 
 | { | 
 | 	struct tcp_bbr_info bbr; | 
 |  | 
 | 	if (len < sizeof(bbr)) | 
 | 		return false; | 
 | 	if (umove_or_printaddr(tcp, addr, &bbr)) | 
 | 		return true; | 
 |  | 
 | 	PRINT_FIELD_X("{", bbr, bbr_bw_lo); | 
 | 	PRINT_FIELD_X(", ", bbr, bbr_bw_hi); | 
 | 	PRINT_FIELD_U(", ", bbr, bbr_min_rtt); | 
 | 	PRINT_FIELD_U(", ", bbr, bbr_pacing_gain); | 
 | 	PRINT_FIELD_U(", ", bbr, bbr_cwnd_gain); | 
 | 	tprints("}"); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | static const nla_decoder_t inet_diag_msg_nla_decoders[] = { | 
 | 	[INET_DIAG_MEMINFO]	= decode_inet_diag_meminfo, | 
 | 	[INET_DIAG_INFO]	= NULL,			/* unimplemented */ | 
 | 	[INET_DIAG_VEGASINFO]	= decode_tcpvegas_info, | 
 | 	[INET_DIAG_CONG]	= decode_nla_str, | 
 | 	[INET_DIAG_TOS]		= decode_nla_u8, | 
 | 	[INET_DIAG_TCLASS]	= decode_nla_u8, | 
 | 	[INET_DIAG_SKMEMINFO]	= decode_nla_meminfo, | 
 | 	[INET_DIAG_SHUTDOWN]	= decode_nla_u8, | 
 | 	[INET_DIAG_DCTCPINFO]	= decode_tcp_dctcp_info, | 
 | 	[INET_DIAG_PROTOCOL]	= decode_nla_u8, | 
 | 	[INET_DIAG_SKV6ONLY]	= decode_nla_u8, | 
 | 	[INET_DIAG_LOCALS]	= NULL,			/* unimplemented */ | 
 | 	[INET_DIAG_PEERS]	= NULL,			/* unimplemented */ | 
 | 	[INET_DIAG_PAD]		= NULL, | 
 | 	[INET_DIAG_MARK]	= decode_nla_u32, | 
 | 	[INET_DIAG_BBRINFO]	= decode_tcp_bbr_info, | 
 | 	[INET_DIAG_CLASS_ID]	= decode_nla_u32 | 
 | }; | 
 |  | 
 | DECL_NETLINK_DIAG_DECODER(decode_inet_diag_msg) | 
 | { | 
 | 	struct inet_diag_msg msg = { .idiag_family = family }; | 
 | 	size_t offset = sizeof(msg.idiag_family); | 
 | 	bool decode_nla = false; | 
 |  | 
 | 	PRINT_FIELD_XVAL("{", msg, idiag_family, addrfams, "AF_???"); | 
 | 	tprints(", "); | 
 | 	if (len >= sizeof(msg)) { | 
 | 		if (!umoven_or_printaddr(tcp, addr + offset, | 
 | 					 sizeof(msg) - offset, | 
 | 					 (char *) &msg + offset)) { | 
 | 			PRINT_FIELD_XVAL("", msg, idiag_state, | 
 | 					 tcp_states, "TCP_???"); | 
 | 			PRINT_FIELD_U(", ", msg, idiag_timer); | 
 | 			PRINT_FIELD_U(", ", msg, idiag_retrans); | 
 | 			PRINT_FIELD_INET_DIAG_SOCKID(", ", msg, id, | 
 | 						     msg.idiag_family); | 
 | 			PRINT_FIELD_U(", ", msg, idiag_expires); | 
 | 			PRINT_FIELD_U(", ", msg, idiag_rqueue); | 
 | 			PRINT_FIELD_U(", ", msg, idiag_wqueue); | 
 | 			PRINT_FIELD_U(", ", msg, idiag_uid); | 
 | 			PRINT_FIELD_U(", ", msg, idiag_inode); | 
 | 			decode_nla = true; | 
 | 		} | 
 | 	} else | 
 | 		tprints("..."); | 
 | 	tprints("}"); | 
 |  | 
 | 	offset = NLMSG_ALIGN(sizeof(msg)); | 
 | 	if (decode_nla && len > offset) { | 
 | 		tprints(", "); | 
 | 		decode_nlattr(tcp, addr + offset, len - offset, | 
 | 			      inet_diag_attrs, "INET_DIAG_???", | 
 | 			      inet_diag_msg_nla_decoders, | 
 | 			      ARRAY_SIZE(inet_diag_msg_nla_decoders), NULL); | 
 | 	} | 
 | } |