diff --git a/include/libgenl.h b/include/libgenl.h
index 656493a2c3c4..97281cc1103f 100644
--- a/include/libgenl.h
+++ b/include/libgenl.h
@@ -21,6 +21,7 @@ struct {								\
 	},								\
 }
 
+int genl_add_mcast_grp(struct rtnl_handle *grth, __u16 genl_family, const char *group);
 int genl_resolve_family(struct rtnl_handle *grth, const char *family);
 int genl_init_handle(struct rtnl_handle *grth, const char *family,
 		     int *genl_family);
diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h
index c3e40165ad4b..d7d47802bc98 100644
--- a/include/uapi/linux/mptcp.h
+++ b/include/uapi/linux/mptcp.h
@@ -174,6 +174,8 @@ enum mptcp_event_attr {
 	MPTCP_ATTR_FLAGS,	/* u16 */
 	MPTCP_ATTR_TIMEOUT,	/* u32 */
 	MPTCP_ATTR_IF_IDX,	/* s32 */
+	MPTCP_ATTR_RESET_REASON,/* u32 */
+	MPTCP_ATTR_RESET_FLAGS, /* u32 */
 
 	__MPTCP_ATTR_AFTER_LAST
 };
diff --git a/ip/ipmptcp.c b/ip/ipmptcp.c
index e1ffafb3c658..fa8481069124 100644
--- a/ip/ipmptcp.c
+++ b/ip/ipmptcp.c
@@ -23,6 +23,7 @@ static void usage(void)
 		"	ip mptcp endpoint flush\n"
 		"	ip mptcp limits set [ subflows NR ] [ add_addr_accepted NR ]\n"
 		"	ip mptcp limits show\n"
+		"	ip mptcp monitor\n"
 		"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
 		"FLAG  := [ signal | subflow | backup ]\n");
 
@@ -385,6 +386,110 @@ static int mptcp_limit_get_set(int argc, char **argv, int cmd)
 	return 0;
 }
 
+static const char * const event_to_str[] = {
+	[MPTCP_EVENT_CREATED] = "CREATED",
+	[MPTCP_EVENT_ESTABLISHED] = "ESTABLISHED",
+	[MPTCP_EVENT_CLOSED] = "CLOSED",
+	[MPTCP_EVENT_ANNOUNCED] = "ANNOUNCED",
+	[MPTCP_EVENT_REMOVED] = "REMOVED",
+	[MPTCP_EVENT_SUB_ESTABLISHED] = "SF_ESTABLISHED",
+	[MPTCP_EVENT_SUB_CLOSED] = "SF_CLOSED",
+	[MPTCP_EVENT_SUB_PRIORITY] = "SF_PRIO",
+};
+
+static void print_addr(const char *key, int af, struct rtattr *value)
+{
+	void *data = RTA_DATA(value);
+	char str[INET6_ADDRSTRLEN];
+
+	if (inet_ntop(af, data, str, sizeof(str)))
+		printf(" %s=%s", key, str);
+}
+
+static int mptcp_monitor_msg(struct rtnl_ctrl_data *ctrl,
+			     struct nlmsghdr *n, void *arg)
+{
+	const struct genlmsghdr *ghdr = NLMSG_DATA(n);
+	struct rtattr *tb[MPTCP_ATTR_MAX + 1];
+	int len = n->nlmsg_len;
+
+	len -= NLMSG_LENGTH(GENL_HDRLEN);
+	if (len < 0)
+		return -1;
+
+	if (n->nlmsg_type != genl_family)
+		return 0;
+
+	if (timestamp)
+		print_timestamp(stdout);
+
+	if (ghdr->cmd >= ARRAY_SIZE(event_to_str)) {
+		printf("[UNKNOWN %u]\n", ghdr->cmd);
+		goto out;
+	}
+
+	if (event_to_str[ghdr->cmd] == NULL) {
+		printf("[UNKNOWN %u]\n", ghdr->cmd);
+		goto out;
+	}
+
+	printf("[%14s]", event_to_str[ghdr->cmd]);
+
+	parse_rtattr(tb, MPTCP_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
+
+	printf(" token=%08x", rta_getattr_u32(tb[MPTCP_ATTR_TOKEN]));
+
+	if (tb[MPTCP_ATTR_REM_ID])
+		printf(" remid=%u", rta_getattr_u8(tb[MPTCP_ATTR_REM_ID]));
+	if (tb[MPTCP_ATTR_LOC_ID])
+		printf(" locid=%u", rta_getattr_u8(tb[MPTCP_ATTR_LOC_ID]));
+
+	if (tb[MPTCP_ATTR_SADDR4])
+		print_addr("saddr4", AF_INET, tb[MPTCP_ATTR_SADDR4]);
+	if (tb[MPTCP_ATTR_DADDR4])
+		print_addr("daddr4", AF_INET, tb[MPTCP_ATTR_DADDR4]);
+	if (tb[MPTCP_ATTR_SADDR6])
+		print_addr("saddr6", AF_INET6, tb[MPTCP_ATTR_SADDR6]);
+	if (tb[MPTCP_ATTR_DADDR6])
+		print_addr("daddr6", AF_INET6, tb[MPTCP_ATTR_DADDR6]);
+	if (tb[MPTCP_ATTR_SPORT])
+		printf(" sport=%u", rta_getattr_be16(tb[MPTCP_ATTR_SPORT]));
+	if (tb[MPTCP_ATTR_DPORT])
+		printf(" dport=%u", rta_getattr_be16(tb[MPTCP_ATTR_DPORT]));
+	if (tb[MPTCP_ATTR_BACKUP])
+		printf(" backup=%d", rta_getattr_u8(tb[MPTCP_ATTR_BACKUP]));
+	if (tb[MPTCP_ATTR_ERROR])
+		printf(" error=%d", rta_getattr_u8(tb[MPTCP_ATTR_ERROR]));
+	if (tb[MPTCP_ATTR_FLAGS])
+		printf(" flags=%x", rta_getattr_u16(tb[MPTCP_ATTR_FLAGS]));
+	if (tb[MPTCP_ATTR_TIMEOUT])
+		printf(" timeout=%u", rta_getattr_u32(tb[MPTCP_ATTR_TIMEOUT]));
+	if (tb[MPTCP_ATTR_IF_IDX])
+		printf(" ifindex=%d", rta_getattr_s32(tb[MPTCP_ATTR_IF_IDX]));
+	if (tb[MPTCP_ATTR_RESET_REASON])
+		printf(" reset_reason=%u", rta_getattr_u32(tb[MPTCP_ATTR_RESET_REASON]));
+	if (tb[MPTCP_ATTR_RESET_FLAGS])
+		printf(" reset_flags=0x%x", rta_getattr_u32(tb[MPTCP_ATTR_RESET_FLAGS]));
+
+	puts("");
+out:
+	fflush(stdout);
+	return 0;
+}
+
+static int mptcp_monitor(void)
+{
+	if (genl_add_mcast_grp(&genl_rth, genl_family, MPTCP_PM_EV_GRP_NAME) < 0) {
+		perror("can't subscribe to mptcp events");
+		return 1;
+	}
+
+	if (rtnl_listen(&genl_rth, mptcp_monitor_msg, stdout) < 0)
+		return 2;
+
+	return 0;
+}
+
 int do_mptcp(int argc, char **argv)
 {
 	if (argc == 0)
@@ -429,6 +534,14 @@ int do_mptcp(int argc, char **argv)
 						   MPTCP_PM_CMD_GET_LIMITS);
 	}
 
+	if (matches(*argv, "monitor") == 0) {
+		NEXT_ARG_FWD();
+		if (argc == 0)
+			return mptcp_monitor();
+
+		goto unknown;
+	}
+
 unknown:
 	fprintf(stderr, "Command \"%s\" is unknown, try \"ip mptcp help\".\n",
 		*argv);
diff --git a/lib/libgenl.c b/lib/libgenl.c
index f2ce698fc711..4c51d47af46b 100644
--- a/lib/libgenl.c
+++ b/lib/libgenl.c
@@ -67,6 +67,72 @@ int genl_resolve_family(struct rtnl_handle *grth, const char *family)
 	return fnum;
 }
 
+static int genl_parse_grps(struct rtattr *attr, const char *name, unsigned int *id)
+{
+	const struct rtattr *pos;
+
+	rtattr_for_each_nested(pos, attr) {
+		struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
+
+		parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, pos);
+
+		if (tb[CTRL_ATTR_MCAST_GRP_NAME] && tb[CTRL_ATTR_MCAST_GRP_ID]) {
+			if (strcmp(name, rta_getattr_str(tb[CTRL_ATTR_MCAST_GRP_NAME])) == 0) {
+				*id = rta_getattr_u32(tb[CTRL_ATTR_MCAST_GRP_ID]);
+				return 0;
+			}
+		}
+	}
+
+	return -1;
+}
+
+int genl_add_mcast_grp(struct rtnl_handle *grth, __u16 fnum, const char *group)
+{
+	GENL_REQUEST(req, 1024, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY,
+		     NLM_F_REQUEST);
+	struct rtattr *tb[CTRL_ATTR_MAX + 1];
+	struct nlmsghdr *answer = NULL;
+	struct genlmsghdr *ghdr;
+	struct rtattr *attrs;
+	int len, ret = -1;
+	unsigned int id;
+
+	addattr16(&req.n, sizeof(req), CTRL_ATTR_FAMILY_ID, fnum);
+
+	if (rtnl_talk(grth, &req.n, &answer) < 0) {
+		fprintf(stderr, "Error talking to the kernel\n");
+		return -2;
+	}
+
+	ghdr = NLMSG_DATA(answer);
+	len = answer->nlmsg_len;
+
+	if (answer->nlmsg_type != GENL_ID_CTRL)
+		goto err_free;
+
+	len -= NLMSG_LENGTH(GENL_HDRLEN);
+	if (len < 0)
+		goto err_free;
+
+	attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
+	parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
+
+	if (tb[CTRL_ATTR_MCAST_GROUPS] == NULL) {
+		fprintf(stderr, "Missing mcast groups TLV\n");
+		goto err_free;
+	}
+
+	if (genl_parse_grps(tb[CTRL_ATTR_MCAST_GROUPS], group, &id) < 0)
+		goto err_free;
+
+	ret = rtnl_add_nl_group(grth, id);
+
+err_free:
+	free(answer);
+	return ret;
+}
+
 int genl_init_handle(struct rtnl_handle *grth, const char *family,
 		     int *genl_family)
 {
diff --git a/man/man8/ip-mptcp.8 b/man/man8/ip-mptcp.8
index ef8409ea4a24..b02cf03e757d 100644
--- a/man/man8/ip-mptcp.8
+++ b/man/man8/ip-mptcp.8
@@ -65,6 +65,9 @@ ip-mptcp \- MPTCP path manager configuration
 .ti -8
 .BR "ip mptcp limits show"
 
+.ti -8
+.BR "ip mptcp monitor"
+
 .SH DESCRIPTION
 
 MPTCP is a transport protocol built on top of TCP that allows TCP
@@ -137,5 +140,10 @@ each accepted ADD_ADDR option, respecting the
 .IR SUBFLOW_NR
 limit.
 
+.sp
+.PP
+.B monitor
+displays creation and deletion of MPTCP connections as well as addition or removal of remote addresses and subflows.
+
 .SH AUTHOR
 Original Manpage by Paolo Abeni <pabeni@redhat.com>
