[PATCH v2 1/2] rtnl_link: decode ifinfomsg netlink attributes

Dmitry V. Levin ldv at altlinux.org
Sat Aug 19 22:33:53 UTC 2017


On Sat, Aug 19, 2017 at 09:50:10AM +0800, JingPiao Chen wrote:
> * configure.ac (AC_CHECK_HEADERS): Add linux/if_link.h.
> (AC_CHECK_TYPES): Check for struct rtnl_link_stats64 in linux/if_link.h.
> (AC_CHECK_MEMBERS): Check for rx_nohandler field
> in struct rtnl_link_stats and struct rtnl_link_stats64.
> * rtnl_link.c: Include <arpa/inet.h>, <linux/if_arp.h>,
> <linux/if_link.h> and <linux/netdevice.h>.
> (min_ifla_address_len, ifla_address_default_decoder,
> ifla_address_type_specific_decoder,
> decode_ifla_address, decode_rtnl_link_stats,
> decode_rtnl_link_ifmap, decode_rtnl_link_stats64,
> print_item_id, decode_ifla_phys_item_id): New functions.
> (decode_ifla_phys_item_id): New array.

How could decode_ifla_phys_item_id be both a function and an array?

> (decode_ifinfomsg): Use it.
> ---
>  configure.ac |   6 ++
>  rtnl_link.c  | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 303 insertions(+), 1 deletion(-)
> 
> diff --git a/configure.ac b/configure.ac
> index cb398fb..dacd67f 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -382,6 +382,7 @@ AC_CHECK_HEADERS(m4_normalize([
>  	linux/genetlink.h
>  	linux/hiddev.h
>  	linux/if_addr.h
> +	linux/if_link.h
>  	linux/ip_vs.h
>  	linux/ipc.h
>  	linux/mmtimer.h
> @@ -447,6 +448,11 @@ AC_CHECK_TYPES([struct dcbmsg],,, [#include <linux/dcbnl.h>])
>  AC_CHECK_TYPES([struct ifaddrlblmsg],,, [#include <linux/if_addrlabel.h>])
>  AC_CHECK_TYPES([struct netconfmsg],,, [#include <linux/netconf.h>])
>  
> +AC_CHECK_TYPES([struct rtnl_link_stats64], [
> +	AC_CHECK_MEMBERS([struct rtnl_link_stats64.rx_nohandler],,, [#include <linux/if_link.h>])
> +],, [#include <linux/if_link.h>])
> +AC_CHECK_MEMBERS([struct rtnl_link_stats.rx_nohandler],,, [#include <linux/if_link.h>])
> +
>  AC_CHECK_TYPES([struct statfs], [
>  	AC_CHECK_MEMBERS([struct statfs.f_frsize],,, [#include <linux/types.h>
>  #include <asm/statfs.h>])
> diff --git a/rtnl_link.c b/rtnl_link.c
> index 18897f3..29c2e19 100644
> --- a/rtnl_link.c
> +++ b/rtnl_link.c
> @@ -32,11 +32,305 @@
>  #include "nlattr.h"
>  #include "print_fields.h"
>  
> +#include <arpa/inet.h>
> +
> +#include <linux/if_arp.h>
> +#ifdef HAVE_LINUX_IF_LINK_H
> +# include <linux/if_link.h>
> +#endif
> +#include <linux/netdevice.h>
>  #include "netlink.h"
>  #include <linux/rtnetlink.h>
>  
>  #include "xlat/rtnl_link_attrs.h"
>  
> +static int
> +min_ifla_address_len(const unsigned short type)
> +{
> +	switch (type) {
> +	case ARPHRD_TUNNEL:
> +	case ARPHRD_SIT:
> +	case ARPHRD_IPGRE:
> +		return sizeof(struct in_addr);
> +	case ARPHRD_TUNNEL6:
> +		return sizeof(struct in6_addr);
> +	default:
> +		return 0;
> +	}
> +}

The same switch on ifi_type value is repeated twice: first in
min_ifla_address_len, second in ifla_address_type_specific_decoder.
In the previous version of this patch there was just one switch
on ifi_type value.

> +
> +static bool
> +ifla_address_default_decoder(struct tcb *const tcp,
> +                             const kernel_ulong_t addr,
> +                             const unsigned int len)
> +{
> +	const unsigned int addr_len =
> +		len < MAX_ADDR_LEN ? len : MAX_ADDR_LEN;
> +	size_t i;
> +
> +	for (i = 0; i < addr_len; i++) {
> +		uint8_t buf;
> +		if (umove(tcp, addr + i, &buf) < 0)
> +			break;
> +		if (i)
> +			tprints(":");
> +		tprintf("%02x", buf);
> +	}
> +	if (i < addr_len || addr_len < len)
> +		tprints(": ...");
> +
> +	return true;
> +}

ifla_address_default_decoder inefficiently fetches one byte at a time
instead of the whole address.  Why umoven_or_printaddr is not used here?

> +
> +static bool
> +ifla_address_type_specific_decoder(const char *const buf,
> +				   const unsigned short type)
> +{
> +	switch (type) {
> +	case ARPHRD_TUNNEL:
> +	case ARPHRD_SIT:
> +	case ARPHRD_IPGRE: {
> +		char str[INET_ADDRSTRLEN];
> +
> +		if (!inet_ntop(AF_INET, buf, str, sizeof(str)))
> +			return false;
> +		tprintf("%s", str);
> +		break;
> +	}
> +	case ARPHRD_TUNNEL6: {
> +		char str[INET6_ADDRSTRLEN];
> +
> +		if (!inet_ntop(AF_INET6, buf, str, sizeof(str)))
> +			return false;
> +		tprintf("%s", str);
> +		break;
> +	}
> +	default:
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static bool
> +decode_ifla_address(struct tcb *const tcp,
> +		    const kernel_ulong_t addr,
> +		    const unsigned int len,
> +		    const void *const opaque_data)
> +{
> +	const unsigned short type =
> +		((struct ifinfomsg *) opaque_data)->ifi_type;
> +	size_t size = min_ifla_address_len(type);
> +	char buf[256];
> +
> +	if (!size || len < size)
> +		return ifla_address_default_decoder(tcp, addr, len);
> +	else if (!umoven_or_printaddr(tcp, addr, size, buf))
> +		return ifla_address_type_specific_decoder(buf, type);
> +
> +	return true;
> +}
> +
> +static bool
> +decode_rtnl_link_stats(struct tcb *const tcp,
> +		       const kernel_ulong_t addr,
> +		       const unsigned int len,
> +		       const void *const opaque_data)
> +{
> +	struct rtnl_link_stats st;
> +
> +	if (len < sizeof(st))
> +		return false;

The kernel may not transfer struct rtnl_link_stats.rx_nohandler despite
the latter being defined by linux/if_link.h, e.g. if the kernel uses
an older version of struct rtnl_link_stats.
The minimal size is therefore not sizeof(struct rtnl_link_stats)
but offsetofend(struct rtnl_link_stats, tx_compressed).

Likewise, with struct rtnl_link_stats64.rx_nohandler.

> +	else if (!umove_or_printaddr(tcp, addr, &st)) {
> +		PRINT_FIELD_U("{", st, rx_packets);
> +		PRINT_FIELD_U(", ", st, tx_packets);
> +		PRINT_FIELD_U(", ", st, rx_bytes);
> +		PRINT_FIELD_U(", ", st, tx_bytes);
> +		PRINT_FIELD_U(", ", st, rx_errors);
> +		PRINT_FIELD_U(", ", st, tx_errors);
> +		PRINT_FIELD_U(", ", st, rx_dropped);
> +		PRINT_FIELD_U(", ", st, tx_dropped);
> +		PRINT_FIELD_U(", ", st, multicast);
> +		PRINT_FIELD_U(", ", st, collisions);
> +
> +		PRINT_FIELD_U(", ", st, rx_length_errors);
> +		PRINT_FIELD_U(", ", st, rx_over_errors);
> +		PRINT_FIELD_U(", ", st, rx_crc_errors);
> +		PRINT_FIELD_U(", ", st, rx_frame_errors);
> +		PRINT_FIELD_U(", ", st, rx_fifo_errors);
> +		PRINT_FIELD_U(", ", st, rx_missed_errors);
> +
> +		PRINT_FIELD_U(", ", st, tx_aborted_errors);
> +		PRINT_FIELD_U(", ", st, tx_carrier_errors);
> +		PRINT_FIELD_U(", ", st, tx_fifo_errors);
> +		PRINT_FIELD_U(", ", st, tx_heartbeat_errors);
> +		PRINT_FIELD_U(", ", st, tx_window_errors);
> +
> +		PRINT_FIELD_U(", ", st, rx_compressed);
> +		PRINT_FIELD_U(", ", st, tx_compressed);
> +#ifdef HAVE_STRUCT_RTNL_LINK_STATS_RX_NOHANDLER
> +		PRINT_FIELD_U(", ", st, rx_nohandler);
> +#endif
> +		tprints("}");
> +	}
> +
> +	return true;
> +}
> +
> +static bool
> +decode_rtnl_link_ifmap(struct tcb *const tcp,
> +		       const kernel_ulong_t addr,
> +		       const unsigned int len,
> +		       const void *const opaque_data)
> +{
> +	struct rtnl_link_ifmap map;
> +	const unsigned int sizeof_ifmap =
> +		offsetofend(struct rtnl_link_ifmap, port);
> +
> +	if (len < sizeof_ifmap)
> +		return false;
> +	else if (!umoven_or_printaddr(tcp, addr, sizeof_ifmap, &map)) {
> +		PRINT_FIELD_X("{", map, mem_start);
> +		PRINT_FIELD_X(", ", map, mem_end);
> +		PRINT_FIELD_X(", ", map, base_addr);
> +		PRINT_FIELD_U(", ", map, irq);
> +		PRINT_FIELD_U(", ", map, dma);
> +		PRINT_FIELD_U(", ", map, port);
> +		tprints("}");
> +	}
> +
> +	return true;
> +}
> +
> +static bool
> +decode_rtnl_link_stats64(struct tcb *const tcp,
> +		         const kernel_ulong_t addr,
> +			 const unsigned int len,
> +			 const void *const opaque_data)
> +{
> +#ifdef HAVE_STRUCT_RTNL_LINK_STATS64
> +	struct rtnl_link_stats64 st;
> +
> +	if (len < sizeof(st))
> +		return false;
> +	else if (!umove_or_printaddr(tcp, addr, &st)) {
> +		PRINT_FIELD_U("{", st, rx_packets);
> +		PRINT_FIELD_U(", ", st, tx_packets);
> +		PRINT_FIELD_U(", ", st, rx_bytes);
> +		PRINT_FIELD_U(", ", st, tx_bytes);
> +		PRINT_FIELD_U(", ", st, rx_errors);
> +		PRINT_FIELD_U(", ", st, tx_errors);
> +		PRINT_FIELD_U(", ", st, rx_dropped);
> +		PRINT_FIELD_U(", ", st, tx_dropped);
> +		PRINT_FIELD_U(", ", st, multicast);
> +		PRINT_FIELD_U(", ", st, collisions);
> +
> +		PRINT_FIELD_U(", ", st, rx_length_errors);
> +		PRINT_FIELD_U(", ", st, rx_over_errors);
> +		PRINT_FIELD_U(", ", st, rx_crc_errors);
> +		PRINT_FIELD_U(", ", st, rx_frame_errors);
> +		PRINT_FIELD_U(", ", st, rx_fifo_errors);
> +		PRINT_FIELD_U(", ", st, rx_missed_errors);
> +
> +		PRINT_FIELD_U(", ", st, tx_aborted_errors);
> +		PRINT_FIELD_U(", ", st, tx_carrier_errors);
> +		PRINT_FIELD_U(", ", st, tx_fifo_errors);
> +		PRINT_FIELD_U(", ", st, tx_heartbeat_errors);
> +		PRINT_FIELD_U(", ", st, tx_window_errors);
> +
> +		PRINT_FIELD_U(", ", st, rx_compressed);
> +		PRINT_FIELD_U(", ", st, tx_compressed);
> +#ifdef HAVE_STRUCT_RTNL_LINK_STATS64_RX_NOHANDLER
> +		PRINT_FIELD_U(", ", st, rx_nohandler);
> +#endif
> +		tprints("}");
> +	}
> +
> +	return true;
> +#else
> +	return false;
> +#endif
> +}
> +
> +static bool
> +print_item_id(struct tcb *const tcp, void *const elem_buf,
> +	      const size_t elem_size, void *const opaque_data)
> +{
> +	unsigned int *const count = opaque_data;
> +
> +	/* MAX_PHYS_ITEM_ID_LEN = 32 */
> +	if ((*count)++ >= 32) {
> +		tprints("...");
> +		return false;
> +	}
> +
> +	tprintf("%" PRIu8, *(uint8_t *) elem_buf);
> +
> +	return true;
> +}
> +
> +static bool
> +decode_ifla_phys_item_id(struct tcb *const tcp,
> +		         const kernel_ulong_t addr,
> +			 const unsigned int len,
> +			 const void *const opaque_data)
> +{
> +	uint8_t id;
> +	unsigned int count = 0;
> +
> +	print_array(tcp, addr, len, &id, sizeof(id),
> +		    umoven_or_printaddr, print_item_id, &count);

I'm not sure it's the best way to decode struct netdev_phys_item_id.id.

> +
> +	return true;
> +}
> +
> +static const nla_decoder_t ifinfomsg_nla_decoders[] = {
> +	[IFLA_ADDRESS]		= decode_ifla_address,
> +	[IFLA_BROADCAST]	= decode_ifla_address,
> +	[IFLA_IFNAME]		= decode_nla_str,
> +	[IFLA_MTU]		= decode_nla_u32,
> +	[IFLA_LINK]		= decode_nla_u32,
> +	[IFLA_QDISC]		= decode_nla_str,
> +	[IFLA_STATS]		= decode_rtnl_link_stats,
> +	[IFLA_COST]		= NULL,
> +	[IFLA_PRIORITY]		= NULL,
> +	[IFLA_MASTER]		= decode_nla_u32,
> +	[IFLA_WIRELESS]		= NULL,

Here a parser of struct iw_event is expected.

> +	[IFLA_PROTINFO]		= NULL,

This one seems to be used in the kernel.

> +	[IFLA_TXQLEN]		= decode_nla_u32,
> +	[IFLA_MAP]		= decode_rtnl_link_ifmap,
> +	[IFLA_WEIGHT]		= decode_nla_u32,
> +	[IFLA_OPERSTATE]	= decode_nla_u8,
> +	[IFLA_LINKMODE]		= decode_nla_u8,
> +	[IFLA_LINKINFO]		= NULL,

This one also seems to be used in the kernel.

> +	[IFLA_NET_NS_PID]	= decode_nla_u32,
> +	[IFLA_IFALIAS]		= decode_nla_str,
> +	[IFLA_NUM_VF]		= decode_nla_u32,
> +	[IFLA_VFINFO_LIST]	= NULL,

Likewise.

> +	[IFLA_STATS64]		= decode_rtnl_link_stats64,
> +	[IFLA_VF_PORTS]		= NULL,

Likewise.

> +	[IFLA_PORT_SELF]	= NULL,

Likewise.

> +	[IFLA_AF_SPEC]		= NULL,

Likewise.

> +	[IFLA_GROUP]		= decode_nla_u32,
> +	[IFLA_NET_NS_FD]	= decode_nla_u32,
> +	[IFLA_EXT_MASK]		= decode_nla_u32,
> +	[IFLA_PROMISCUITY]	= decode_nla_u32,
> +	[IFLA_NUM_TX_QUEUES]	= decode_nla_u32,
> +	[IFLA_NUM_RX_QUEUES]	= decode_nla_u32,
> +	[IFLA_CARRIER]		= decode_nla_u8,
> +	[IFLA_PHYS_PORT_ID]	= decode_ifla_phys_item_id,
> +	[IFLA_CARRIER_CHANGES]	= decode_nla_u32,
> +	[IFLA_PHYS_SWITCH_ID]	= decode_ifla_phys_item_id,
> +	[IFLA_LINK_NETNSID]	= decode_nla_s32,
> +	[IFLA_PHYS_PORT_NAME]	= decode_nla_str,
> +	[IFLA_PROTO_DOWN]	= decode_nla_u8,
> +	[IFLA_GSO_MAX_SEGS]	= decode_nla_u32,
> +	[IFLA_GSO_MAX_SIZE]	= decode_nla_u32,
> +	[IFLA_PAD]		= NULL,
> +	[IFLA_XDP]		= NULL,

Likewise.


-- 
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/20170820/9fe462e1/attachment.bin>


More information about the Strace-devel mailing list