diff --git a/SOURCES/0020-uapi-linux-update-kernel-UAPI-header-files.patch b/SOURCES/0020-uapi-linux-update-kernel-UAPI-header-files.patch
new file mode 100644
index 0000000..f36f8b9
--- /dev/null
+++ b/SOURCES/0020-uapi-linux-update-kernel-UAPI-header-files.patch
@@ -0,0 +1,117 @@
+From 8fa6c3a1bc2536816e961a4ef865c67218683bf2 Mon Sep 17 00:00:00 2001
+From: Amit Cohen <amitc@mellanox.com>
+Date: Thu, 2 Jul 2020 16:11:09 +0300
+Subject: [PATCH 20/26] uapi: linux: update kernel UAPI header files
+
+Add copies of kernel UAPI header files needed for link extended state:
+	uapi/linux/ethtool.h
+	uapi/linux/ethtool_netlink.h
+
+The copies are taken from net-next tree,
+commit ecc31c60240b ("ethtool: Add link extended state")
+
+Signed-off-by: Amit Cohen <amitc@mellanox.com>
+Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
+(cherry picked from commit dc46dd315071190b880dba9b48dd4521cf7c133e)
+---
+ uapi/linux/ethtool.h         | 70 ++++++++++++++++++++++++++++++++++++
+ uapi/linux/ethtool_netlink.h |  2 ++
+ 2 files changed, 72 insertions(+)
+
+diff --git a/uapi/linux/ethtool.h b/uapi/linux/ethtool.h
+index 355e718a99fd..847ccd0b1fce 100644
+--- a/uapi/linux/ethtool.h
++++ b/uapi/linux/ethtool.h
+@@ -577,6 +577,76 @@ struct ethtool_pauseparam {
+ 	__u32	tx_pause;
+ };
+ 
++/**
++ * enum ethtool_link_ext_state - link extended state
++ */
++enum ethtool_link_ext_state {
++	ETHTOOL_LINK_EXT_STATE_AUTONEG,
++	ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
++	ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
++	ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY,
++	ETHTOOL_LINK_EXT_STATE_NO_CABLE,
++	ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
++	ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE,
++	ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE,
++	ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED,
++	ETHTOOL_LINK_EXT_STATE_OVERHEAT,
++};
++
++/**
++ * enum ethtool_link_ext_substate_autoneg - more information in addition to
++ * ETHTOOL_LINK_EXT_STATE_AUTONEG.
++ */
++enum ethtool_link_ext_substate_autoneg {
++	ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED = 1,
++	ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED,
++	ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED,
++	ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE,
++	ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE,
++	ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD,
++};
++
++/**
++ * enum ethtool_link_ext_substate_link_training - more information in addition to
++ * ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE.
++ */
++enum ethtool_link_ext_substate_link_training {
++	ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED = 1,
++	ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT,
++	ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY,
++	ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT,
++};
++
++/**
++ * enum ethtool_link_ext_substate_logical_mismatch - more information in addition
++ * to ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH.
++ */
++enum ethtool_link_ext_substate_link_logical_mismatch {
++	ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK = 1,
++	ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK,
++	ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS,
++	ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED,
++	ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED,
++};
++
++/**
++ * enum ethtool_link_ext_substate_bad_signal_integrity - more information in
++ * addition to ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY.
++ */
++enum ethtool_link_ext_substate_bad_signal_integrity {
++	ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS = 1,
++	ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE,
++};
++
++/**
++ * enum ethtool_link_ext_substate_cable_issue - more information in
++ * addition to ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE.
++ */
++enum ethtool_link_ext_substate_cable_issue {
++	ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE = 1,
++	ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE,
++};
++
+ #define ETH_GSTRING_LEN		32
+ 
+ /**
+diff --git a/uapi/linux/ethtool_netlink.h b/uapi/linux/ethtool_netlink.h
+index c1b4d67d35d1..cebdb52e6a05 100644
+--- a/uapi/linux/ethtool_netlink.h
++++ b/uapi/linux/ethtool_netlink.h
+@@ -237,6 +237,8 @@ enum {
+ 	ETHTOOL_A_LINKSTATE_LINK,		/* u8 */
+ 	ETHTOOL_A_LINKSTATE_SQI,		/* u32 */
+ 	ETHTOOL_A_LINKSTATE_SQI_MAX,		/* u32 */
++	ETHTOOL_A_LINKSTATE_EXT_STATE,		/* u8 */
++	ETHTOOL_A_LINKSTATE_EXT_SUBSTATE,	/* u8 */
+ 
+ 	/* add new constants above here */
+ 	__ETHTOOL_A_LINKSTATE_CNT,
+-- 
+2.26.2
+
diff --git a/SOURCES/0021-netlink-settings-expand-linkstate_reply_cb-to-suppor.patch b/SOURCES/0021-netlink-settings-expand-linkstate_reply_cb-to-suppor.patch
new file mode 100644
index 0000000..1b5bcf3
--- /dev/null
+++ b/SOURCES/0021-netlink-settings-expand-linkstate_reply_cb-to-suppor.patch
@@ -0,0 +1,187 @@
+From abc33073ed6d4528d3c951cc719a155a6e1178cd Mon Sep 17 00:00:00 2001
+From: Amit Cohen <amitc@mellanox.com>
+Date: Thu, 2 Jul 2020 16:11:11 +0300
+Subject: [PATCH 21/26] netlink: settings: expand linkstate_reply_cb() to
+ support link extended state
+
+Print extended state in addition to link state.
+
+In case that extended state is not provided, print state only.
+If extended substate is provided in addition to the extended state,
+print it also.
+
+Signed-off-by: Amit Cohen <amitc@mellanox.com>
+Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
+(cherry picked from commit ba6367d2bb32173166b91fbcc053865c99ca7c57)
+---
+ netlink/settings.c | 147 ++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 146 insertions(+), 1 deletion(-)
+
+diff --git a/netlink/settings.c b/netlink/settings.c
+index 3a9518a7e12b..75db15e5069c 100644
+--- a/netlink/settings.c
++++ b/netlink/settings.c
+@@ -565,6 +565,149 @@ int linkinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+ 	return MNL_CB_OK;
+ }
+ 
++static const char *get_enum_string(const char *const *string_table, unsigned int n_string_table,
++				   unsigned int val)
++{
++	if (val >= n_string_table || !string_table[val])
++		return NULL;
++	else
++		return string_table[val];
++}
++
++static const char *const names_link_ext_state[] = {
++	[ETHTOOL_LINK_EXT_STATE_AUTONEG]		= "Autoneg",
++	[ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE]	= "Link training failure",
++	[ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH]	= "Logical mismatch",
++	[ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY]	= "Bad signal integrity",
++	[ETHTOOL_LINK_EXT_STATE_NO_CABLE]		= "No cable",
++	[ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE]		= "Cable issue",
++	[ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE]		= "EEPROM issue",
++	[ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE]	= "Calibration failure",
++	[ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED]	= "Power budget exceeded",
++	[ETHTOOL_LINK_EXT_STATE_OVERHEAT]		= "Overheat",
++};
++
++static const char *const names_autoneg_link_ext_substate[] = {
++	[ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED]		=
++		"No partner detected",
++	[ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED]			=
++		"Ack not received",
++	[ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED]	=
++		"Next page exchange failed",
++	[ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE]	=
++		"No partner detected during force mode",
++	[ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE]	=
++		"FEC mismatch during override",
++	[ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD]				=
++		"No HCD",
++};
++
++static const char *const names_link_training_link_ext_substate[] = {
++	[ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED]			=
++		"KR frame lock not acquired",
++	[ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT]				=
++		"KR link inhibit timeout",
++	[ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY]	=
++		"KR Link partner did not set receiver ready",
++	[ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT]					=
++		"Remote side is not ready yet",
++};
++
++static const char *const names_link_logical_mismatch_link_ext_substate[] = {
++	[ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK]	=
++		"PCS did not acquire block lock",
++	[ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK]	=
++		"PCS did not acquire AM lock",
++	[ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS]	=
++		"PCS did not get align_status",
++	[ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED]		=
++		"FC FEC is not locked",
++	[ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED]		=
++		"RS FEC is not locked",
++};
++
++static const char *const names_bad_signal_integrity_link_ext_substate[] = {
++	[ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS]	=
++		"Large number of physical errors",
++	[ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE]		=
++		"Unsupported rate",
++};
++
++static const char *const names_cable_issue_link_ext_substate[] = {
++	[ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE]	=
++		"Unsupported cable",
++	[ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE]	=
++		"Cable test failure",
++};
++
++static const char *link_ext_substate_get(uint8_t link_ext_state_val, uint8_t link_ext_substate_val)
++{
++	switch (link_ext_state_val) {
++	case ETHTOOL_LINK_EXT_STATE_AUTONEG:
++		return get_enum_string(names_autoneg_link_ext_substate,
++				       ARRAY_SIZE(names_autoneg_link_ext_substate),
++				       link_ext_substate_val);
++	case ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE:
++		return get_enum_string(names_link_training_link_ext_substate,
++				       ARRAY_SIZE(names_link_training_link_ext_substate),
++				       link_ext_substate_val);
++	case ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH:
++		return get_enum_string(names_link_logical_mismatch_link_ext_substate,
++				       ARRAY_SIZE(names_link_logical_mismatch_link_ext_substate),
++				       link_ext_substate_val);
++	case ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY:
++		return get_enum_string(names_bad_signal_integrity_link_ext_substate,
++				       ARRAY_SIZE(names_bad_signal_integrity_link_ext_substate),
++				       link_ext_substate_val);
++	case ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE:
++		return get_enum_string(names_cable_issue_link_ext_substate,
++				       ARRAY_SIZE(names_cable_issue_link_ext_substate),
++				       link_ext_substate_val);
++	default:
++		return NULL;
++	}
++}
++
++static void linkstate_link_ext_substate_print(const struct nlattr *tb[], struct nl_context *nlctx,
++					      uint8_t link_ext_state_val)
++{
++	uint8_t link_ext_substate_val;
++	const char *link_ext_substate_str;
++
++	if (!tb[ETHTOOL_A_LINKSTATE_EXT_SUBSTATE])
++		return;
++
++	link_ext_substate_val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_EXT_SUBSTATE]);
++
++	link_ext_substate_str = link_ext_substate_get(link_ext_state_val, link_ext_substate_val);
++	if (!link_ext_substate_str)
++		printf(", %u", link_ext_substate_val);
++	else
++		printf(", %s", link_ext_substate_str);
++}
++
++static void linkstate_link_ext_state_print(const struct nlattr *tb[], struct nl_context *nlctx)
++{
++	uint8_t link_ext_state_val;
++	const char *link_ext_state_str;
++
++	if (!tb[ETHTOOL_A_LINKSTATE_EXT_STATE])
++		return;
++
++	link_ext_state_val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_EXT_STATE]);
++
++	link_ext_state_str = get_enum_string(names_link_ext_state,
++					     ARRAY_SIZE(names_link_ext_state),
++					     link_ext_state_val);
++	if (!link_ext_state_str)
++		printf(" (%u", link_ext_state_val);
++	else
++		printf(" (%s", link_ext_state_str);
++
++	linkstate_link_ext_substate_print(tb, nlctx, link_ext_state_val);
++	printf(")");
++}
++
+ int linkstate_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+ {
+ 	const struct nlattr *tb[ETHTOOL_A_LINKSTATE_MAX + 1] = {};
+@@ -585,7 +728,9 @@ int linkstate_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+ 		uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_LINK]);
+ 
+ 		print_banner(nlctx);
+-		printf("\tLink detected: %s\n", val ? "yes" : "no");
++		printf("\tLink detected: %s", val ? "yes" : "no");
++		linkstate_link_ext_state_print(tb, nlctx);
++		printf("\n");
+ 	}
+ 
+ 	if (tb[ETHTOOL_A_LINKSTATE_SQI]) {
+-- 
+2.26.2
+
diff --git a/SOURCES/0022-netlink-prepare-for-more-per-op-info.patch b/SOURCES/0022-netlink-prepare-for-more-per-op-info.patch
new file mode 100644
index 0000000..4b3a008
--- /dev/null
+++ b/SOURCES/0022-netlink-prepare-for-more-per-op-info.patch
@@ -0,0 +1,126 @@
+From 15e57173470b0929fd649bc7b0376d41c786ddbe Mon Sep 17 00:00:00 2001
+From: Jakub Kicinski <kuba@kernel.org>
+Date: Sun, 18 Oct 2020 14:31:49 -0700
+Subject: [PATCH 22/26] netlink: prepare for more per-op info
+
+We stored an array of op flags, to check if operations are
+supported. Make that array a structure rather than plain
+uint32_t in preparation for storing more state.
+
+v3: new patch
+
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
+(cherry picked from commit 8d36270be3c06b99eba281ccf341ebfab555c6b6)
+---
+ netlink/netlink.c | 25 +++++++++++++------------
+ netlink/netlink.h |  6 +++++-
+ 2 files changed, 18 insertions(+), 13 deletions(-)
+
+diff --git a/netlink/netlink.c b/netlink/netlink.c
+index e42d57076a4b..86dc1efdf5ce 100644
+--- a/netlink/netlink.c
++++ b/netlink/netlink.c
+@@ -120,19 +120,19 @@ bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd,
+ 		nlctx->wildcard_unsupported = true;
+ 		return true;
+ 	}
+-	if (!nlctx->ops_flags) {
++	if (!nlctx->ops_info) {
+ 		nlctx->ioctl_fallback = true;
+ 		return false;
+ 	}
+-	if (cmd > ETHTOOL_MSG_USER_MAX || !nlctx->ops_flags[cmd]) {
++	if (cmd > ETHTOOL_MSG_USER_MAX || !nlctx->ops_info[cmd].op_flags) {
+ 		nlctx->ioctl_fallback = true;
+ 		return true;
+ 	}
+ 
+-	if (is_dump && !(nlctx->ops_flags[cmd] & GENL_CMD_CAP_DUMP))
++	if (is_dump && !(nlctx->ops_info[cmd].op_flags & GENL_CMD_CAP_DUMP))
+ 		nlctx->wildcard_unsupported = true;
+ 
+-	return !(nlctx->ops_flags[cmd] & cap);
++	return !(nlctx->ops_info[cmd].op_flags & cap);
+ }
+ 
+ /* initialization */
+@@ -140,12 +140,12 @@ bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd,
+ static int genl_read_ops(struct nl_context *nlctx,
+ 			 const struct nlattr *ops_attr)
+ {
++	struct nl_op_info *ops_info;
+ 	struct nlattr *op_attr;
+-	uint32_t *ops_flags;
+ 	int ret;
+ 
+-	ops_flags = calloc(__ETHTOOL_MSG_USER_CNT, sizeof(ops_flags[0]));
+-	if (!ops_flags)
++	ops_info = calloc(__ETHTOOL_MSG_USER_CNT, sizeof(ops_info[0]));
++	if (!ops_info)
+ 		return -ENOMEM;
+ 
+ 	mnl_attr_for_each_nested(op_attr, ops_attr) {
+@@ -163,13 +163,14 @@ static int genl_read_ops(struct nl_context *nlctx,
+ 		if (op_id >= __ETHTOOL_MSG_USER_CNT)
+ 			continue;
+ 
+-		ops_flags[op_id] = mnl_attr_get_u32(tb[CTRL_ATTR_OP_FLAGS]);
++		ops_info[op_id].op_flags =
++			mnl_attr_get_u32(tb[CTRL_ATTR_OP_FLAGS]);
+ 	}
+ 
+-	nlctx->ops_flags = ops_flags;
++	nlctx->ops_info = ops_info;
+ 	return 0;
+ err:
+-	free(ops_flags);
++	free(ops_info);
+ 	return ret;
+ }
+ 
+@@ -273,7 +274,7 @@ int netlink_init(struct cmd_context *ctx)
+ out_nlsk:
+ 	nlsock_done(nlctx->ethnl_socket);
+ out_free:
+-	free(nlctx->ops_flags);
++	free(nlctx->ops_info);
+ 	free(nlctx);
+ 	return ret;
+ }
+@@ -283,7 +284,7 @@ static void netlink_done(struct cmd_context *ctx)
+ 	if (!ctx->nlctx)
+ 		return;
+ 
+-	free(ctx->nlctx->ops_flags);
++	free(ctx->nlctx->ops_info);
+ 	free(ctx->nlctx);
+ 	ctx->nlctx = NULL;
+ 	cleanup_all_strings();
+diff --git a/netlink/netlink.h b/netlink/netlink.h
+index dd4a02bcc916..61a072db8ed9 100644
+--- a/netlink/netlink.h
++++ b/netlink/netlink.h
+@@ -25,6 +25,10 @@ enum link_mode_class {
+ 	LM_CLASS_FEC,
+ };
+ 
++struct nl_op_info {
++	uint32_t		op_flags;
++};
++
+ struct nl_context {
+ 	struct cmd_context	*ctx;
+ 	void			*cmd_private;
+@@ -34,7 +38,7 @@ struct nl_context {
+ 	unsigned int		suppress_nlerr;
+ 	uint16_t		ethnl_fam;
+ 	uint32_t		ethnl_mongrp;
+-	uint32_t		*ops_flags;
++	struct nl_op_info	*ops_info;
+ 	struct nl_socket	*ethnl_socket;
+ 	struct nl_socket	*ethnl2_socket;
+ 	struct nl_socket	*rtnl_socket;
+-- 
+2.26.2
+
diff --git a/SOURCES/0023-netlink-fix-use-after-free-in-netlink_run_handler.patch b/SOURCES/0023-netlink-fix-use-after-free-in-netlink_run_handler.patch
new file mode 100644
index 0000000..45cfd61
--- /dev/null
+++ b/SOURCES/0023-netlink-fix-use-after-free-in-netlink_run_handler.patch
@@ -0,0 +1,53 @@
+From ef1675823905ff09cb5e551700a124d0133648b7 Mon Sep 17 00:00:00 2001
+From: Michal Kubecek <mkubecek@suse.cz>
+Date: Mon, 9 Nov 2020 13:30:54 +0100
+Subject: [PATCH 23/26] netlink: fix use after free in netlink_run_handler()
+
+Valgrind detected use after free in netlink_run_handler(): some members of
+struct nl_context are accessed after the netlink context is freed by
+netlink_done(). Use local variables to store the two flags and check them
+instead.
+
+Fixes: 6c19c0d559c8 ("netlink: use genetlink ops information to decide about fallback")
+Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
+(cherry picked from commit 29b38ea218bd978d1950e12cc24da98215a1eeef)
+---
+ netlink/netlink.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/netlink/netlink.c b/netlink/netlink.c
+index 86dc1efdf5ce..2a12bb8b1759 100644
+--- a/netlink/netlink.c
++++ b/netlink/netlink.c
+@@ -303,6 +303,7 @@ void netlink_run_handler(struct cmd_context *ctx, nl_func_t nlfunc,
+ 			 bool no_fallback)
+ {
+ 	bool wildcard = ctx->devname && !strcmp(ctx->devname, WILDCARD_DEVNAME);
++	bool wildcard_unsupported, ioctl_fallback;
+ 	struct nl_context *nlctx;
+ 	const char *reason;
+ 	int ret;
+@@ -324,14 +325,17 @@ void netlink_run_handler(struct cmd_context *ctx, nl_func_t nlfunc,
+ 	nlctx = ctx->nlctx;
+ 
+ 	ret = nlfunc(ctx);
++	wildcard_unsupported = nlctx->wildcard_unsupported;
++	ioctl_fallback = nlctx->ioctl_fallback;
+ 	netlink_done(ctx);
+-	if (no_fallback || ret != -EOPNOTSUPP || !nlctx->ioctl_fallback) {
+-		if (nlctx->wildcard_unsupported)
++
++	if (no_fallback || ret != -EOPNOTSUPP || !ioctl_fallback) {
++		if (wildcard_unsupported)
+ 			fprintf(stderr, "%s\n",
+ 				"subcommand does not support wildcard dump");
+ 		exit(ret >= 0 ? ret : 1);
+ 	}
+-	if (nlctx->wildcard_unsupported)
++	if (wildcard_unsupported)
+ 		reason = "subcommand does not support wildcard dump";
+ 	else
+ 		reason = "kernel netlink support for subcommand missing";
+-- 
+2.26.2
+
diff --git a/SOURCES/0024-netlink-fix-leaked-instances-of-struct-nl_socket.patch b/SOURCES/0024-netlink-fix-leaked-instances-of-struct-nl_socket.patch
new file mode 100644
index 0000000..8d6b809
--- /dev/null
+++ b/SOURCES/0024-netlink-fix-leaked-instances-of-struct-nl_socket.patch
@@ -0,0 +1,64 @@
+From 5fad40a007b61092161d1e4fd0ffbbc3e7b8ba6c Mon Sep 17 00:00:00 2001
+From: Michal Kubecek <mkubecek@suse.cz>
+Date: Mon, 9 Nov 2020 13:30:57 +0100
+Subject: [PATCH 24/26] netlink: fix leaked instances of struct nl_socket
+
+Valgrind detected memory leaks caused by missing cleanup of netlink
+context's ethnl_socket, ethnl2_socket and rtnl_socket. Also, contrary to
+its description, nlsock_done() does not free struct nl_socket itself.
+Fix nlsock_done() to free the structure and use it to dispose of sockets
+pointed to by struct nl_context members.
+
+Fixes: 50efb3cdd2bb ("netlink: netlink socket wrapper and helpers")
+Fixes: 87307c30724d ("netlink: initialize ethtool netlink socket")
+Fixes: 7f3585b22a4b ("netlink: add handler for permaddr (-P)")
+Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
+(cherry picked from commit d85f57499d75da5c98b73652488f3f62c6f6d8ea)
+---
+ netlink/netlink.c | 11 ++++++++---
+ netlink/nlsock.c  |  3 +++
+ 2 files changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/netlink/netlink.c b/netlink/netlink.c
+index 2a12bb8b1759..5677274c2fce 100644
+--- a/netlink/netlink.c
++++ b/netlink/netlink.c
+@@ -281,11 +281,16 @@ out_free:
+ 
+ static void netlink_done(struct cmd_context *ctx)
+ {
+-	if (!ctx->nlctx)
++	struct nl_context *nlctx = ctx->nlctx;
++
++	if (!nlctx)
+ 		return;
+ 
+-	free(ctx->nlctx->ops_info);
+-	free(ctx->nlctx);
++	nlsock_done(nlctx->ethnl_socket);
++	nlsock_done(nlctx->ethnl2_socket);
++	nlsock_done(nlctx->rtnl_socket);
++	free(nlctx->ops_info);
++	free(nlctx);
+ 	ctx->nlctx = NULL;
+ 	cleanup_all_strings();
+ }
+diff --git a/netlink/nlsock.c b/netlink/nlsock.c
+index ef31d8c33b29..0ec2738d81d2 100644
+--- a/netlink/nlsock.c
++++ b/netlink/nlsock.c
+@@ -395,8 +395,11 @@ out_msgbuff:
+  */
+ void nlsock_done(struct nl_socket *nlsk)
+ {
++	if (!nlsk)
++		return;
+ 	if (nlsk->sk)
+ 		mnl_socket_close(nlsk->sk);
+ 	msgbuff_done(&nlsk->msgbuff);
+ 	memset(nlsk, '\0', sizeof(*nlsk));
++	free(nlsk);
+ }
+-- 
+2.26.2
+
diff --git a/SOURCES/0025-netlink-do-not-send-messages-and-process-replies-in-.patch b/SOURCES/0025-netlink-do-not-send-messages-and-process-replies-in-.patch
new file mode 100644
index 0000000..e5866c2
--- /dev/null
+++ b/SOURCES/0025-netlink-do-not-send-messages-and-process-replies-in-.patch
@@ -0,0 +1,301 @@
+From fc5f2a6bb2911a951bf5a1364dc5732e521d735a Mon Sep 17 00:00:00 2001
+From: Michal Kubecek <mkubecek@suse.cz>
+Date: Mon, 9 Nov 2020 14:29:56 +0100
+Subject: [PATCH 25/26] netlink: do not send messages and process replies in
+ nl_parser()
+
+When called with group_style = PARSER_GROUP_MSG, nl_parser() not only
+parses the command line and composes the messages but also sends them to
+kernel and processes the replies. This is inconsistent with other modes and
+also impractical as it takes the control over the process from caller where
+it belongs.
+
+Modify nl_parser() to pass composed messages back to caller (which is only
+nl_sset() at the moment) and let it send requests and process replies. This
+will be needed for an upcoming backward compatibility patch which will need
+to inspect and possibly modify one of the composed messages.
+
+Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
+(cherry picked from commit acd9730d1e794f85caf1192fe8788876e6f96305)
+---
+ netlink/cable_test.c |  2 +-
+ netlink/channels.c   |  2 +-
+ netlink/coalesce.c   |  2 +-
+ netlink/eee.c        |  2 +-
+ netlink/parser.c     | 43 ++++++++++++++++++++++++++++---------------
+ netlink/parser.h     |  3 ++-
+ netlink/pause.c      |  2 +-
+ netlink/rings.c      |  2 +-
+ netlink/settings.c   | 35 ++++++++++++++++++++++++++++++-----
+ 9 files changed, 66 insertions(+), 27 deletions(-)
+
+diff --git a/netlink/cable_test.c b/netlink/cable_test.c
+index d39b7d82efb0..e05b67c4e231 100644
+--- a/netlink/cable_test.c
++++ b/netlink/cable_test.c
+@@ -574,7 +574,7 @@ int nl_cable_test_tdr(struct cmd_context *ctx)
+ 			       ctx->devname, 0))
+ 		return -EMSGSIZE;
+ 
+-	ret = nl_parser(nlctx, tdr_params, NULL, PARSER_GROUP_NEST);
++	ret = nl_parser(nlctx, tdr_params, NULL, PARSER_GROUP_NEST, NULL);
+ 	if (ret < 0)
+ 		return ret;
+ 
+diff --git a/netlink/channels.c b/netlink/channels.c
+index c6002ceeb121..894c74bcc11a 100644
+--- a/netlink/channels.c
++++ b/netlink/channels.c
+@@ -126,7 +126,7 @@ int nl_schannels(struct cmd_context *ctx)
+ 			       ctx->devname, 0))
+ 		return -EMSGSIZE;
+ 
+-	ret = nl_parser(nlctx, schannels_params, NULL, PARSER_GROUP_NONE);
++	ret = nl_parser(nlctx, schannels_params, NULL, PARSER_GROUP_NONE, NULL);
+ 	if (ret < 0)
+ 		return 1;
+ 
+diff --git a/netlink/coalesce.c b/netlink/coalesce.c
+index 65f75cf9a8dd..0223f8e3484e 100644
+--- a/netlink/coalesce.c
++++ b/netlink/coalesce.c
+@@ -254,7 +254,7 @@ int nl_scoalesce(struct cmd_context *ctx)
+ 			       ctx->devname, 0))
+ 		return -EMSGSIZE;
+ 
+-	ret = nl_parser(nlctx, scoalesce_params, NULL, PARSER_GROUP_NONE);
++	ret = nl_parser(nlctx, scoalesce_params, NULL, PARSER_GROUP_NONE, NULL);
+ 	if (ret < 0)
+ 		return 1;
+ 
+diff --git a/netlink/eee.c b/netlink/eee.c
+index d3135b2094a4..04d8f0bbe3fc 100644
+--- a/netlink/eee.c
++++ b/netlink/eee.c
+@@ -174,7 +174,7 @@ int nl_seee(struct cmd_context *ctx)
+ 			       ctx->devname, 0))
+ 		return -EMSGSIZE;
+ 
+-	ret = nl_parser(nlctx, seee_params, NULL, PARSER_GROUP_NONE);
++	ret = nl_parser(nlctx, seee_params, NULL, PARSER_GROUP_NONE, NULL);
+ 	if (ret < 0)
+ 		return 1;
+ 
+diff --git a/netlink/parser.c b/netlink/parser.c
+index 3b25f5d5a88e..c2eae93efb69 100644
+--- a/netlink/parser.c
++++ b/netlink/parser.c
+@@ -920,7 +920,7 @@ static void __parser_set(uint64_t *map, unsigned int idx)
+ }
+ 
+ struct tmp_buff {
+-	struct nl_msg_buff	msgbuff;
++	struct nl_msg_buff	*msgbuff;
+ 	unsigned int		id;
+ 	unsigned int		orig_len;
+ 	struct tmp_buff		*next;
+@@ -951,7 +951,12 @@ static struct tmp_buff *tmp_buff_find_or_create(struct tmp_buff **phead,
+ 	if (!new_buff)
+ 		return NULL;
+ 	new_buff->id = id;
+-	msgbuff_init(&new_buff->msgbuff);
++	new_buff->msgbuff = malloc(sizeof(*new_buff->msgbuff));
++	if (!new_buff->msgbuff) {
++		free(new_buff);
++		return NULL;
++	}
++	msgbuff_init(new_buff->msgbuff);
+ 	new_buff->next = NULL;
+ 	*pbuff = new_buff;
+ 
+@@ -965,7 +970,10 @@ static void tmp_buff_destroy(struct tmp_buff *head)
+ 
+ 	while (buff) {
+ 		next = buff->next;
+-		msgbuff_done(&buff->msgbuff);
++		if (buff->msgbuff) {
++			msgbuff_done(buff->msgbuff);
++			free(buff->msgbuff);
++		}
+ 		free(buff);
+ 		buff = next;
+ 	}
+@@ -980,13 +988,22 @@ static void tmp_buff_destroy(struct tmp_buff *head)
+  *               param_parser::offset)
+  * @group_style: defines if identifiers in .group represent separate messages,
+  *               nested attributes or are not allowed
++ * @msgbuffs:    (only used for @group_style = PARSER_GROUP_MSG) array to store
++ *               pointers to composed messages; caller must make sure this
++ *               array is sufficient, i.e. that it has at least as many entries
++ *               as the number of different .group values in params array;
++ *               entries are filled from the start, remaining entries are not
++ *               modified; caller should zero initialize the array before
++ *               calling nl_parser()
+  */
+ int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
+-	      void *dest, enum parser_group_style group_style)
++	      void *dest, enum parser_group_style group_style,
++	      struct nl_msg_buff **msgbuffs)
+ {
+ 	struct nl_socket *nlsk = nlctx->ethnl_socket;
+ 	const struct param_parser *parser;
+ 	struct tmp_buff *buffs = NULL;
++	unsigned int n_msgbuffs = 0;
+ 	struct tmp_buff *buff;
+ 	unsigned int n_params;
+ 	uint64_t *params_seen;
+@@ -1004,7 +1021,7 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
+ 		buff = tmp_buff_find_or_create(&buffs, parser->group);
+ 		if (!buff)
+ 			goto out_free_buffs;
+-		msgbuff = &buff->msgbuff;
++		msgbuff = buff->msgbuff;
+ 		ret = msg_init(nlctx, msgbuff, parser->group,
+ 			       NLM_F_REQUEST | NLM_F_ACK);
+ 		if (ret < 0)
+@@ -1013,7 +1030,7 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
+ 		switch (group_style) {
+ 		case PARSER_GROUP_NEST:
+ 			ret = -EMSGSIZE;
+-			nest = ethnla_nest_start(&buff->msgbuff, parser->group);
++			nest = ethnla_nest_start(buff->msgbuff, parser->group);
+ 			if (!nest)
+ 				goto out_free_buffs;
+ 			break;
+@@ -1062,7 +1079,7 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
+ 		buff = NULL;
+ 		if (parser->group)
+ 			buff = tmp_buff_find(buffs, parser->group);
+-		msgbuff = buff ? &buff->msgbuff : &nlsk->msgbuff;
++		msgbuff = buff ? buff->msgbuff : &nlsk->msgbuff;
+ 
+ 		param_dest = dest ? ((char *)dest + parser->dest_offset) : NULL;
+ 		ret = parser->handler(nlctx, parser->type, parser->handler_data,
+@@ -1074,12 +1091,12 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
+ 	if (group_style == PARSER_GROUP_MSG) {
+ 		ret = -EOPNOTSUPP;
+ 		for (buff = buffs; buff; buff = buff->next)
+-			if (msgbuff_len(&buff->msgbuff) > buff->orig_len &&
++			if (msgbuff_len(buff->msgbuff) > buff->orig_len &&
+ 			    netlink_cmd_check(nlctx->ctx, buff->id, false))
+ 				goto out_free;
+ 	}
+ 	for (buff = buffs; buff; buff = buff->next) {
+-		struct nl_msg_buff *msgbuff = &buff->msgbuff;
++		struct nl_msg_buff *msgbuff = buff->msgbuff;
+ 
+ 		if (group_style == PARSER_GROUP_NONE ||
+ 		    msgbuff_len(msgbuff) == buff->orig_len)
+@@ -1092,12 +1109,8 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
+ 				goto out_free;
+ 			break;
+ 		case PARSER_GROUP_MSG:
+-			ret = nlsock_sendmsg(nlsk, msgbuff);
+-			if (ret < 0)
+-				goto out_free;
+-			ret = nlsock_process_reply(nlsk, nomsg_reply_cb, NULL);
+-			if (ret < 0)
+-				goto out_free;
++			msgbuffs[n_msgbuffs++] = msgbuff;
++			buff->msgbuff = NULL;
+ 			break;
+ 		default:
+ 			break;
+diff --git a/netlink/parser.h b/netlink/parser.h
+index fd55bc768d42..28f26ccc2a1c 100644
+--- a/netlink/parser.h
++++ b/netlink/parser.h
+@@ -143,6 +143,7 @@ int nl_parse_char_bitset(struct nl_context *nlctx, uint16_t type,
+ 
+ /* main entry point called to parse the command line */
+ int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
+-	      void *dest, enum parser_group_style group_style);
++	      void *dest, enum parser_group_style group_style,
++	      struct nl_msg_buff **msgbuffs);
+ 
+ #endif /* ETHTOOL_NETLINK_PARSER_H__ */
+diff --git a/netlink/pause.c b/netlink/pause.c
+index 7b6b3a1d2c10..048320b123d2 100644
+--- a/netlink/pause.c
++++ b/netlink/pause.c
+@@ -208,7 +208,7 @@ int nl_spause(struct cmd_context *ctx)
+ 			       ctx->devname, 0))
+ 		return -EMSGSIZE;
+ 
+-	ret = nl_parser(nlctx, spause_params, NULL, PARSER_GROUP_NONE);
++	ret = nl_parser(nlctx, spause_params, NULL, PARSER_GROUP_NONE, NULL);
+ 	if (ret < 0)
+ 		return 1;
+ 
+diff --git a/netlink/rings.c b/netlink/rings.c
+index 4061520212d5..b8c458fce25f 100644
+--- a/netlink/rings.c
++++ b/netlink/rings.c
+@@ -126,7 +126,7 @@ int nl_sring(struct cmd_context *ctx)
+ 			       ctx->devname, 0))
+ 		return -EMSGSIZE;
+ 
+-	ret = nl_parser(nlctx, sring_params, NULL, PARSER_GROUP_NONE);
++	ret = nl_parser(nlctx, sring_params, NULL, PARSER_GROUP_NONE, NULL);
+ 	if (ret < 0)
+ 		return 1;
+ 
+diff --git a/netlink/settings.c b/netlink/settings.c
+index 75db15e5069c..fac192e2fbb7 100644
+--- a/netlink/settings.c
++++ b/netlink/settings.c
+@@ -1108,9 +1108,16 @@ static const struct param_parser sset_params[] = {
+ 	{}
+ };
+ 
++/* Maximum number of request messages sent to kernel; must be equal to the
++ * number of different .group values in sset_params[] array.
++ */
++#define SSET_MAX_MSGS 4
++
+ int nl_sset(struct cmd_context *ctx)
+ {
++	struct nl_msg_buff *msgbuffs[SSET_MAX_MSGS] = {};
+ 	struct nl_context *nlctx = ctx->nlctx;
++	unsigned int i;
+ 	int ret;
+ 
+ 	nlctx->cmd = "-s";
+@@ -1118,11 +1125,29 @@ int nl_sset(struct cmd_context *ctx)
+ 	nlctx->argc = ctx->argc;
+ 	nlctx->devname = ctx->devname;
+ 
+-	ret = nl_parser(nlctx, sset_params, NULL, PARSER_GROUP_MSG);
+-	if (ret < 0)
+-		return 1;
++	ret = nl_parser(nlctx, sset_params, NULL, PARSER_GROUP_MSG, msgbuffs);
++	if (ret < 0) {
++		ret = 1;
++		goto out_free;
++	}
++
++	for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) {
++		struct nl_socket *nlsk = nlctx->ethnl_socket;
+ 
+-	if (ret == 0)
+-		return 0;
++		ret = nlsock_sendmsg(nlsk, msgbuffs[i]);
++		if (ret < 0)
++			goto out_free;
++		ret = nlsock_process_reply(nlsk, nomsg_reply_cb, NULL);
++		if (ret < 0)
++			goto out_free;
++	}
++
++out_free:
++	for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) {
++		msgbuff_done(msgbuffs[i]);
++		free(msgbuffs[i]);
++	}
++	if (ret >= 0)
++		return ret;
+ 	return nlctx->exit_code ?: 75;
+ }
+-- 
+2.26.2
+
diff --git a/SOURCES/0026-ethtool-Improve-compatibility-between-netlink-and-io.patch b/SOURCES/0026-ethtool-Improve-compatibility-between-netlink-and-io.patch
new file mode 100644
index 0000000..bb2e9da
--- /dev/null
+++ b/SOURCES/0026-ethtool-Improve-compatibility-between-netlink-and-io.patch
@@ -0,0 +1,191 @@
+From f16bc54fe82b9129d6852273d02e044b9cb28789 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Mon, 9 Nov 2020 14:29:59 +0100
+Subject: [PATCH 26/26] ethtool: Improve compatibility between netlink and
+ ioctl interfaces
+
+With the ioctl interface, when autoneg is enabled, but without
+specifying speed, duplex or link modes, the advertised link modes are
+set to the supported link modes by the ethtool user space utility.
+
+This does not happen when using the netlink interface. Fix this
+incompatibility problem by having ethtool query the supported link modes
+from the kernel and advertise all the "real" ones when only "autoneg on"
+is specified.
+
+Before:
+
+Settings for eth0:
+	Supported ports: [ TP ]
+	Supported link modes:   10baseT/Half 10baseT/Full
+	                        100baseT/Half 100baseT/Full
+	                        1000baseT/Full
+	Supported pause frame use: No
+	Supports auto-negotiation: Yes
+	Supported FEC modes: Not reported
+	Advertised link modes:  100baseT/Half 100baseT/Full
+	Advertised pause frame use: No
+	Advertised auto-negotiation: Yes
+	Advertised FEC modes: Not reported
+	Speed: 1000Mb/s
+	Duplex: Full
+	Auto-negotiation: on
+	Port: Twisted Pair
+	PHYAD: 0
+	Transceiver: internal
+	MDI-X: off (auto)
+	Supports Wake-on: umbg
+	Wake-on: d
+        Current message level: 0x00000007 (7)
+                               drv probe link
+	Link detected: yes
+
+After:
+
+Settings for eth0:
+	Supported ports: [ TP ]
+	Supported link modes:   10baseT/Half 10baseT/Full
+	                        100baseT/Half 100baseT/Full
+	                        1000baseT/Full
+	Supported pause frame use: No
+	Supports auto-negotiation: Yes
+	Supported FEC modes: Not reported
+	Advertised link modes:  10baseT/Half 10baseT/Full
+	                        100baseT/Half 100baseT/Full
+	                        1000baseT/Full
+	Advertised pause frame use: No
+	Advertised auto-negotiation: Yes
+	Advertised FEC modes: Not reported
+	Speed: 1000Mb/s
+	Duplex: Full
+	Auto-negotiation: on
+	Port: Twisted Pair
+	PHYAD: 0
+	Transceiver: internal
+	MDI-X: on (auto)
+	Supports Wake-on: umbg
+	Wake-on: d
+        Current message level: 0x00000007 (7)
+                               drv probe link
+	Link detected: yes
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
+(cherry picked from commit 124a3c06d1c34b125d84a9eb312fddd365bb7bf6)
+---
+ netlink/settings.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 92 insertions(+)
+
+diff --git a/netlink/settings.c b/netlink/settings.c
+index fac192e2fbb7..01c1d38d323f 100644
+--- a/netlink/settings.c
++++ b/netlink/settings.c
+@@ -1113,6 +1113,93 @@ static const struct param_parser sset_params[] = {
+  */
+ #define SSET_MAX_MSGS 4
+ 
++static int linkmodes_reply_advert_all_cb(const struct nlmsghdr *nlhdr,
++					 void *data)
++{
++	const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
++	DECLARE_ATTR_TB_INFO(tb);
++	struct nl_msg_buff *req_msgbuff = data;
++	const struct nlattr *ours_attr;
++	struct nlattr *req_bitset;
++	uint32_t *supported_modes;
++	unsigned int modes_count;
++	unsigned int i;
++	int ret;
++
++	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
++	if (ret < 0)
++		return MNL_CB_ERROR;
++	ours_attr = tb[ETHTOOL_A_LINKMODES_OURS];
++	if (!ours_attr)
++		return MNL_CB_ERROR;
++	modes_count = bitset_get_count(tb[ETHTOOL_A_LINKMODES_OURS], &ret);
++	if (ret < 0)
++		return MNL_CB_ERROR;
++	supported_modes = get_compact_bitset_mask(tb[ETHTOOL_A_LINKMODES_OURS]);
++	if (!supported_modes)
++		return MNL_CB_ERROR;
++
++	/* keep only "real" link modes */
++	for (i = 0; i < modes_count; i++)
++		if (!lm_class_match(i, LM_CLASS_REAL))
++			supported_modes[i / 32] &= ~((uint32_t)1 << (i % 32));
++
++	req_bitset = ethnla_nest_start(req_msgbuff, ETHTOOL_A_LINKMODES_OURS);
++	if (!req_bitset)
++		return MNL_CB_ERROR;
++
++	if (ethnla_put_u32(req_msgbuff, ETHTOOL_A_BITSET_SIZE, modes_count) ||
++	    ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_VALUE,
++		       DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t),
++		       supported_modes) ||
++	    ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_MASK,
++		       DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t),
++		       supported_modes)) {
++		ethnla_nest_cancel(req_msgbuff, req_bitset);
++		return MNL_CB_ERROR;
++	}
++
++	ethnla_nest_end(req_msgbuff, req_bitset);
++	return MNL_CB_OK;
++}
++
++/* For compatibility reasons with ioctl-based ethtool, when "autoneg on" is
++ * specified without "advertise", "speed" and "duplex", we need to query the
++ * supported link modes from the kernel and advertise all the "real" ones.
++ */
++static int nl_sset_compat_linkmodes(struct nl_context *nlctx,
++				    struct nl_msg_buff *msgbuff)
++{
++	const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
++	DECLARE_ATTR_TB_INFO(tb);
++	struct nl_socket *nlsk = nlctx->ethnl_socket;
++	int ret;
++
++	ret = mnl_attr_parse(msgbuff->nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
++	if (ret < 0)
++		return ret;
++	if (!tb[ETHTOOL_A_LINKMODES_AUTONEG] || tb[ETHTOOL_A_LINKMODES_OURS] ||
++	    tb[ETHTOOL_A_LINKMODES_SPEED] || tb[ETHTOOL_A_LINKMODES_DUPLEX])
++		return 0;
++	if (!mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_AUTONEG]))
++		return 0;
++
++	/* all conditions satisfied, create ETHTOOL_A_LINKMODES_OURS */
++	if (netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_GET, false) ||
++	    netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_SET, false))
++		return -EOPNOTSUPP;
++	ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_LINKMODES_GET,
++				      ETHTOOL_A_LINKMODES_HEADER,
++				      ETHTOOL_FLAG_COMPACT_BITSETS);
++	if (ret < 0)
++		return ret;
++	ret = nlsock_sendmsg(nlsk, NULL);
++	if (ret < 0)
++		return ret;
++	return nlsock_process_reply(nlsk, linkmodes_reply_advert_all_cb,
++				    msgbuff);
++}
++
+ int nl_sset(struct cmd_context *ctx)
+ {
+ 	struct nl_msg_buff *msgbuffs[SSET_MAX_MSGS] = {};
+@@ -1134,6 +1221,11 @@ int nl_sset(struct cmd_context *ctx)
+ 	for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) {
+ 		struct nl_socket *nlsk = nlctx->ethnl_socket;
+ 
++		if (msgbuffs[i]->genlhdr->cmd == ETHTOOL_MSG_LINKMODES_SET) {
++			ret = nl_sset_compat_linkmodes(nlctx, msgbuffs[i]);
++			if (ret < 0)
++				goto out_free;
++		}
+ 		ret = nlsock_sendmsg(nlsk, msgbuffs[i]);
+ 		if (ret < 0)
+ 			goto out_free;
+-- 
+2.26.2
+
diff --git a/SPECS/ethtool.spec b/SPECS/ethtool.spec
index 2ad4bbd..874fba0 100644
--- a/SPECS/ethtool.spec
+++ b/SPECS/ethtool.spec
@@ -1,7 +1,7 @@
 Name:		ethtool
 Epoch:		2
 Version:	5.8
-Release:	3%{?dist}
+Release:	5%{?dist}
 Summary:	Settings tool for Ethernet NICs
 License:	GPLv2
 Group:		Applications/System
@@ -26,6 +26,13 @@ Patch16:	0016-netlink-return-ENOMEM-when-calloc-fails.patch
 Patch17:	0017-netlink-fix-memory-leak.patch
 Patch18:	0018-fix-memory-leaks-in-do_sfeatures.patch
 Patch19:	0019-netlink-fix-copy-paste-error-in-rtm_link_summary.patch
+Patch20:	0020-uapi-linux-update-kernel-UAPI-header-files.patch
+Patch21:	0021-netlink-settings-expand-linkstate_reply_cb-to-suppor.patch
+Patch22:	0022-netlink-prepare-for-more-per-op-info.patch
+Patch23:	0023-netlink-fix-use-after-free-in-netlink_run_handler.patch
+Patch24:	0024-netlink-fix-leaked-instances-of-struct-nl_socket.patch
+Patch25:	0025-netlink-do-not-send-messages-and-process-replies-in-.patch
+Patch26:	0026-ethtool-Improve-compatibility-between-netlink-and-io.patch
 BuildRequires:	libmnl-devel
 Conflicts:	filesystem < 3
 
@@ -55,6 +62,13 @@ network devices, especially of Ethernet devices.
 %patch17 -p1
 %patch18 -p1
 %patch19 -p1
+%patch20 -p1
+%patch21 -p1
+%patch22 -p1
+%patch23 -p1
+%patch24 -p1
+%patch25 -p1
+%patch26 -p1
 
 %build
 %configure
@@ -71,6 +85,12 @@ make DESTDIR=%{buildroot} INSTALL='install -p' install
 %{_datadir}/bash-completion/completions/ethtool
 
 %changelog
+* Thu Nov 12 2020 Ivan Vecera <ivecera@redhat.com> - 2:5.8-5
+- Fixed a regression
+
+* Fri Nov 06 2020 Ivan Vecera <ivecera@redhat.com> - 2:5.8-4
+- Added support for extended link state reporting
+
 * Fri Sep 25 2020 Ivan Vecera <ivecera@redhat.com> - 2:5.8-3
 - Fixed memory leak