[PATCH] netlink: decode AF_INET diag attributes

Dmitry V. Levin ldv at altlinux.org
Fri Jun 16 22:04:19 UTC 2017


On Mon, May 22, 2017 at 09:18:09PM +0800, JingPiao Chen wrote:
> * netlink_sock_diag.c: Include <linux/sock_diag.h>.
> (decode_inet_attrs): New function.
> (decode_inet_diag_msg): Use decode_inet_attrs.
> * linux/inet_diag.h (inet_diag_meminfo, tcpvegas_info,
> tcp_dctcp_info, tcp_bbr_info): New structures.
> * linux/sock_diag.h (SK_MEMINFO_VARS): New macro.
> ---
>  linux/inet_diag.h   |  34 +++++++++++++++
>  linux/sock_diag.h   |   2 +
>  netlink_sock_diag.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++-
>  3 files changed, 154 insertions(+), 1 deletion(-)
> 
> diff --git a/linux/inet_diag.h b/linux/inet_diag.h
> index e1df3bb..24302db 100644
> --- a/linux/inet_diag.h
> +++ b/linux/inet_diag.h
> @@ -76,4 +76,38 @@ enum {
>  	INET_DIAG_BBRINFO,
>  };
>  
> +/* INET_DIAG_MEM */
> +struct inet_diag_meminfo {
> +	uint32_t idiag_rmem;
> +	uint32_t idiag_wmem;
> +	uint32_t idiag_fmem;
> +	uint32_t idiag_tmem;
> +};
> +
> +/* INET_DIAG_VEGASINFO */
> +struct tcpvegas_info {
> +	uint32_t tcpv_enabled;
> +	uint32_t tcpv_rttcnt;
> +	uint32_t tcpv_rtt;
> +	uint32_t tcpv_minrtt;
> +};
> +
> +/* INET_DIAG_DCTCPINFO */
> +struct tcp_dctcp_info {
> +	uint16_t dctcp_enabled;
> +	uint16_t dctcp_ce_state;
> +	uint32_t dctcp_alpha;
> +	uint32_t dctcp_ab_ecn;
> +	uint32_t dctcp_ab_tot;
> +};
> +
> +/* INET_DIAG_BBRINFO */
> +struct tcp_bbr_info {
> +	uint32_t bbr_bw_lo;
> +	uint32_t bbr_bw_hi;
> +	uint32_t bbr_min_rtt;
> +	uint32_t bbr_pacing_gain;
> +	uint32_t bbr_cwnd_gain;
> +};
> +
>  #endif /* !STRACE_LINUX_INET_DIAG_H */
> diff --git a/linux/sock_diag.h b/linux/sock_diag.h
> index 83daf35..f69c385 100644
> --- a/linux/sock_diag.h
> +++ b/linux/sock_diag.h
> @@ -4,6 +4,8 @@
>  #define SOCK_DIAG_BY_FAMILY 20
>  #define SOCK_DESTROY 21
>  
> +#define SK_MEMINFO_VARS		9
> +
>  struct sock_diag_req {
>  	uint8_t	sdiag_family;
>  	uint8_t	sdiag_protocol;
> diff --git a/netlink_sock_diag.c b/netlink_sock_diag.c
> index dc8ff43..25b3434 100644
> --- a/netlink_sock_diag.c
> +++ b/netlink_sock_diag.c
> @@ -38,6 +38,7 @@
>  #ifdef AF_SMC
>  # include <linux/smc_diag.h>
>  #endif
> +#include <linux/sock_diag.h>
>  #include <linux/unix_diag.h>
>  
>  #include "xlat/inet_diag_attrs.h"
> @@ -111,6 +112,28 @@ decode_unix_diag_req(struct tcb *const tcp,
>  	tprints("}");
>  }
>  
> +static bool
> +print_meminfo(struct tcb *tcp, void *elem_buf,
> +	      size_t elem_size, void *opaque_data)
> +{
> +	tprintf("%" PRIu32, *(uint32_t *) elem_buf);
> +
> +	return true;
> +}
> +
> +static void
> +decode_meminfo(struct tcb *tcp, kernel_ulong_t addr, kernel_ulong_t len)
> +{
> +	uint32_t mem;
> +	int nmemb = len / sizeof(mem);
> +
> +	if (nmemb > SK_MEMINFO_VARS)
> +		nmemb = SK_MEMINFO_VARS;
> +
> +	print_array(tcp, addr, nmemb, &mem, sizeof(mem),
> +		    umoven_or_printaddr, print_meminfo, 0);
> +}
> +
>  static void
>  decode_unix_diag_msg(struct tcb *const tcp,
>  		     const struct nlmsghdr *const nlmsghdr,
> @@ -406,6 +429,99 @@ decode_inet_diag_req(struct tcb *const tcp,
>  					       family, addr, len);
>  }
>  
> +static struct nla_policy inet_policy[] = {
> +	[INET_DIAG_MEMINFO]	= { .type = NLA_NESTED },
> +	[INET_DIAG_INFO]	= { .type = NLA_NESTED }, /* unimplemented */
> +	[INET_DIAG_VEGASINFO]	= { .type = NLA_NESTED },
> +	[INET_DIAG_CONG]	= { .type = NLA_NUL_STRING },
> +	[INET_DIAG_TOS]		= { .type = NLA_U8 },
> +	[INET_DIAG_TCLASS]	= { .type = NLA_U8 },
> +	[INET_DIAG_SKMEMINFO]	= { .type = NLA_NESTED },
> +	[INET_DIAG_SHUTDOWN]	= { .type = NLA_U8 },
> +	[INET_DIAG_DCTCPINFO]	= { .type = NLA_NESTED },
> +	[INET_DIAG_PROTOCOL]	= { .type = NLA_U8 },
> +	[INET_DIAG_SKV6ONLY]	= { .type = NLA_U8 },
> +	[INET_DIAG_LOCALS]	= { .type = NLA_NESTED }, /* unimplemented */
> +	[INET_DIAG_PEERS]	= { .type = NLA_NESTED }, /* unimplemented */
> +	[INET_DIAG_PAD]		= { .type = NLA_UNSPEC },
> +	[INET_DIAG_MARK]	= { .type = NLA_U32 },
> +	[INET_DIAG_BBRINFO]	= { .type = NLA_NESTED },
> +};

Firstly, a table is created.

> +
> +static bool
> +decode_inet_attrs(struct tcb *tcp, kernel_ulong_t addr,
> +		  kernel_ulong_t len, int type, void *opaque_data)
> +{
> +	switch (type) {
> +	case INET_DIAG_MEMINFO: {
> +		struct inet_diag_meminfo minfo;
> +
> +		if (len < sizeof(minfo))
> +			return false;
> +		if (umove_or_printaddr(tcp, addr, &minfo))
> +			break;
> +
> +		tprintf("{idiag_rmem=%" PRIu32 ", idiag_wmem=%" PRIu32
> +			", idiag_fmem=%" PRIu32 ", idiag_tmem=%" PRIu32 "}",
> +			minfo.idiag_rmem, minfo.idiag_wmem,
> +			minfo.idiag_fmem, minfo.idiag_tmem);
> +		break;
> +	}
> +	case INET_DIAG_VEGASINFO: {
> +		struct tcpvegas_info vegas;
> +
> +		if (len < sizeof(vegas))
> +			return false;
> +		if (umove_or_printaddr(tcp, addr, &vegas))
> +			break;
> +
> +		tprintf("{tcpv_enabled=%" PRIu32 ", tcpv_rttcnt=%" PRIu32
> +			", tcpv_rtt=%" PRIu32 ", tcpv_minrtt=%" PRIu32 "}",
> +			vegas.tcpv_enabled, vegas.tcpv_rttcnt,
> +			vegas.tcpv_rtt, vegas.tcpv_minrtt);
> +		break;
> +	}
> +	case INET_DIAG_DCTCPINFO: {
> +		struct tcp_dctcp_info dctcp;
> +
> +		if (len < sizeof(dctcp))
> +			return false;
> +		if (umove_or_printaddr(tcp, addr, &dctcp))
> +			break;
> +
> +		tprintf("{dctcp_enabled=%" PRIu16 ", dctcp_ce_state=%" PRIu16
> +			", dctcp_alpha=%" PRIu32 ", dctcp_ab_ecn=%" PRIu32
> +			", dctcp_ab_tot=%" PRIu32 "}",
> +			dctcp.dctcp_enabled, dctcp.dctcp_ce_state,
> +			dctcp.dctcp_alpha, dctcp.dctcp_ab_ecn,
> +			dctcp.dctcp_ab_tot);
> +		break;
> +	}
> +	case INET_DIAG_BBRINFO: {
> +		struct tcp_bbr_info bbr;
> +
> +		if (len < sizeof(bbr))
> +			return false;
> +		if (umove_or_printaddr(tcp, addr, &bbr))
> +			break;
> +
> +		tprintf("{bbr_bw_lo=%#" PRIx32 ", bbr_bw_hi=%#" PRIx32
> +			", bbr_min_rtt=%" PRIu32 ", bbr_pacing_gain=%" PRIu32
> +			", bbr_cwnd_gain=%" PRIu32 "}",
> +			bbr.bbr_bw_lo, bbr.bbr_bw_hi, bbr.bbr_min_rtt,
> +			bbr.bbr_pacing_gain, bbr.bbr_cwnd_gain);
> +		break;
> +	}
> +	case INET_DIAG_SKMEMINFO:
> +		decode_meminfo(tcp, addr, len);
> +		break;
> +	default:
> +		return false;
> +	}
> +
> +	return true;
> +}

Secondly, a big switch with cases from the table is created.

> +
>  static void
>  decode_inet_diag_msg(struct tcb *const tcp,
>  		     const struct nlmsghdr *const nlmsghdr,
> @@ -447,7 +563,8 @@ decode_inet_diag_msg(struct tcb *const tcp,
>  	tprints("}");
>  
>  	decode_nlattr(tcp, addr, len, sizeof(msg), inet_diag_attrs,
> -		      "INET_DIAG_???", 0, NULL, NULL, NULL);
> +		      "INET_DIAG_???", ARRAY_SIZE(inet_policy),
> +		      inet_policy, decode_inet_attrs, NULL);
>  }

Lastly, nlattr decoder is called with both the table and the function
containing this big switch.

For each nla of verification type NLA_NESTED, this decoder
does the following:
- finds this nla verification type in the table;
- switches by this nla verification type;
- calls the function;
- the function switches by nla type and does something useful.

Wouldn't it be much easier if the corresponding function was specified
in the table along with its nla verification type?  This way both
switches could be skipped for nla types that have functions defined.


-- 
ldv
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.strace.io/pipermail/strace-devel/attachments/20170617/3ab7bfe2/attachment.bin>


More information about the Strace-devel mailing list