All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management
@ 2022-01-28  0:38 Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 01/14] mptcp: add sysctl param to limit userspace PM activity Kishen Maloor
                   ` (13 more replies)
  0 siblings, 14 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This patch series brings together the base functionality
and new netlink APIs for flexible path management from userspace.
It further extends the MPTCP self-testing framework to support the new
netlink APIs along with the ability to capture MPTCP netlink events
to aid in functional/behavioral validations. Lastly, it adds a new
self-testing script with a suite of test cases covering the entire
range of the new userspace path management capabilities.

Note: This patch series depends on the prior series "mptcp: fixes and
enhancements related to path management".

v1 -> v2:
-fixed build error when IPv6 is not enabled

v2 -> v3:
-new sysctl configurable param net.mptcp.userspace_pm_local_addr_max
to control/bound userspace PM activity
-new test case to verify that net.mptcp.userspace_pm_local_addr_max is
enforced by the kernel
-verify expected value of the server_side attribute in MPTCP connection
events in the self-testing script
-incorporate new addr flag MPTCP_PM_ADDR_FLAG_NO_LISTEN and helper
lsk_list_find_or_create() in MPTCP_PM_CMD_ANNOUNCE
-added descriptive remarks throughout the self-testing script
(userspace_pm.sh)

Florian Westphal (2):
  mptcp: netlink: split mptcp_pm_parse_addr into two functions
  mptcp: netlink: allow userspace-driven subflow establishment

Kishen Maloor (12):
  mptcp: add sysctl param to limit userspace PM activity
  mptcp: allow ADD_ADDR reissuance by userspace PMs
  mptcp: handle local addrs announced by userspace PMs
  mptcp: read attributes of addr entries managed by userspace PMs
  mptcp: netlink: Add MPTCP_PM_CMD_ANNOUNCE
  mptcp: selftests: support MPTCP_PM_CMD_ANNOUNCE
  mptcp: netlink: Add MPTCP_PM_CMD_REMOVE
  mptcp: selftests: support MPTCP_PM_CMD_REMOVE
  mptcp: selftests: support MPTCP_PM_CMD_SUBFLOW_CREATE
  mptcp: selftests: support MPTCP_PM_CMD_SUBFLOW_DESTROY
  mptcp: selftests: capture netlink events
  selftests: mptcp: functional tests for the userspace PM type

 include/uapi/linux/mptcp.h                    |   7 +
 net/mptcp/ctrl.c                              |  17 +
 net/mptcp/pm.c                                |   4 +-
 net/mptcp/pm_netlink.c                        | 637 +++++++++++++++--
 net/mptcp/protocol.c                          |   2 +
 net/mptcp/protocol.h                          |   5 +-
 net/mptcp/subflow.c                           |   2 +-
 tools/testing/selftests/net/mptcp/pm_nl_ctl.c | 598 +++++++++++++++-
 .../selftests/net/mptcp/userspace_pm.sh       | 656 ++++++++++++++++++
 9 files changed, 1862 insertions(+), 66 deletions(-)
 create mode 100755 tools/testing/selftests/net/mptcp/userspace_pm.sh

-- 
2.31.1


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 01/14] mptcp: add sysctl param to limit userspace PM activity
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  6:38   ` Geliang Tang
  2022-01-28  0:38 ` [PATCH mptcp-next v3 02/14] mptcp: allow ADD_ADDR reissuance by userspace PMs Kishen Maloor
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change adds a new configurable param userspace_pm_local_addr_max
to set a limit on the number of local addresses that may be recorded
by a userspace path manager in the context of any MPTCP connection.
Consequently, it also limits the # of subflows, i.e. to as many that
could be established via the bounded set of local addresses.

The patch also ensures that other stats inside struct mptcp_pm_data
that are updated along kernel code paths exercised by userspace PMs
remain untouched for userspace PM managed sockets.

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
 net/mptcp/ctrl.c       | 17 +++++++++++++++++
 net/mptcp/pm.c         |  4 +++-
 net/mptcp/pm_netlink.c |  3 +++
 net/mptcp/protocol.h   |  1 +
 4 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c
index ae20b7d92e28..dffc2da6d713 100644
--- a/net/mptcp/ctrl.c
+++ b/net/mptcp/ctrl.c
@@ -19,6 +19,7 @@ static int mptcp_pernet_id;
 
 #ifdef CONFIG_SYSCTL
 static int mptcp_pm_type_max = __MPTCP_PM_TYPE_MAX;
+static int mptcp_max_addrs = MPTCP_PM_MAX_ADDR_ID;
 #endif
 
 struct mptcp_pernet {
@@ -32,6 +33,7 @@ struct mptcp_pernet {
 	u8 checksum_enabled;
 	u8 allow_join_initial_addr_port;
 	u8 pm_type;
+	u8 userspace_pm_local_addr_max;
 };
 
 static struct mptcp_pernet *mptcp_get_pernet(const struct net *net)
@@ -69,6 +71,11 @@ int mptcp_get_pm_type(const struct net *net)
 	return mptcp_get_pernet(net)->pm_type;
 }
 
+unsigned int mptcp_get_userspace_pm_local_addr_max(const struct net *net)
+{
+	return mptcp_get_pernet(net)->userspace_pm_local_addr_max;
+}
+
 static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
 {
 	pernet->mptcp_enabled = 1;
@@ -77,6 +84,7 @@ static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
 	pernet->allow_join_initial_addr_port = 1;
 	pernet->stale_loss_cnt = 4;
 	pernet->pm_type = MPTCP_PM_TYPE_KERNEL;
+	pernet->userspace_pm_local_addr_max = 10;
 }
 
 #ifdef CONFIG_SYSCTL
@@ -128,6 +136,14 @@ static struct ctl_table mptcp_sysctl_table[] = {
 		.extra1       = SYSCTL_ZERO,
 		.extra2       = &mptcp_pm_type_max
 	},
+	{
+		.procname = "userspace_pm_local_addr_max",
+		.maxlen = sizeof(u8),
+		.mode = 0644,
+		.proc_handler = proc_dou8vec_minmax,
+		.extra1       = SYSCTL_ONE,
+		.extra2       = &mptcp_max_addrs
+	},
 	{}
 };
 
@@ -149,6 +165,7 @@ static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
 	table[3].data = &pernet->allow_join_initial_addr_port;
 	table[4].data = &pernet->stale_loss_cnt;
 	table[5].data = &pernet->pm_type;
+	table[6].data = &pernet->userspace_pm_local_addr_max;
 
 	hdr = register_net_sysctl(net, MPTCP_SYSCTL_PATH, table);
 	if (!hdr)
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index e5d5cb847209..24839cd7025c 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -182,7 +182,9 @@ void mptcp_pm_subflow_check_next(struct mptcp_sock *msk, const struct sock *ssk,
 	bool update_subflows;
 
 	update_subflows = (ssk->sk_state == TCP_CLOSE) &&
-			  (subflow->request_join || subflow->mp_join);
+			  (subflow->request_join || subflow->mp_join) &&
+			  READ_ONCE(pm->pm_type) == MPTCP_PM_TYPE_KERNEL;
+
 	if (!READ_ONCE(pm->work_pending) && !update_subflows)
 		return;
 
diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
index 126cc961a4fd..882988e43d76 100644
--- a/net/mptcp/pm_netlink.c
+++ b/net/mptcp/pm_netlink.c
@@ -933,6 +933,9 @@ static void mptcp_pm_nl_rm_addr_or_subflow(struct mptcp_sock *msk,
 		if (!removed)
 			continue;
 
+		if (READ_ONCE(msk->pm.pm_type) != MPTCP_PM_TYPE_KERNEL)
+			continue;
+
 		if (rm_type == MPTCP_MIB_RMADDR) {
 			msk->pm.add_addr_accepted--;
 			WRITE_ONCE(msk->pm.accept_addr, true);
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 30006735afb7..6cfa8ec26482 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -852,6 +852,7 @@ unsigned int mptcp_pm_get_add_addr_signal_max(struct mptcp_sock *msk);
 unsigned int mptcp_pm_get_add_addr_accept_max(struct mptcp_sock *msk);
 unsigned int mptcp_pm_get_subflows_max(struct mptcp_sock *msk);
 unsigned int mptcp_pm_get_local_addr_max(struct mptcp_sock *msk);
+unsigned int mptcp_get_userspace_pm_local_addr_max(const struct net *net);
 
 void mptcp_sockopt_sync(struct mptcp_sock *msk, struct sock *ssk);
 void mptcp_sockopt_sync_locked(struct mptcp_sock *msk, struct sock *ssk);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 02/14] mptcp: allow ADD_ADDR reissuance by userspace PMs
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 01/14] mptcp: add sysctl param to limit userspace PM activity Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 03/14] mptcp: handle local addrs announced " Kishen Maloor
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change allows userspace PM implementations to reissue ADD_ADDR
announcements (if necessary) based on their chosen policy.

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
 net/mptcp/pm_netlink.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
index 882988e43d76..726dc0a56fca 100644
--- a/net/mptcp/pm_netlink.c
+++ b/net/mptcp/pm_netlink.c
@@ -479,8 +479,16 @@ static bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk,
 
 	lockdep_assert_held(&msk->pm.lock);
 
-	if (mptcp_lookup_anno_list_by_saddr(msk, &entry->addr))
-		return false;
+	add_entry = mptcp_lookup_anno_list_by_saddr(msk, &entry->addr);
+
+	if (add_entry) {
+		if (READ_ONCE(msk->pm.pm_type) == MPTCP_PM_TYPE_KERNEL)
+			return false;
+
+		sk_reset_timer(sk, &add_entry->add_timer,
+			       jiffies + mptcp_get_add_addr_timeout(net));
+		return true;
+	}
 
 	add_entry = kmalloc(sizeof(*add_entry), GFP_ATOMIC);
 	if (!add_entry)
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 03/14] mptcp: handle local addrs announced by userspace PMs
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 01/14] mptcp: add sysctl param to limit userspace PM activity Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 02/14] mptcp: allow ADD_ADDR reissuance by userspace PMs Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-02-01 11:58   ` Paolo Abeni
  2022-01-28  0:38 ` [PATCH mptcp-next v3 04/14] mptcp: read attributes of addr entries managed " Kishen Maloor
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change adds a new internal function to store/retrieve local
addrs announced by userspace PM implementations to/from its kernel
context. The function captures the requirements of three scenarios:
1) ADD_ADDR announcements (which require that a local id be
provided), 2) retrieving the local id associated with an address,
also where one may need to be assigned, and 3) reissuance of
ADD_ADDRs when there's a successful match of addr/id.

The list of all stored local addr entries is held under the
MPTCP sock structure. This list, if not released by the REMOVE_ADDR
flow is freed while the sock is destructed.

Additionally, this function enforces the kernel imposed limit on
the number of local addresses that may be used over a connection.

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
v3: incorporate the new sysctl configurable limit on the # of local
addresses that may be populated by userspace PMs
---
 net/mptcp/pm_netlink.c | 88 ++++++++++++++++++++++++++++++++++++++++++
 net/mptcp/protocol.c   |  2 +
 net/mptcp/protocol.h   |  2 +
 3 files changed, 92 insertions(+)

diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
index 726dc0a56fca..ebec3610bb38 100644
--- a/net/mptcp/pm_netlink.c
+++ b/net/mptcp/pm_netlink.c
@@ -511,6 +511,34 @@ static bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk,
 	return true;
 }
 
+void mptcp_free_local_addr_list(struct mptcp_sock *msk)
+{
+	struct mptcp_pm_addr_entry *entry, *tmp;
+	struct sock *sk = (struct sock *)msk;
+	struct pm_nl_pernet *pernet;
+	LIST_HEAD(free_list);
+
+	if (READ_ONCE(msk->pm.pm_type) == MPTCP_PM_TYPE_KERNEL)
+		return;
+
+	pernet = net_generic(sock_net(sk), pm_nl_pernet_id);
+
+	pr_debug("msk=%p", msk);
+
+	mptcp_data_lock(sk);
+	list_splice_init(&msk->local_addr_list, &free_list);
+	spin_lock_bh(&msk->pm.lock);
+	msk->pm.local_addr_used = 0;
+	spin_unlock_bh(&msk->pm.lock);
+	mptcp_data_unlock(sk);
+
+	list_for_each_entry_safe(entry, tmp, &free_list, list) {
+		if (entry->lsk_ref)
+			lsk_list_release(pernet, entry->lsk_ref);
+		kfree(entry);
+	}
+}
+
 void mptcp_pm_free_anno_list(struct mptcp_sock *msk)
 {
 	struct mptcp_pm_add_entry *entry, *tmp;
@@ -1007,6 +1035,66 @@ static bool address_use_port(struct mptcp_pm_addr_entry *entry)
 		MPTCP_PM_ADDR_FLAG_SIGNAL;
 }
 
+static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk,
+						    struct mptcp_pm_addr_entry *entry)
+{
+	DECLARE_BITMAP(id_bitmap, MPTCP_PM_MAX_ADDR_ID + 1);
+	struct mptcp_pm_addr_entry *match = NULL;
+	struct sock *sk = (struct sock *)msk;
+	struct mptcp_pm_addr_entry *e;
+	bool addr_match = false;
+	bool id_match = false;
+	int ret = -EINVAL;
+
+	bitmap_zero(id_bitmap, MPTCP_PM_MAX_ADDR_ID + 1);
+
+	mptcp_data_lock(sk);
+	list_for_each_entry(e, &msk->local_addr_list, list) {
+		addr_match = addresses_equal(&e->addr, &entry->addr, true);
+		if (addr_match && entry->addr.id == 0)
+			entry->addr.id = e->addr.id;
+		id_match = (e->addr.id == entry->addr.id);
+		if (addr_match && id_match) {
+			match = e;
+			break;
+		} else if (addr_match || id_match) {
+			break;
+		}
+		__set_bit(e->addr.id, id_bitmap);
+	}
+
+	if (!match && !addr_match && !id_match) {
+		spin_lock_bh(&msk->pm.lock);
+		if (msk->pm.local_addr_used <
+		    mptcp_get_userspace_pm_local_addr_max(sock_net(sk))) {
+			e = kmalloc(sizeof(*e), GFP_ATOMIC);
+			if (!e) {
+				mptcp_data_unlock(sk);
+				return -ENOMEM;
+			}
+
+			*e = *entry;
+			if (!e->addr.id)
+				e->addr.id = find_next_zero_bit(id_bitmap,
+								MPTCP_PM_MAX_ADDR_ID + 1,
+								1);
+			list_add_tail_rcu(&e->list, &msk->local_addr_list);
+			++msk->pm.local_addr_used;
+			ret = e->addr.id;
+
+			if (e->lsk_ref && e->addr.port)
+				lsk_list_add_ref(e->lsk_ref);
+		}
+		spin_unlock_bh(&msk->pm.lock);
+	} else if (match) {
+		ret = entry->addr.id;
+	}
+
+	mptcp_data_unlock(sk);
+
+	return ret;
+}
+
 static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet,
 					     struct mptcp_pm_addr_entry *entry)
 {
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 7c591177c3e8..82b4f9b76f42 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -2540,6 +2540,7 @@ static int __mptcp_init_sock(struct sock *sk)
 	INIT_LIST_HEAD(&msk->conn_list);
 	INIT_LIST_HEAD(&msk->join_list);
 	INIT_LIST_HEAD(&msk->rtx_queue);
+	INIT_LIST_HEAD(&msk->local_addr_list);
 	INIT_WORK(&msk->work, mptcp_worker);
 	__skb_queue_head_init(&msk->receive_queue);
 	msk->out_of_order_queue = RB_ROOT;
@@ -3036,6 +3037,7 @@ void mptcp_destroy_common(struct mptcp_sock *msk)
 	msk->rmem_fwd_alloc = 0;
 	mptcp_token_destroy(msk);
 	mptcp_pm_free_anno_list(msk);
+	mptcp_free_local_addr_list(msk);
 }
 
 static void mptcp_destroy(struct sock *sk)
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 6cfa8ec26482..dbf0c134e923 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -281,6 +281,7 @@ struct mptcp_sock {
 	struct sk_buff_head receive_queue;
 	struct list_head conn_list;
 	struct list_head rtx_queue;
+	struct list_head local_addr_list;
 	struct mptcp_data_frag *first_pending;
 	struct list_head join_list;
 	struct socket	*subflow; /* outgoing connect/listener/!mp_capable */
@@ -733,6 +734,7 @@ struct mptcp_sock *mptcp_token_get_sock(struct net *net, u32 token);
 struct mptcp_sock *mptcp_token_iter_next(const struct net *net, long *s_slot,
 					 long *s_num);
 void mptcp_token_destroy(struct mptcp_sock *msk);
+void mptcp_free_local_addr_list(struct mptcp_sock *msk);
 
 void mptcp_crypto_key_sha(u64 key, u32 *token, u64 *idsn);
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 04/14] mptcp: read attributes of addr entries managed by userspace PMs
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
                   ` (2 preceding siblings ...)
  2022-01-28  0:38 ` [PATCH mptcp-next v3 03/14] mptcp: handle local addrs announced " Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 05/14] mptcp: netlink: split mptcp_pm_parse_addr into two functions Kishen Maloor
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change introduces a parallel path in the kernel for retrieving
the local id, flags, if_index for an addr entry in the context of
an MPTCP connection that's being managed by a userspace PM. The
userspace and in-kernel PM modes deviate in their procedures for
obtaining this information.

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
 net/mptcp/pm_netlink.c | 101 ++++++++++++++++++++++++++++-------------
 net/mptcp/protocol.h   |   2 +-
 net/mptcp/subflow.c    |   2 +-
 3 files changed, 71 insertions(+), 34 deletions(-)

diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
index ebec3610bb38..6ead218a20de 100644
--- a/net/mptcp/pm_netlink.c
+++ b/net/mptcp/pm_netlink.c
@@ -1219,6 +1219,7 @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct sock_common *skc)
 	struct mptcp_addr_info msk_local;
 	struct pm_nl_pernet *pernet;
 	int ret = -1;
+	int pm_type;
 
 	if (WARN_ON_ONCE(!msk))
 		return -1;
@@ -1236,31 +1237,50 @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct sock_common *skc)
 
 	pernet = net_generic(sock_net((struct sock *)msk), pm_nl_pernet_id);
 
-	rcu_read_lock();
-	list_for_each_entry_rcu(entry, &pernet->local_addr_list, list) {
-		if (addresses_equal(&entry->addr, &skc_local, entry->addr.port)) {
-			ret = entry->addr.id;
-			break;
-		}
-	}
-	rcu_read_unlock();
-	if (ret >= 0)
-		return ret;
-
 	/* address not found, add to local list */
-	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
-	if (!entry)
-		return -ENOMEM;
-
-	entry->addr = skc_local;
-	entry->addr.id = 0;
-	entry->addr.port = 0;
-	entry->ifindex = 0;
-	entry->flags = 0;
-	entry->lsk_ref = NULL;
-	ret = mptcp_pm_nl_append_new_local_addr(pernet, entry);
-	if (ret < 0)
-		kfree(entry);
+
+	pm_type = READ_ONCE(msk->pm.pm_type);
+
+	if (pm_type == MPTCP_PM_TYPE_KERNEL) {
+		rcu_read_lock();
+		list_for_each_entry_rcu(entry, &pernet->local_addr_list, list) {
+			if (addresses_equal(&entry->addr, &skc_local, entry->addr.port)) {
+				ret = entry->addr.id;
+				break;
+			}
+		}
+		rcu_read_unlock();
+
+		if (ret >= 0)
+			return ret;
+
+		entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+		if (!entry)
+			return -ENOMEM;
+
+		entry->addr = skc_local;
+		entry->addr.id = 0;
+		entry->addr.port = 0;
+		entry->ifindex = 0;
+		entry->flags = 0;
+		entry->lsk_ref = NULL;
+		ret = mptcp_pm_nl_append_new_local_addr(pernet, entry);
+		if (ret < 0)
+			kfree(entry);
+	} else if (pm_type == MPTCP_PM_TYPE_USERSPACE) {
+		struct mptcp_pm_addr_entry new_entry;
+		__be16 msk_sport =  ((struct inet_sock *)
+				     inet_sk((struct sock *)msk))->inet_sport;
+
+		memset(&new_entry, 0, sizeof(struct mptcp_pm_addr_entry));
+		new_entry.addr = skc_local;
+		new_entry.addr.id = 0;
+
+		if (new_entry.addr.port == msk_sport)
+			new_entry.addr.port = 0;
+
+		ret = mptcp_userspace_pm_append_new_local_addr(msk, &new_entry);
+	}
 
 	return ret;
 }
@@ -1491,22 +1511,39 @@ static int mptcp_nl_cmd_add_addr(struct sk_buff *skb, struct genl_info *info)
 	return 0;
 }
 
-int mptcp_pm_get_flags_and_ifindex_by_id(struct net *net, unsigned int id,
+int mptcp_pm_get_flags_and_ifindex_by_id(struct mptcp_sock *msk, unsigned int id,
 					 u8 *flags, int *ifindex)
 {
-	struct mptcp_pm_addr_entry *entry;
+	struct mptcp_pm_addr_entry *entry, *match = NULL;
+	struct sock *sk = (struct sock *)msk;
+	struct net *net = sock_net(sk);
 
 	*flags = 0;
 	*ifindex = 0;
 
 	if (id) {
-		rcu_read_lock();
-		entry = __lookup_addr_by_id(net_generic(net, pm_nl_pernet_id), id);
-		if (entry) {
-			*flags = entry->flags;
-			*ifindex = entry->ifindex;
+		if (READ_ONCE(msk->pm.pm_type) == MPTCP_PM_TYPE_KERNEL) {
+			rcu_read_lock();
+			entry = __lookup_addr_by_id(net_generic(net, pm_nl_pernet_id), id);
+			if (entry) {
+				*flags = entry->flags;
+				*ifindex = entry->ifindex;
+			}
+			rcu_read_unlock();
+		} else {
+			mptcp_data_lock(sk);
+			list_for_each_entry(entry, &msk->local_addr_list, list) {
+				if (id == entry->addr.id) {
+					match = entry;
+					break;
+				}
+			}
+			mptcp_data_unlock(sk);
+			if (match) {
+				*flags = match->flags;
+				*ifindex = match->ifindex;
+			}
 		}
-		rcu_read_unlock();
 	}
 
 	return 0;
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index dbf0c134e923..fbcd378115f7 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -773,7 +773,7 @@ mptcp_pm_del_add_timer(struct mptcp_sock *msk,
 struct mptcp_pm_add_entry *
 mptcp_lookup_anno_list_by_saddr(struct mptcp_sock *msk,
 				struct mptcp_addr_info *addr);
-int mptcp_pm_get_flags_and_ifindex_by_id(struct net *net, unsigned int id,
+int mptcp_pm_get_flags_and_ifindex_by_id(struct mptcp_sock *msk, unsigned int id,
 					 u8 *flags, int *ifindex);
 
 int mptcp_pm_announce_addr(struct mptcp_sock *msk,
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 479a4f53bbdd..e5f2df4b92a8 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -1416,7 +1416,7 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
 		local_id = err;
 	}
 
-	mptcp_pm_get_flags_and_ifindex_by_id(sock_net(sk), local_id,
+	mptcp_pm_get_flags_and_ifindex_by_id(msk, local_id,
 					     &flags, &ifindex);
 	subflow->remote_key = msk->remote_key;
 	subflow->local_key = msk->local_key;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 05/14] mptcp: netlink: split mptcp_pm_parse_addr into two functions
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
                   ` (3 preceding siblings ...)
  2022-01-28  0:38 ` [PATCH mptcp-next v3 04/14] mptcp: read attributes of addr entries managed " Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 06/14] mptcp: netlink: Add MPTCP_PM_CMD_ANNOUNCE Kishen Maloor
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp; +Cc: Florian Westphal

From: Florian Westphal <fw@strlen.de>

Next patch will need to parse MPTCP_PM_ATTR_ADDR attributes and
fill an mptcp_addr_info structure from a different genl command
callback.

To avoid copy-paste, split the existing function to a helper
that does the common part and then call the helper from the
(renamed)mptcp_pm_parse_entry function.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 net/mptcp/pm_netlink.c | 60 +++++++++++++++++++++++++++---------------
 1 file changed, 39 insertions(+), 21 deletions(-)

diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
index 6ead218a20de..829e20a6d0dd 100644
--- a/net/mptcp/pm_netlink.c
+++ b/net/mptcp/pm_netlink.c
@@ -1360,11 +1360,12 @@ static int mptcp_pm_family_to_addr(int family)
 	return MPTCP_PM_ADDR_ATTR_ADDR4;
 }
 
-static int mptcp_pm_parse_addr(struct nlattr *attr, struct genl_info *info,
-			       bool require_family,
-			       struct mptcp_pm_addr_entry *entry)
+static int mptcp_pm_parse_pm_addr_attr(struct nlattr *tb[],
+				       const struct nlattr *attr,
+				       struct genl_info *info,
+				       struct mptcp_addr_info *addr,
+				       bool require_family)
 {
-	struct nlattr *tb[MPTCP_PM_ADDR_ATTR_MAX + 1];
 	int err, addr_addr;
 
 	if (!attr) {
@@ -1378,27 +1379,29 @@ static int mptcp_pm_parse_addr(struct nlattr *attr, struct genl_info *info,
 	if (err)
 		return err;
 
-	memset(entry, 0, sizeof(*entry));
+	if (tb[MPTCP_PM_ADDR_ATTR_ID])
+		addr->id = nla_get_u8(tb[MPTCP_PM_ADDR_ATTR_ID]);
+
 	if (!tb[MPTCP_PM_ADDR_ATTR_FAMILY]) {
 		if (!require_family)
-			goto skip_family;
+			return err;
 
 		NL_SET_ERR_MSG_ATTR(info->extack, attr,
 				    "missing family");
 		return -EINVAL;
 	}
 
-	entry->addr.family = nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_FAMILY]);
-	if (entry->addr.family != AF_INET
+	addr->family = nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_FAMILY]);
+	if (addr->family != AF_INET
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
-	    && entry->addr.family != AF_INET6
+	    && addr->family != AF_INET6
 #endif
 	    ) {
 		NL_SET_ERR_MSG_ATTR(info->extack, attr,
 				    "unknown address family");
 		return -EINVAL;
 	}
-	addr_addr = mptcp_pm_family_to_addr(entry->addr.family);
+	addr_addr = mptcp_pm_family_to_addr(addr->family);
 	if (!tb[addr_addr]) {
 		NL_SET_ERR_MSG_ATTR(info->extack, attr,
 				    "missing address data");
@@ -1406,22 +1409,37 @@ static int mptcp_pm_parse_addr(struct nlattr *attr, struct genl_info *info,
 	}
 
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
-	if (entry->addr.family == AF_INET6)
-		entry->addr.addr6 = nla_get_in6_addr(tb[addr_addr]);
+	if (addr->family == AF_INET6)
+		addr->addr6 = nla_get_in6_addr(tb[addr_addr]);
 	else
 #endif
-		entry->addr.addr.s_addr = nla_get_in_addr(tb[addr_addr]);
+		addr->addr.s_addr = nla_get_in_addr(tb[addr_addr]);
+
+	if (tb[MPTCP_PM_ADDR_ATTR_PORT])
+		addr->port = htons(nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_PORT]));
+
+	return err;
+}
+
+static int mptcp_pm_parse_entry(struct nlattr *attr, struct genl_info *info,
+				bool require_family,
+				struct mptcp_pm_addr_entry *entry)
+{
+	struct nlattr *tb[MPTCP_PM_ADDR_ATTR_MAX + 1];
+	int err;
+
+	memset(entry, 0, sizeof(*entry));
+
+	err = mptcp_pm_parse_pm_addr_attr(tb, attr, info, &entry->addr, require_family);
+	if (err)
+		return err;
 
-skip_family:
 	if (tb[MPTCP_PM_ADDR_ATTR_IF_IDX]) {
 		u32 val = nla_get_s32(tb[MPTCP_PM_ADDR_ATTR_IF_IDX]);
 
 		entry->ifindex = val;
 	}
 
-	if (tb[MPTCP_PM_ADDR_ATTR_ID])
-		entry->addr.id = nla_get_u8(tb[MPTCP_PM_ADDR_ATTR_ID]);
-
 	if (tb[MPTCP_PM_ADDR_ATTR_FLAGS])
 		entry->flags = nla_get_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]);
 
@@ -1469,7 +1487,7 @@ static int mptcp_nl_cmd_add_addr(struct sk_buff *skb, struct genl_info *info)
 	struct mptcp_pm_addr_entry addr, *entry;
 	int ret;
 
-	ret = mptcp_pm_parse_addr(attr, info, true, &addr);
+	ret = mptcp_pm_parse_entry(attr, info, true, &addr);
 	if (ret < 0)
 		return ret;
 
@@ -1678,7 +1696,7 @@ static int mptcp_nl_cmd_del_addr(struct sk_buff *skb, struct genl_info *info)
 	unsigned int addr_max;
 	int ret;
 
-	ret = mptcp_pm_parse_addr(attr, info, false, &addr);
+	ret = mptcp_pm_parse_entry(attr, info, false, &addr);
 	if (ret < 0)
 		return ret;
 
@@ -1854,7 +1872,7 @@ static int mptcp_nl_cmd_get_addr(struct sk_buff *skb, struct genl_info *info)
 	void *reply;
 	int ret;
 
-	ret = mptcp_pm_parse_addr(attr, info, false, &addr);
+	ret = mptcp_pm_parse_entry(attr, info, false, &addr);
 	if (ret < 0)
 		return ret;
 
@@ -2065,7 +2083,7 @@ static int mptcp_nl_cmd_set_flags(struct sk_buff *skb, struct genl_info *info)
 	u8 bkup = 0, lookup_by_id = 0;
 	int ret;
 
-	ret = mptcp_pm_parse_addr(attr, info, false, &addr);
+	ret = mptcp_pm_parse_entry(attr, info, false, &addr);
 	if (ret < 0)
 		return ret;
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 06/14] mptcp: netlink: Add MPTCP_PM_CMD_ANNOUNCE
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
                   ` (4 preceding siblings ...)
  2022-01-28  0:38 ` [PATCH mptcp-next v3 05/14] mptcp: netlink: split mptcp_pm_parse_addr into two functions Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 07/14] mptcp: selftests: support MPTCP_PM_CMD_ANNOUNCE Kishen Maloor
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change adds a MPTCP netlink interface for issuing
ADD_ADDR advertisements over the chosen MPTCP connection from a
userspace path manager.

The command requires the following parameters:
{ token, { loc_id, family, daddr4 | daddr6 [, dport] } [, if_idx],
flags[signal|skiplsk] }.

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
v3: use MPTCP_PM_ADDR_FLAG_NO_LISTEN to skip creating a listening socket
in the kernel for an announced address, use the new helper
lsk_list_find_or_create()
---
 include/uapi/linux/mptcp.h |   2 +
 net/mptcp/pm_netlink.c     | 101 +++++++++++++++++++++++++++++++++++++
 2 files changed, 103 insertions(+)

diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h
index 0df44a116a31..088bdfbf442c 100644
--- a/include/uapi/linux/mptcp.h
+++ b/include/uapi/linux/mptcp.h
@@ -55,6 +55,7 @@ enum {
 	MPTCP_PM_ATTR_ADDR,				/* nested address */
 	MPTCP_PM_ATTR_RCV_ADD_ADDRS,			/* u32 */
 	MPTCP_PM_ATTR_SUBFLOWS,				/* u32 */
+	MPTCP_PM_ATTR_TOKEN,				/* u32 */
 
 	__MPTCP_PM_ATTR_MAX
 };
@@ -93,6 +94,7 @@ enum {
 	MPTCP_PM_CMD_SET_LIMITS,
 	MPTCP_PM_CMD_GET_LIMITS,
 	MPTCP_PM_CMD_SET_FLAGS,
+	MPTCP_PM_CMD_ANNOUNCE,
 
 	__MPTCP_PM_CMD_AFTER_LAST
 };
diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
index 829e20a6d0dd..e880177742d3 100644
--- a/net/mptcp/pm_netlink.c
+++ b/net/mptcp/pm_netlink.c
@@ -1312,6 +1312,7 @@ static const struct nla_policy mptcp_pm_policy[MPTCP_PM_ATTR_MAX + 1] = {
 					NLA_POLICY_NESTED(mptcp_pm_addr_policy),
 	[MPTCP_PM_ATTR_RCV_ADD_ADDRS]	= { .type	= NLA_U32,	},
 	[MPTCP_PM_ATTR_SUBFLOWS]	= { .type	= NLA_U32,	},
+	[MPTCP_PM_ATTR_TOKEN]		= { .type	= NLA_U32,	},
 };
 
 void mptcp_pm_nl_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)
@@ -2072,6 +2073,101 @@ static int mptcp_nl_set_flags(struct net *net,
 	return ret;
 }
 
+static int mptcp_nl_cmd_announce(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr *token = info->attrs[MPTCP_PM_ATTR_TOKEN];
+	struct nlattr *addr = info->attrs[MPTCP_PM_ATTR_ADDR];
+	struct pm_nl_pernet *pernet = genl_info_pm_nl(info);
+	struct mptcp_local_lsk *lsk_ref = NULL;
+	struct mptcp_pm_addr_entry addr_val;
+	bool reuse_port = false;
+	struct mptcp_sock *msk;
+	u32 token_val;
+	int err;
+
+	if (!addr || !token) {
+		GENL_SET_ERR_MSG(info, "missing required inputs");
+		return -EINVAL;
+	}
+
+	token_val = nla_get_u32(token);
+
+	msk = mptcp_token_get_sock(sock_net(skb->sk), token_val);
+	if (!msk) {
+		NL_SET_ERR_MSG_ATTR(info->extack, token, "invalid token");
+		return -EINVAL;
+	}
+
+	if (READ_ONCE(msk->pm.pm_type) != MPTCP_PM_TYPE_USERSPACE) {
+		GENL_SET_ERR_MSG(info, "invalid request; userspace PM not selected");
+		return -EINVAL;
+	}
+
+	err = mptcp_pm_parse_entry(addr, info, true, &addr_val);
+	if (err < 0) {
+		GENL_SET_ERR_MSG(info, "error parsing local address");
+		return err;
+	}
+
+	if (addr_val.addr.id == 0 || !(addr_val.flags & MPTCP_PM_ADDR_FLAG_SIGNAL)) {
+		GENL_SET_ERR_MSG(info, "invalid addr id or flags");
+		return -EINVAL;
+	}
+
+	if (!(addr_val.flags & MPTCP_PM_ADDR_FLAG_NO_LISTEN)) {
+		if (!addr_val.addr.port) {
+			addr_val.addr.port =
+				((struct inet_sock *)inet_sk
+				 ((struct sock *)msk))->inet_sport;
+
+			reuse_port = true;
+		}
+
+		lsk_ref = lsk_list_find_or_create(sock_net(skb->sk), pernet, &addr_val, &err);
+
+		if ((!lsk_ref && !reuse_port) || (!lsk_ref && (err != -EADDRINUSE) && reuse_port)) {
+			GENL_SET_ERR_MSG(info, "error creating listen socket");
+			err = (err == 0) ? -ENOMEM : err;
+			return err;
+		}
+
+		if (!reuse_port) {
+			addr_val.lsk_ref = lsk_ref;
+			lsk_ref = NULL;
+		} else {
+			addr_val.addr.port = 0;
+		}
+	}
+
+	err = mptcp_userspace_pm_append_new_local_addr(msk, &addr_val);
+	if (err < 0) {
+		if (addr_val.lsk_ref)
+			lsk_list_release(pernet, addr_val.lsk_ref);
+		else if (lsk_ref)
+			lsk_list_release(pernet, lsk_ref);
+		GENL_SET_ERR_MSG(info, "did not match address and id");
+		return err;
+	}
+
+	lock_sock((struct sock *)msk);
+	spin_lock_bh(&msk->pm.lock);
+
+	if (mptcp_pm_alloc_anno_list(msk, &addr_val, lsk_ref)) {
+		mptcp_pm_announce_addr(msk, &addr_val.addr, false);
+		mptcp_pm_nl_addr_send_ack(msk);
+	}
+
+	spin_unlock_bh(&msk->pm.lock);
+	release_sock((struct sock *)msk);
+
+	if (addr_val.lsk_ref)
+		lsk_list_release(pernet, addr_val.lsk_ref);
+	else if (lsk_ref)
+		lsk_list_release(pernet, lsk_ref);
+
+	return 0;
+}
+
 static int mptcp_nl_cmd_set_flags(struct sk_buff *skb, struct genl_info *info)
 {
 	struct mptcp_pm_addr_entry addr = { .addr = { .family = AF_UNSPEC }, }, *entry;
@@ -2425,6 +2521,11 @@ static const struct genl_small_ops mptcp_pm_ops[] = {
 		.doit   = mptcp_nl_cmd_set_flags,
 		.flags  = GENL_ADMIN_PERM,
 	},
+	{
+		.cmd    = MPTCP_PM_CMD_ANNOUNCE,
+		.doit   = mptcp_nl_cmd_announce,
+		.flags  = GENL_ADMIN_PERM,
+	},
 };
 
 static struct genl_family mptcp_genl_family __ro_after_init = {
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 07/14] mptcp: selftests: support MPTCP_PM_CMD_ANNOUNCE
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
                   ` (5 preceding siblings ...)
  2022-01-28  0:38 ` [PATCH mptcp-next v3 06/14] mptcp: netlink: Add MPTCP_PM_CMD_ANNOUNCE Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 08/14] mptcp: netlink: Add MPTCP_PM_CMD_REMOVE Kishen Maloor
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change updates the "pm_nl_ctl" testing sample with an "ann" (announce)
option to support the newly added netlink interface command
MPTCP_PM_CMD_ANNOUNCE to issue ADD_ADDR advertisements over the
chosen MPTCP connection.

E.g. ./pm_nl_ctl ann 192.168.122.75 token 823274047 id 25 dev enp1s0

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
v3: support MPTCP_PM_ADDR_FLAG_NO_LISTEN in both the
in-kernel PM's add_addr and userspace PM's amnounce_addr
implementations
---
 tools/testing/selftests/net/mptcp/pm_nl_ctl.c | 138 +++++++++++++++++-
 1 file changed, 137 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
index 2a57462764d0..a8c6a53ef8d1 100644
--- a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
+++ b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
@@ -6,6 +6,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <limits.h>
 
 #include <sys/socket.h>
 #include <sys/types.h>
@@ -25,7 +26,8 @@
 static void syntax(char *argv[])
 {
 	fprintf(stderr, "%s add|get|set|del|flush|dump|accept [<args>]\n", argv[0]);
-	fprintf(stderr, "\tadd [flags signal|subflow|backup|fullmesh] [id <nr>] [dev <name>] <ip>\n");
+	fprintf(stderr, "\tadd [flags signal|subflow|backup|fullmesh|nolisten] [id <nr>] [dev <name>] <ip>\n");
+	fprintf(stderr, "\tann <local-ip> id <local-id> token <token> [port <local-port>] [dev <name>] [nolisten]\n");
 	fprintf(stderr, "\tdel <id> [<ip>]\n");
 	fprintf(stderr, "\tget <id>\n");
 	fprintf(stderr, "\tset <ip> [flags backup|nobackup|fullmesh|nofullmesh] [port <nr>]\n");
@@ -170,6 +172,136 @@ static int resolve_mptcp_pm_netlink(int fd)
 	return genl_parse_getfamily((void *)data);
 }
 
+int announce_addr(int fd, int pm_family, int argc, char *argv[])
+{
+	char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+		  NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
+		  1024];
+	u_int32_t flags = MPTCP_PM_ADDR_FLAG_SIGNAL;
+	u_int32_t token = UINT_MAX;
+	struct rtattr *rta, *addr;
+	u_int32_t id = UINT_MAX;
+	struct nlmsghdr *nh;
+	u_int16_t family;
+	int addr_start;
+	int off = 0;
+	int arg;
+
+	memset(data, 0, sizeof(data));
+	nh = (void *)data;
+	off = init_genl_req(data, pm_family, MPTCP_PM_CMD_ANNOUNCE,
+			    MPTCP_PM_VER);
+
+	if (argc < 7)
+		syntax(argv);
+
+	/* local-ip header */
+	addr_start = off;
+	addr = (void *)(data + off);
+	addr->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR;
+	addr->rta_len = RTA_LENGTH(0);
+	off += NLMSG_ALIGN(addr->rta_len);
+
+	/* local-ip data */
+	/* record addr type */
+	rta = (void *)(data + off);
+	if (inet_pton(AF_INET, argv[2], RTA_DATA(rta))) {
+		family = AF_INET;
+		rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4;
+		rta->rta_len = RTA_LENGTH(4);
+	} else if (inet_pton(AF_INET6, argv[2], RTA_DATA(rta))) {
+		family = AF_INET6;
+		rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6;
+		rta->rta_len = RTA_LENGTH(16);
+	} else
+		error(1, errno, "can't parse ip %s", argv[2]);
+	off += NLMSG_ALIGN(rta->rta_len);
+
+	/* addr family */
+	rta = (void *)(data + off);
+	rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY;
+	rta->rta_len = RTA_LENGTH(2);
+	memcpy(RTA_DATA(rta), &family, 2);
+	off += NLMSG_ALIGN(rta->rta_len);
+
+	for (arg = 3; arg < argc; arg++) {
+		if (!strcmp(argv[arg], "nolisten")) {
+			/* nolisten */
+			flags |= MPTCP_PM_ADDR_FLAG_NO_LISTEN;
+		} else if (!strcmp(argv[arg], "id")) {
+			/* local-id */
+			if (++arg >= argc)
+				error(1, 0, " missing id value");
+
+			id = atoi(argv[arg]);
+			rta = (void *)(data + off);
+			rta->rta_type = MPTCP_PM_ADDR_ATTR_ID;
+			rta->rta_len = RTA_LENGTH(1);
+			memcpy(RTA_DATA(rta), &id, 1);
+			off += NLMSG_ALIGN(rta->rta_len);
+		} else if (!strcmp(argv[arg], "dev")) {
+			/* for the if_index */
+			int32_t ifindex;
+
+			if (++arg >= argc)
+				error(1, 0, " missing dev name");
+
+			ifindex = if_nametoindex(argv[arg]);
+			if (!ifindex)
+				error(1, errno, "unknown device %s", argv[arg]);
+
+			rta = (void *)(data + off);
+			rta->rta_type = MPTCP_PM_ADDR_ATTR_IF_IDX;
+			rta->rta_len = RTA_LENGTH(4);
+			memcpy(RTA_DATA(rta), &ifindex, 4);
+			off += NLMSG_ALIGN(rta->rta_len);
+		} else if (!strcmp(argv[arg], "port")) {
+			/* local-port (optional) */
+			u_int16_t port;
+
+			if (++arg >= argc)
+				error(1, 0, " missing port value");
+
+			port = atoi(argv[arg]);
+			rta = (void *)(data + off);
+			rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT;
+			rta->rta_len = RTA_LENGTH(2);
+			memcpy(RTA_DATA(rta), &port, 2);
+			off += NLMSG_ALIGN(rta->rta_len);
+		} else if (!strcmp(argv[arg], "token")) {
+			/* MPTCP connection token */
+			if (++arg >= argc)
+				error(1, 0, " missing token value");
+
+			token = atoi(argv[arg]);
+		} else
+			error(1, 0, "unknown keyword %s", argv[arg]);
+	}
+
+	/* addr flags */
+	rta = (void *)(data + off);
+	rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS;
+	rta->rta_len = RTA_LENGTH(4);
+	memcpy(RTA_DATA(rta), &flags, 4);
+	off += NLMSG_ALIGN(rta->rta_len);
+
+	addr->rta_len = off - addr_start;
+
+	if (id == UINT_MAX || token == UINT_MAX)
+		error(1, 0, " missing mandatory inputs");
+
+	/* token */
+	rta = (void *)(data + off);
+	rta->rta_type = MPTCP_PM_ATTR_TOKEN;
+	rta->rta_len = RTA_LENGTH(4);
+	memcpy(RTA_DATA(rta), &token, 4);
+	off += NLMSG_ALIGN(rta->rta_len);
+
+	do_nl_req(fd, nh, off, 0);
+
+	return 0;
+}
+
 int add_addr(int fd, int pm_family, int argc, char *argv[])
 {
 	char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
@@ -238,6 +370,8 @@ int add_addr(int fd, int pm_family, int argc, char *argv[])
 					flags |= MPTCP_PM_ADDR_FLAG_BACKUP;
 				else if (!strcmp(tok, "fullmesh"))
 					flags |= MPTCP_PM_ADDR_FLAG_FULLMESH;
+				else if (!strcmp(tok, "nolisten"))
+					flags |= MPTCP_PM_ADDR_FLAG_NO_LISTEN;
 				else
 					error(1, errno,
 					      "unknown flag %s", argv[arg]);
@@ -758,6 +892,8 @@ int main(int argc, char *argv[])
 
 	if (!strcmp(argv[1], "add"))
 		return add_addr(fd, pm_family, argc, argv);
+	else if (!strcmp(argv[1], "ann"))
+		return announce_addr(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "del"))
 		return del_addr(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "flush"))
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 08/14] mptcp: netlink: Add MPTCP_PM_CMD_REMOVE
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
                   ` (6 preceding siblings ...)
  2022-01-28  0:38 ` [PATCH mptcp-next v3 07/14] mptcp: selftests: support MPTCP_PM_CMD_ANNOUNCE Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 09/14] mptcp: selftests: support MPTCP_PM_CMD_REMOVE Kishen Maloor
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change adds a MPTCP netlink command for issuing
REMOVE_ADDR signals for a specific address over the chosen MPTCP
connection from a userspace path manager.

The command requires the following parameters: {token, loc_id}.

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
v3: update the per-msk count of local addresses populated by the
userspace PM; this is the value that needs to stay within the
new sysctl configurable limit
---
 include/uapi/linux/mptcp.h |  2 ++
 net/mptcp/pm_netlink.c     | 71 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+)

diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h
index 088bdfbf442c..c2d7d3cefd02 100644
--- a/include/uapi/linux/mptcp.h
+++ b/include/uapi/linux/mptcp.h
@@ -56,6 +56,7 @@ enum {
 	MPTCP_PM_ATTR_RCV_ADD_ADDRS,			/* u32 */
 	MPTCP_PM_ATTR_SUBFLOWS,				/* u32 */
 	MPTCP_PM_ATTR_TOKEN,				/* u32 */
+	MPTCP_PM_ATTR_LOC_ID,				/* u8 */
 
 	__MPTCP_PM_ATTR_MAX
 };
@@ -95,6 +96,7 @@ enum {
 	MPTCP_PM_CMD_GET_LIMITS,
 	MPTCP_PM_CMD_SET_FLAGS,
 	MPTCP_PM_CMD_ANNOUNCE,
+	MPTCP_PM_CMD_REMOVE,
 
 	__MPTCP_PM_CMD_AFTER_LAST
 };
diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
index e880177742d3..f2130374c5fc 100644
--- a/net/mptcp/pm_netlink.c
+++ b/net/mptcp/pm_netlink.c
@@ -1313,6 +1313,7 @@ static const struct nla_policy mptcp_pm_policy[MPTCP_PM_ATTR_MAX + 1] = {
 	[MPTCP_PM_ATTR_RCV_ADD_ADDRS]	= { .type	= NLA_U32,	},
 	[MPTCP_PM_ATTR_SUBFLOWS]	= { .type	= NLA_U32,	},
 	[MPTCP_PM_ATTR_TOKEN]		= { .type	= NLA_U32,	},
+	[MPTCP_PM_ATTR_LOC_ID]		= { .type	= NLA_U8,	},
 };
 
 void mptcp_pm_nl_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)
@@ -1749,6 +1750,7 @@ static void mptcp_pm_remove_addrs_and_subflows(struct mptcp_sock *msk,
 		    slist.nr < MPTCP_RM_IDS_MAX) {
 			alist.ids[alist.nr++] = entry->addr.id;
 			slist.ids[slist.nr++] = entry->addr.id;
+			remove_anno_list_by_saddr(msk, &entry->addr);
 		} else if (remove_anno_list_by_saddr(msk, &entry->addr) &&
 			 alist.nr < MPTCP_RM_IDS_MAX) {
 			alist.ids[alist.nr++] = entry->addr.id;
@@ -2375,6 +2377,70 @@ void mptcp_event_addr_removed(const struct mptcp_sock *msk, uint8_t id)
 	kfree_skb(skb);
 }
 
+static int mptcp_nl_cmd_remove(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr *token = info->attrs[MPTCP_PM_ATTR_TOKEN];
+	struct nlattr *id = info->attrs[MPTCP_PM_ATTR_LOC_ID];
+	struct pm_nl_pernet *pernet = genl_info_pm_nl(info);
+	struct mptcp_pm_addr_entry *match = NULL;
+	struct mptcp_pm_addr_entry *entry;
+	struct mptcp_sock *msk;
+	LIST_HEAD(free_list);
+	u32 token_val;
+	u8 id_val;
+
+	if (!id || !token) {
+		GENL_SET_ERR_MSG(info, "missing required inputs");
+		return -EINVAL;
+	}
+
+	id_val = nla_get_u8(id);
+	token_val = nla_get_u32(token);
+
+	msk = mptcp_token_get_sock(sock_net(skb->sk), token_val);
+	if (!msk) {
+		NL_SET_ERR_MSG_ATTR(info->extack, token, "invalid token");
+		return -EINVAL;
+	}
+
+	if (READ_ONCE(msk->pm.pm_type) != MPTCP_PM_TYPE_USERSPACE) {
+		GENL_SET_ERR_MSG(info, "invalid request; userspace PM not selected");
+		return -EINVAL;
+	}
+
+	lock_sock((struct sock *)msk);
+
+	list_for_each_entry(entry, &msk->local_addr_list, list) {
+		if (entry->addr.id == id_val) {
+			match = entry;
+			break;
+		}
+	}
+
+	if (!match) {
+		GENL_SET_ERR_MSG(info, "address with specified id not found");
+		release_sock((struct sock *)msk);
+		return -EINVAL;
+	}
+
+	list_move(&match->list, &free_list);
+
+	mptcp_pm_remove_addrs_and_subflows(msk, &free_list);
+
+	release_sock((struct sock *)msk);
+
+	spin_lock_bh(&msk->pm.lock);
+	--msk->pm.local_addr_used;
+	spin_unlock_bh(&msk->pm.lock);
+
+	list_for_each_entry_safe(match, entry, &free_list, list) {
+		if (match->lsk_ref)
+			lsk_list_release(pernet, match->lsk_ref);
+		kfree(match);
+	}
+	return 0;
+}
+
 void mptcp_event_addr_announced(const struct mptcp_sock *msk,
 				const struct mptcp_addr_info *info,
 				const struct sock *ssk)
@@ -2526,6 +2592,11 @@ static const struct genl_small_ops mptcp_pm_ops[] = {
 		.doit   = mptcp_nl_cmd_announce,
 		.flags  = GENL_ADMIN_PERM,
 	},
+	{
+		.cmd    = MPTCP_PM_CMD_REMOVE,
+		.doit   = mptcp_nl_cmd_remove,
+		.flags  = GENL_ADMIN_PERM,
+	},
 };
 
 static struct genl_family mptcp_genl_family __ro_after_init = {
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 09/14] mptcp: selftests: support MPTCP_PM_CMD_REMOVE
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
                   ` (7 preceding siblings ...)
  2022-01-28  0:38 ` [PATCH mptcp-next v3 08/14] mptcp: netlink: Add MPTCP_PM_CMD_REMOVE Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 10/14] mptcp: netlink: allow userspace-driven subflow establishment Kishen Maloor
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change updates the "pm_nl_ctl" testing sample with a "rem" (remove)
option to support the newly added netlink interface command
MPTCP_PM_CMD_REMOVE to issue REMOVE_ADDR signals over the
chosen MPTCP connection.

E.g. ./pm_nl_ctl rem token 823274047 id 23

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
 tools/testing/selftests/net/mptcp/pm_nl_ctl.c | 52 +++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
index a8c6a53ef8d1..8cde5053f082 100644
--- a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
+++ b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
@@ -28,6 +28,7 @@ static void syntax(char *argv[])
 	fprintf(stderr, "%s add|get|set|del|flush|dump|accept [<args>]\n", argv[0]);
 	fprintf(stderr, "\tadd [flags signal|subflow|backup|fullmesh|nolisten] [id <nr>] [dev <name>] <ip>\n");
 	fprintf(stderr, "\tann <local-ip> id <local-id> token <token> [port <local-port>] [dev <name>] [nolisten]\n");
+	fprintf(stderr, "\trem id <local-id> token <token>\n");
 	fprintf(stderr, "\tdel <id> [<ip>]\n");
 	fprintf(stderr, "\tget <id>\n");
 	fprintf(stderr, "\tset <ip> [flags backup|nobackup|fullmesh|nofullmesh] [port <nr>]\n");
@@ -172,6 +173,55 @@ static int resolve_mptcp_pm_netlink(int fd)
 	return genl_parse_getfamily((void *)data);
 }
 
+int remove_addr(int fd, int pm_family, int argc, char *argv[])
+{
+	char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+		  NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
+		  1024];
+	struct nlmsghdr *nh;
+	struct rtattr *rta;
+	u_int32_t token;
+	u_int8_t id;
+	int off = 0;
+	int arg;
+
+	memset(data, 0, sizeof(data));
+	nh = (void *)data;
+	off = init_genl_req(data, pm_family, MPTCP_PM_CMD_REMOVE,
+			    MPTCP_PM_VER);
+
+	if (argc < 6)
+		syntax(argv);
+
+	for (arg = 2; arg < argc; arg++) {
+		if (!strcmp(argv[arg], "id")) {
+			if (++arg >= argc)
+				error(1, 0, " missing id value");
+
+			id = atoi(argv[arg]);
+			rta = (void *)(data + off);
+			rta->rta_type = MPTCP_PM_ATTR_LOC_ID;
+			rta->rta_len = RTA_LENGTH(1);
+			memcpy(RTA_DATA(rta), &id, 1);
+			off += NLMSG_ALIGN(rta->rta_len);
+		} else if (!strcmp(argv[arg], "token")) {
+			if (++arg >= argc)
+				error(1, 0, " missing token value");
+
+			token = atoi(argv[arg]);
+			rta = (void *)(data + off);
+			rta->rta_type = MPTCP_PM_ATTR_TOKEN;
+			rta->rta_len = RTA_LENGTH(4);
+			memcpy(RTA_DATA(rta), &token, 4);
+			off += NLMSG_ALIGN(rta->rta_len);
+		} else
+			error(1, 0, "unknown keyword %s", argv[arg]);
+	}
+
+	do_nl_req(fd, nh, off, 0);
+	return 0;
+}
+
 int announce_addr(int fd, int pm_family, int argc, char *argv[])
 {
 	char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
@@ -894,6 +944,8 @@ int main(int argc, char *argv[])
 		return add_addr(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "ann"))
 		return announce_addr(fd, pm_family, argc, argv);
+	else if (!strcmp(argv[1], "rem"))
+		return remove_addr(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "del"))
 		return del_addr(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "flush"))
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 10/14] mptcp: netlink: allow userspace-driven subflow establishment
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
                   ` (8 preceding siblings ...)
  2022-01-28  0:38 ` [PATCH mptcp-next v3 09/14] mptcp: selftests: support MPTCP_PM_CMD_REMOVE Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 11/14] mptcp: selftests: support MPTCP_PM_CMD_SUBFLOW_CREATE Kishen Maloor
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp; +Cc: Florian Westphal

From: Florian Westphal <fw@strlen.de>

This allows userspace to tell kernel to add a new subflow to an existing
mptcp connection.

Userspace provides the token to identify the mptcp-level connection
that needs a change in active subflows and the local and remote
addresses of the new or the to-be-removed subflow.

MPTCP_PM_CMD_SUBFLOW_CREATE requires the following parameters:
{ token, { loc_id, family, loc_addr4 | loc_addr6 }, { family, rem_addr4 |
rem_addr6, rem_port }

MPTCP_PM_CMD_SUBFLOW_DESTROY requires the following parameters:
{ token, { family, loc_addr4 | loc_addr6, loc_port }, { family, rem_addr4 |
rem_addr6, rem_port }

Signed-off-by: Florian Westphal <fw@strlen.de>
Co-developed-by: Kishen Maloor <kishen.maloor@intel.com>
Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
v2: fix build error when IPv6 is not enabled
---
 include/uapi/linux/mptcp.h |   3 +
 net/mptcp/pm_netlink.c     | 201 +++++++++++++++++++++++++++++++++++++
 2 files changed, 204 insertions(+)

diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h
index c2d7d3cefd02..b0b1cc7cddbe 100644
--- a/include/uapi/linux/mptcp.h
+++ b/include/uapi/linux/mptcp.h
@@ -57,6 +57,7 @@ enum {
 	MPTCP_PM_ATTR_SUBFLOWS,				/* u32 */
 	MPTCP_PM_ATTR_TOKEN,				/* u32 */
 	MPTCP_PM_ATTR_LOC_ID,				/* u8 */
+	MPTCP_PM_ATTR_ADDR_REMOTE,			/* nested address */
 
 	__MPTCP_PM_ATTR_MAX
 };
@@ -97,6 +98,8 @@ enum {
 	MPTCP_PM_CMD_SET_FLAGS,
 	MPTCP_PM_CMD_ANNOUNCE,
 	MPTCP_PM_CMD_REMOVE,
+	MPTCP_PM_CMD_SUBFLOW_CREATE,
+	MPTCP_PM_CMD_SUBFLOW_DESTROY,
 
 	__MPTCP_PM_CMD_AFTER_LAST
 };
diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
index f2130374c5fc..056534761036 100644
--- a/net/mptcp/pm_netlink.c
+++ b/net/mptcp/pm_netlink.c
@@ -1314,6 +1314,8 @@ static const struct nla_policy mptcp_pm_policy[MPTCP_PM_ATTR_MAX + 1] = {
 	[MPTCP_PM_ATTR_SUBFLOWS]	= { .type	= NLA_U32,	},
 	[MPTCP_PM_ATTR_TOKEN]		= { .type	= NLA_U32,	},
 	[MPTCP_PM_ATTR_LOC_ID]		= { .type	= NLA_U8,	},
+	[MPTCP_PM_ATTR_ADDR_REMOTE]	=
+					NLA_POLICY_NESTED(mptcp_pm_addr_policy),
 };
 
 void mptcp_pm_nl_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)
@@ -1423,6 +1425,16 @@ static int mptcp_pm_parse_pm_addr_attr(struct nlattr *tb[],
 	return err;
 }
 
+static int mptcp_pm_parse_addr(struct nlattr *attr, struct genl_info *info,
+			       struct mptcp_addr_info *addr)
+{
+	struct nlattr *tb[MPTCP_PM_ADDR_ATTR_MAX + 1];
+
+	memset(addr, 0, sizeof(*addr));
+
+	return mptcp_pm_parse_pm_addr_attr(tb, attr, info, addr, true);
+}
+
 static int mptcp_pm_parse_entry(struct nlattr *attr, struct genl_info *info,
 				bool require_family,
 				struct mptcp_pm_addr_entry *entry)
@@ -2552,6 +2564,185 @@ void mptcp_event(enum mptcp_event_type type, const struct mptcp_sock *msk,
 	kfree_skb(skb);
 }
 
+static int mptcp_nl_cmd_sf_create(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr *raddr = info->attrs[MPTCP_PM_ATTR_ADDR_REMOTE];
+	struct nlattr *token = info->attrs[MPTCP_PM_ATTR_TOKEN];
+	struct nlattr *laddr = info->attrs[MPTCP_PM_ATTR_ADDR];
+	struct mptcp_addr_info addr_r;
+	struct mptcp_addr_info addr_l;
+	struct mptcp_sock *msk;
+	struct sock *sk;
+	u32 token_val;
+	int ret;
+
+	if (!laddr || !raddr || !token) {
+		GENL_SET_ERR_MSG(info, "missing required inputs");
+		return -EINVAL;
+	}
+
+	token_val = nla_get_u32(token);
+
+	msk = mptcp_token_get_sock(genl_info_net(info), token_val);
+	if (!msk) {
+		NL_SET_ERR_MSG_ATTR(info->extack, token, "invalid token");
+		return -EINVAL;
+	}
+
+	if (READ_ONCE(msk->pm.pm_type) != MPTCP_PM_TYPE_USERSPACE) {
+		GENL_SET_ERR_MSG(info, "invalid request; userspace PM not selected");
+		return -EINVAL;
+	}
+
+	ret = mptcp_pm_parse_addr(laddr, info, &addr_l);
+	if (ret < 0) {
+		NL_SET_ERR_MSG_ATTR(info->extack, laddr, "error parsing local addr");
+		return -EINVAL;
+	}
+
+	if (addr_l.id == 0) {
+		NL_SET_ERR_MSG_ATTR(info->extack, laddr, "missing local addr id");
+		return -EINVAL;
+	}
+
+	ret = mptcp_pm_parse_addr(raddr, info, &addr_r);
+	if (ret < 0) {
+		NL_SET_ERR_MSG_ATTR(info->extack, raddr, "error parsing remote addr");
+		return -EINVAL;
+	}
+
+	sk = &msk->sk.icsk_inet.sk;
+	lock_sock(sk);
+
+	ret = __mptcp_subflow_connect(sk, &addr_l, &addr_r);
+
+	release_sock(sk);
+
+	return ret;
+}
+
+static struct sock *mptcp_nl_find_ssk(struct mptcp_sock *msk,
+				      const struct mptcp_addr_info *local,
+				      const struct mptcp_addr_info *remote)
+{
+	struct sock *sk = &msk->sk.icsk_inet.sk;
+	struct mptcp_subflow_context *subflow;
+	struct sock *found = NULL;
+
+	if (local->family != remote->family)
+		return NULL;
+
+	lock_sock(sk);
+
+	mptcp_for_each_subflow(msk, subflow) {
+		const struct ipv6_pinfo *pinfo;
+		const struct inet_sock *issk;
+		struct sock *ssk;
+
+		ssk = mptcp_subflow_tcp_sock(subflow);
+
+		if (local->family != ssk->sk_family)
+			continue;
+
+		issk = inet_sk(ssk);
+
+		switch (ssk->sk_family) {
+		case AF_INET:
+			if (issk->inet_saddr != local->addr.s_addr ||
+			    issk->inet_daddr != remote->addr.s_addr)
+				continue;
+			break;
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+		case AF_INET6:
+			pinfo = inet6_sk(ssk);
+			if (!ipv6_addr_equal(&local->addr6, &pinfo->saddr) ||
+			    !ipv6_addr_equal(&remote->addr6, &ssk->sk_v6_daddr))
+				continue;
+			break;
+#endif
+		default:
+			continue;
+		}
+
+		if (issk->inet_sport == local->port &&
+		    issk->inet_dport == remote->port) {
+			found = ssk;
+			goto found;
+		}
+	}
+
+found:
+	release_sock(sk);
+
+	return found;
+}
+
+static int mptcp_nl_cmd_sf_destroy(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr *raddr = info->attrs[MPTCP_PM_ATTR_ADDR_REMOTE];
+	struct nlattr *token = info->attrs[MPTCP_PM_ATTR_TOKEN];
+	struct nlattr *laddr = info->attrs[MPTCP_PM_ATTR_ADDR];
+	struct mptcp_addr_info addr_l;
+	struct mptcp_addr_info addr_r;
+	struct mptcp_sock *msk;
+	struct sock *sk, *ssk;
+	u32 token_val;
+	int ret;
+
+	if (!laddr || !raddr || !token) {
+		GENL_SET_ERR_MSG(info, "missing required inputs");
+		return -EINVAL;
+	}
+
+	token_val = nla_get_u32(token);
+
+	msk = mptcp_token_get_sock(genl_info_net(info), token_val);
+	if (!msk) {
+		NL_SET_ERR_MSG_ATTR(info->extack, token, "invalid token");
+		return -EINVAL;
+	}
+
+	if (READ_ONCE(msk->pm.pm_type) != MPTCP_PM_TYPE_USERSPACE) {
+		GENL_SET_ERR_MSG(info, "invalid request; userspace PM not selected");
+		return -EINVAL;
+	}
+
+	ret = mptcp_pm_parse_addr(laddr, info, &addr_l);
+	if (ret < 0) {
+		NL_SET_ERR_MSG_ATTR(info->extack, laddr, "error parsing local addr");
+		return ret;
+	}
+
+	ret = mptcp_pm_parse_addr(raddr, info, &addr_r);
+	if (ret < 0) {
+		NL_SET_ERR_MSG_ATTR(info->extack, raddr, "error parsing remote addr");
+		return ret;
+	}
+
+	if (addr_l.family != addr_r.family) {
+		GENL_SET_ERR_MSG(info, "address families do not match");
+		return -EINVAL;
+	}
+
+	if (!addr_l.port || !addr_r.port) {
+		GENL_SET_ERR_MSG(info, "missing local or remote port");
+		return -EINVAL;
+	}
+
+	sk = &msk->sk.icsk_inet.sk;
+	ssk = mptcp_nl_find_ssk(msk, &addr_l, &addr_r);
+	if (ssk) {
+		struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
+
+		mptcp_subflow_shutdown(sk, ssk, RCV_SHUTDOWN | SEND_SHUTDOWN);
+		mptcp_close_ssk(sk, ssk, subflow);
+	} else {
+		ret = -ESRCH;
+	}
+
+	return ret;
+}
+
 static const struct genl_small_ops mptcp_pm_ops[] = {
 	{
 		.cmd    = MPTCP_PM_CMD_ADD_ADDR,
@@ -2597,6 +2788,16 @@ static const struct genl_small_ops mptcp_pm_ops[] = {
 		.doit   = mptcp_nl_cmd_remove,
 		.flags  = GENL_ADMIN_PERM,
 	},
+	{
+		.cmd    = MPTCP_PM_CMD_SUBFLOW_CREATE,
+		.doit   = mptcp_nl_cmd_sf_create,
+		.flags  = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd    = MPTCP_PM_CMD_SUBFLOW_DESTROY,
+		.doit   = mptcp_nl_cmd_sf_destroy,
+		.flags  = GENL_ADMIN_PERM,
+	},
 };
 
 static struct genl_family mptcp_genl_family __ro_after_init = {
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 11/14] mptcp: selftests: support MPTCP_PM_CMD_SUBFLOW_CREATE
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
                   ` (9 preceding siblings ...)
  2022-01-28  0:38 ` [PATCH mptcp-next v3 10/14] mptcp: netlink: allow userspace-driven subflow establishment Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 12/14] mptcp: selftests: support MPTCP_PM_CMD_SUBFLOW_DESTROY Kishen Maloor
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change updates the "pm_nl_ctl" testing sample with a "csf"
(create subflow) option to support the newly added netlink interface
command MPTCP_PM_CMD_SUBFLOW_CREATE over the chosen MPTCP connection.

E.g. ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2 rport 56789
token 823274047

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
 tools/testing/selftests/net/mptcp/pm_nl_ctl.c | 129 ++++++++++++++++++
 1 file changed, 129 insertions(+)

diff --git a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
index 8cde5053f082..0ba357ab6e4e 100644
--- a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
+++ b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
@@ -29,6 +29,7 @@ static void syntax(char *argv[])
 	fprintf(stderr, "\tadd [flags signal|subflow|backup|fullmesh|nolisten] [id <nr>] [dev <name>] <ip>\n");
 	fprintf(stderr, "\tann <local-ip> id <local-id> token <token> [port <local-port>] [dev <name>] [nolisten]\n");
 	fprintf(stderr, "\trem id <local-id> token <token>\n");
+	fprintf(stderr, "\tcsf lip <local-ip> lid <local-id> rip <remote-ip> rport <remote-port> token <token>\n");
 	fprintf(stderr, "\tdel <id> [<ip>]\n");
 	fprintf(stderr, "\tget <id>\n");
 	fprintf(stderr, "\tset <ip> [flags backup|nobackup|fullmesh|nofullmesh] [port <nr>]\n");
@@ -173,6 +174,132 @@ static int resolve_mptcp_pm_netlink(int fd)
 	return genl_parse_getfamily((void *)data);
 }
 
+int csf(int fd, int pm_family, int argc, char *argv[])
+{
+	char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+		  NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
+		  1024];
+	const char *params[5];
+	struct nlmsghdr *nh;
+	struct rtattr *addr;
+	struct rtattr *rta;
+	u_int16_t family;
+	u_int32_t token;
+	u_int16_t port;
+	int addr_start;
+	u_int8_t id;
+	int off = 0;
+	int arg;
+
+	memset(params, 0, 5 * sizeof(const char *));
+
+	memset(data, 0, sizeof(data));
+	nh = (void *)data;
+	off = init_genl_req(data, pm_family, MPTCP_PM_CMD_SUBFLOW_CREATE,
+			    MPTCP_PM_VER);
+
+	if (argc < 12)
+		syntax(argv);
+
+	/* Params recorded in this order:
+	 * <local-ip>, <local-id>, <remote-ip>, <remote-port>, <token>
+	 */
+	for (arg = 2; arg < argc; arg++) {
+		if (!strcmp(argv[arg], "lip")) {
+			if (++arg >= argc)
+				error(1, 0, " missing local IP");
+
+			params[0] = argv[arg];
+		} else if (!strcmp(argv[arg], "lid")) {
+			if (++arg >= argc)
+				error(1, 0, " missing local id");
+
+			params[1] = argv[arg];
+		} else if (!strcmp(argv[arg], "rip")) {
+			if (++arg >= argc)
+				error(1, 0, " missing remote ip");
+
+			params[2] = argv[arg];
+		} else if (!strcmp(argv[arg], "rport")) {
+			if (++arg >= argc)
+				error(1, 0, " missing remote port");
+
+			params[3] = argv[arg];
+		} else if (!strcmp(argv[arg], "token")) {
+			if (++arg >= argc)
+				error(1, 0, " missing token");
+
+			params[4] = argv[arg];
+		} else
+			error(1, 0, "unknown param %s", argv[arg]);
+	}
+
+	for (arg = 0; arg < 4; arg = arg + 2) {
+		/*  addr header */
+		addr_start = off;
+		addr = (void *)(data + off);
+		addr->rta_type = NLA_F_NESTED |
+			((arg == 0) ? MPTCP_PM_ATTR_ADDR : MPTCP_PM_ATTR_ADDR_REMOTE);
+		addr->rta_len = RTA_LENGTH(0);
+		off += NLMSG_ALIGN(addr->rta_len);
+
+		/*  addr data */
+		rta = (void *)(data + off);
+		if (inet_pton(AF_INET, params[arg], RTA_DATA(rta))) {
+			family = AF_INET;
+			rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4;
+			rta->rta_len = RTA_LENGTH(4);
+		} else if (inet_pton(AF_INET6, params[arg], RTA_DATA(rta))) {
+			family = AF_INET6;
+			rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6;
+			rta->rta_len = RTA_LENGTH(16);
+		} else
+			error(1, errno, "can't parse ip %s", params[arg]);
+		off += NLMSG_ALIGN(rta->rta_len);
+
+		/* family */
+		rta = (void *)(data + off);
+		rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY;
+		rta->rta_len = RTA_LENGTH(2);
+		memcpy(RTA_DATA(rta), &family, 2);
+		off += NLMSG_ALIGN(rta->rta_len);
+
+		if (arg == 2) {
+			/*  port */
+			port = atoi(params[arg + 1]);
+			rta = (void *)(data + off);
+			rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT;
+			rta->rta_len = RTA_LENGTH(2);
+			memcpy(RTA_DATA(rta), &port, 2);
+			off += NLMSG_ALIGN(rta->rta_len);
+		}
+
+		if (arg == 0) {
+			/* id */
+			id = atoi(params[arg + 1]);
+			rta = (void *)(data + off);
+			rta->rta_type = MPTCP_PM_ADDR_ATTR_ID;
+			rta->rta_len = RTA_LENGTH(1);
+			memcpy(RTA_DATA(rta), &id, 1);
+			off += NLMSG_ALIGN(rta->rta_len);
+		}
+
+		addr->rta_len = off - addr_start;
+	}
+
+	/* token */
+	token = atoi(params[4]);
+	rta = (void *)(data + off);
+	rta->rta_type = MPTCP_PM_ATTR_TOKEN;
+	rta->rta_len = RTA_LENGTH(4);
+	memcpy(RTA_DATA(rta), &token, 4);
+	off += NLMSG_ALIGN(rta->rta_len);
+
+	do_nl_req(fd, nh, off, 0);
+
+	return 0;
+}
+
 int remove_addr(int fd, int pm_family, int argc, char *argv[])
 {
 	char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
@@ -946,6 +1073,8 @@ int main(int argc, char *argv[])
 		return announce_addr(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "rem"))
 		return remove_addr(fd, pm_family, argc, argv);
+	else if (!strcmp(argv[1], "csf"))
+		return csf(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "del"))
 		return del_addr(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "flush"))
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 12/14] mptcp: selftests: support MPTCP_PM_CMD_SUBFLOW_DESTROY
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
                   ` (10 preceding siblings ...)
  2022-01-28  0:38 ` [PATCH mptcp-next v3 11/14] mptcp: selftests: support MPTCP_PM_CMD_SUBFLOW_CREATE Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 13/14] mptcp: selftests: capture netlink events Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 14/14] selftests: mptcp: functional tests for the userspace PM type Kishen Maloor
  13 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change updates the "pm_nl_ctl" testing sample with a "dsf"
(destroy subflow) option to support the newly added netlink interface
command MPTCP_PM_CMD_SUBFLOW_DESTROY over the chosen MPTCP connection.

E.g. ./pm_nl_ctl dsf lip 10.0.2.1 lport 44567 rip 10.0.2.2 rport 56789
token 823274047

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
 tools/testing/selftests/net/mptcp/pm_nl_ctl.c | 115 ++++++++++++++++++
 1 file changed, 115 insertions(+)

diff --git a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
index 0ba357ab6e4e..8d595b653b11 100644
--- a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
+++ b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
@@ -30,6 +30,7 @@ static void syntax(char *argv[])
 	fprintf(stderr, "\tann <local-ip> id <local-id> token <token> [port <local-port>] [dev <name>] [nolisten]\n");
 	fprintf(stderr, "\trem id <local-id> token <token>\n");
 	fprintf(stderr, "\tcsf lip <local-ip> lid <local-id> rip <remote-ip> rport <remote-port> token <token>\n");
+	fprintf(stderr, "\tdsf lip <local-ip> lport <local-port> rip <remote-ip> rport <remote-port> token <token>\n");
 	fprintf(stderr, "\tdel <id> [<ip>]\n");
 	fprintf(stderr, "\tget <id>\n");
 	fprintf(stderr, "\tset <ip> [flags backup|nobackup|fullmesh|nofullmesh] [port <nr>]\n");
@@ -174,6 +175,118 @@ static int resolve_mptcp_pm_netlink(int fd)
 	return genl_parse_getfamily((void *)data);
 }
 
+int dsf(int fd, int pm_family, int argc, char *argv[])
+{
+	char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+		  NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
+		  1024];
+	struct rtattr *rta, *addr;
+	u_int16_t family, port;
+	struct nlmsghdr *nh;
+	u_int32_t token;
+	int addr_start;
+	int off = 0;
+	int arg;
+
+	const char *params[5];
+
+	memset(params, 0, 5 * sizeof(const char *));
+
+	memset(data, 0, sizeof(data));
+	nh = (void *)data;
+	off = init_genl_req(data, pm_family, MPTCP_PM_CMD_SUBFLOW_DESTROY,
+			    MPTCP_PM_VER);
+
+	if (argc < 12)
+		syntax(argv);
+
+	/* Params recorded in this order:
+	 * <local-ip>, <local-port>, <remote-ip>, <remote-port>, <token>
+	 */
+	for (arg = 2; arg < argc; arg++) {
+		if (!strcmp(argv[arg], "lip")) {
+			if (++arg >= argc)
+				error(1, 0, " missing local IP");
+
+			params[0] = argv[arg];
+		} else if (!strcmp(argv[arg], "lport")) {
+			if (++arg >= argc)
+				error(1, 0, " missing local port");
+
+			params[1] = argv[arg];
+		} else if (!strcmp(argv[arg], "rip")) {
+			if (++arg >= argc)
+				error(1, 0, " missing remote IP");
+
+			params[2] = argv[arg];
+		} else if (!strcmp(argv[arg], "rport")) {
+			if (++arg >= argc)
+				error(1, 0, " missing remote port");
+
+			params[3] = argv[arg];
+		} else if (!strcmp(argv[arg], "token")) {
+			if (++arg >= argc)
+				error(1, 0, " missing token");
+
+			params[4] = argv[arg];
+		} else
+			error(1, 0, "unknown keyword %s", argv[arg]);
+	}
+
+	for (arg = 0; arg < 4; arg = arg + 2) {
+		/*  addr header */
+		addr_start = off;
+		addr = (void *)(data + off);
+		addr->rta_type = NLA_F_NESTED |
+			((arg == 0) ? MPTCP_PM_ATTR_ADDR : MPTCP_PM_ATTR_ADDR_REMOTE);
+		addr->rta_len = RTA_LENGTH(0);
+		off += NLMSG_ALIGN(addr->rta_len);
+
+		/*  addr data */
+		rta = (void *)(data + off);
+		if (inet_pton(AF_INET, params[arg], RTA_DATA(rta))) {
+			family = AF_INET;
+			rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4;
+			rta->rta_len = RTA_LENGTH(4);
+		} else if (inet_pton(AF_INET6, params[arg], RTA_DATA(rta))) {
+			family = AF_INET6;
+			rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6;
+			rta->rta_len = RTA_LENGTH(16);
+		} else
+			error(1, errno, "can't parse ip %s", params[arg]);
+		off += NLMSG_ALIGN(rta->rta_len);
+
+		/* family */
+		rta = (void *)(data + off);
+		rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY;
+		rta->rta_len = RTA_LENGTH(2);
+		memcpy(RTA_DATA(rta), &family, 2);
+		off += NLMSG_ALIGN(rta->rta_len);
+
+		/*  port */
+		port = atoi(params[arg + 1]);
+		rta = (void *)(data + off);
+		rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT;
+		rta->rta_len = RTA_LENGTH(2);
+		memcpy(RTA_DATA(rta), &port, 2);
+		off += NLMSG_ALIGN(rta->rta_len);
+
+		addr->rta_len = off - addr_start;
+	}
+
+	/* token */
+	token = atoi(params[4]);
+	rta = (void *)(data + off);
+	rta->rta_type = MPTCP_PM_ATTR_TOKEN;
+	rta->rta_len = RTA_LENGTH(4);
+	memcpy(RTA_DATA(rta), &token, 4);
+	off += NLMSG_ALIGN(rta->rta_len);
+
+	do_nl_req(fd, nh, off, 0);
+
+	return 0;
+}
+
 int csf(int fd, int pm_family, int argc, char *argv[])
 {
 	char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
@@ -1075,6 +1188,8 @@ int main(int argc, char *argv[])
 		return remove_addr(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "csf"))
 		return csf(fd, pm_family, argc, argv);
+	else if (!strcmp(argv[1], "dsf"))
+		return dsf(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "del"))
 		return del_addr(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "flush"))
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 13/14] mptcp: selftests: capture netlink events
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
                   ` (11 preceding siblings ...)
  2022-01-28  0:38 ` [PATCH mptcp-next v3 12/14] mptcp: selftests: support MPTCP_PM_CMD_SUBFLOW_DESTROY Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  0:38 ` [PATCH mptcp-next v3 14/14] selftests: mptcp: functional tests for the userspace PM type Kishen Maloor
  13 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change adds to self-testing support for the MPTCP netlink interface
by capturing various MPTCP netlink events (and all their metadata)
associated with connections, subflows and local address announcements.
It can be incorporated into self-test scripts that exercise the
MPTCP netlink commands to then precisely validate those operations
through the dispatched MPTCP netlink events in response to those
commands.

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
v3: support reading the new server_side attribute from the CREATED
and ESTABLISHED netlink events
---
 tools/testing/selftests/net/mptcp/pm_nl_ctl.c | 164 +++++++++++++++++-
 1 file changed, 157 insertions(+), 7 deletions(-)

diff --git a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
index 8d595b653b11..b1cd06c88cd7 100644
--- a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
+++ b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
@@ -22,6 +22,9 @@
 #ifndef MPTCP_PM_NAME
 #define MPTCP_PM_NAME		"mptcp_pm"
 #endif
+#ifndef MPTCP_PM_EVENTS
+#define MPTCP_PM_EVENTS		"mptcp_pm_events"
+#endif
 
 static void syntax(char *argv[])
 {
@@ -37,6 +40,7 @@ static void syntax(char *argv[])
 	fprintf(stderr, "\tflush\n");
 	fprintf(stderr, "\tdump\n");
 	fprintf(stderr, "\tlimits [<rcv addr max> <subflow max>]\n");
+	fprintf(stderr, "\tevents\n");
 	exit(0);
 }
 
@@ -88,6 +92,108 @@ static void nl_error(struct nlmsghdr *nh)
 	}
 }
 
+static int capture_events(int fd, int event_group)
+{
+	u_int8_t buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+			NLMSG_ALIGN(sizeof(struct genlmsghdr)) + 1024];
+	struct genlmsghdr *ghdr;
+	struct rtattr *attrs;
+	struct nlmsghdr *nh;
+	int ret = 0;
+	int res_len;
+	int msg_len;
+	fd_set rfds;
+
+	if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
+		       &event_group, sizeof(event_group)) < 0)
+		error(1, errno, "could not join the " MPTCP_PM_EVENTS " mcast group");
+
+	do {
+		FD_ZERO(&rfds);
+		FD_SET(fd, &rfds);
+		res_len = NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+		  NLMSG_ALIGN(sizeof(struct genlmsghdr)) + 1024;
+
+		ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
+
+		if (ret < 0)
+			error(1, ret, "error in select() on NL socket");
+
+		res_len = recv(fd, buffer, res_len, 0);
+		if (res_len < 0)
+			error(1, res_len, "error on recv() from NL socket");
+
+		nh = (struct nlmsghdr *)buffer;
+
+		for (; NLMSG_OK(nh, res_len); nh = NLMSG_NEXT(nh, res_len)) {
+			if (nh->nlmsg_type == NLMSG_ERROR)
+				error(1, NLMSG_ERROR, "received invalid NL message");
+
+			ghdr = (struct genlmsghdr *)NLMSG_DATA(nh);
+
+			if (ghdr->cmd == 0)
+				continue;
+
+			fprintf(stderr, "type:%d", ghdr->cmd);
+
+			msg_len = nh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
+
+			attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
+			while (RTA_OK(attrs, msg_len)) {
+				if (attrs->rta_type == MPTCP_ATTR_TOKEN)
+					fprintf(stderr, ",token:%u", *(__u32 *)RTA_DATA(attrs));
+				else if (attrs->rta_type == MPTCP_ATTR_FAMILY)
+					fprintf(stderr, ",family:%u", *(__u16 *)RTA_DATA(attrs));
+				else if (attrs->rta_type == MPTCP_ATTR_LOC_ID)
+					fprintf(stderr, ",loc_id:%u", *(__u8 *)RTA_DATA(attrs));
+				else if (attrs->rta_type == MPTCP_ATTR_REM_ID)
+					fprintf(stderr, ",rem_id:%u", *(__u8 *)RTA_DATA(attrs));
+				else if (attrs->rta_type == MPTCP_ATTR_SADDR4) {
+					u_int32_t saddr4 = ntohl(*(__u32 *)RTA_DATA(attrs));
+
+					fprintf(stderr, ",saddr4:%u.%u.%u.%u", saddr4 >> 24,
+					       (saddr4 >> 16) & 0xFF, (saddr4 >> 8) & 0xFF,
+					       (saddr4 & 0xFF));
+				} else if (attrs->rta_type == MPTCP_ATTR_SADDR6) {
+					char buf[INET6_ADDRSTRLEN];
+
+					if (inet_ntop(AF_INET6, RTA_DATA(attrs), buf,
+						      sizeof(buf)) != NULL)
+						fprintf(stderr, ",saddr6:%s", buf);
+				} else if (attrs->rta_type == MPTCP_ATTR_DADDR4) {
+					u_int32_t daddr4 = ntohl(*(__u32 *)RTA_DATA(attrs));
+
+					fprintf(stderr, ",daddr4:%u.%u.%u.%u", daddr4 >> 24,
+					       (daddr4 >> 16) & 0xFF, (daddr4 >> 8) & 0xFF,
+					       (daddr4 & 0xFF));
+				} else if (attrs->rta_type == MPTCP_ATTR_DADDR6) {
+					char buf[INET6_ADDRSTRLEN];
+
+					if (inet_ntop(AF_INET6, RTA_DATA(attrs), buf,
+						      sizeof(buf)) != NULL)
+						fprintf(stderr, ",daddr6:%s", buf);
+				} else if (attrs->rta_type == MPTCP_ATTR_SPORT)
+					fprintf(stderr, ",sport:%u",
+						ntohs(*(__u16 *)RTA_DATA(attrs)));
+				else if (attrs->rta_type == MPTCP_ATTR_DPORT)
+					fprintf(stderr, ",dport:%u",
+						ntohs(*(__u16 *)RTA_DATA(attrs)));
+				else if (attrs->rta_type == MPTCP_ATTR_BACKUP)
+					fprintf(stderr, ",backup:%u", *(__u8 *)RTA_DATA(attrs));
+				else if (attrs->rta_type == MPTCP_ATTR_ERROR)
+					fprintf(stderr, ",error:%u", *(__u8 *)RTA_DATA(attrs));
+				else if (attrs->rta_type == MPTCP_ATTR_SERVER_SIDE)
+					fprintf(stderr, ",server_side:%u", *(__u8 *)RTA_DATA(attrs));
+
+				attrs = RTA_NEXT(attrs, msg_len);
+			}
+		}
+		fprintf(stderr, "\n");
+	} while (1);
+
+	return 0;
+}
+
 /* do a netlink command and, if max > 0, fetch the reply  */
 static int do_nl_req(int fd, struct nlmsghdr *nh, int len, int max)
 {
@@ -121,11 +227,18 @@ static int do_nl_req(int fd, struct nlmsghdr *nh, int len, int max)
 	return ret;
 }
 
-static int genl_parse_getfamily(struct nlmsghdr *nlh)
+static int genl_parse_getfamily(struct nlmsghdr *nlh, int *pm_family,
+				int *events_mcast_grp)
 {
 	struct genlmsghdr *ghdr = NLMSG_DATA(nlh);
 	int len = nlh->nlmsg_len;
 	struct rtattr *attrs;
+	struct rtattr *grps;
+	struct rtattr *grp;
+	int got_events_grp;
+	int got_family;
+	int grps_len;
+	int grp_len;
 
 	if (nlh->nlmsg_type != GENL_ID_CTRL)
 		error(1, errno, "Not a controller message, len=%d type=0x%x\n",
@@ -140,9 +253,42 @@ static int genl_parse_getfamily(struct nlmsghdr *nlh)
 		error(1, errno, "Unknown controller command %d\n", ghdr->cmd);
 
 	attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
+	got_family = 0;
+	got_events_grp = 0;
+
 	while (RTA_OK(attrs, len)) {
-		if (attrs->rta_type == CTRL_ATTR_FAMILY_ID)
-			return *(__u16 *)RTA_DATA(attrs);
+		if (attrs->rta_type == CTRL_ATTR_FAMILY_ID) {
+			*pm_family = *(__u16 *)RTA_DATA(attrs);
+			got_family = 1;
+		} else if (attrs->rta_type == CTRL_ATTR_MCAST_GROUPS) {
+			grps = RTA_DATA(attrs);
+			grps_len = RTA_PAYLOAD(attrs);
+
+			while (RTA_OK(grps, grps_len)) {
+				grp = RTA_DATA(grps);
+				grp_len = RTA_PAYLOAD(grps);
+				got_events_grp = 0;
+
+				while (RTA_OK(grp, grp_len)) {
+					if (grp->rta_type == CTRL_ATTR_MCAST_GRP_ID)
+						*events_mcast_grp = *(__u32 *)RTA_DATA(grp);
+					else if (grp->rta_type == CTRL_ATTR_MCAST_GRP_NAME &&
+						 !strcmp(RTA_DATA(grp), MPTCP_PM_EVENTS))
+						got_events_grp = 1;
+
+					grp = RTA_NEXT(grp, grp_len);
+				}
+
+				if (got_events_grp)
+					break;
+
+				grps = RTA_NEXT(grps, grps_len);
+			}
+		}
+
+		if (got_family && got_events_grp)
+			return 0;
+
 		attrs = RTA_NEXT(attrs, len);
 	}
 
@@ -150,7 +296,7 @@ static int genl_parse_getfamily(struct nlmsghdr *nlh)
 	return -1;
 }
 
-static int resolve_mptcp_pm_netlink(int fd)
+static int resolve_mptcp_pm_netlink(int fd, int *pm_family, int *events_mcast_grp)
 {
 	char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
 		  NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
@@ -172,7 +318,7 @@ static int resolve_mptcp_pm_netlink(int fd)
 	off += NLMSG_ALIGN(rta->rta_len);
 
 	do_nl_req(fd, nh, off, sizeof(data));
-	return genl_parse_getfamily((void *)data);
+	return genl_parse_getfamily((void *)data, pm_family, events_mcast_grp);
 }
 
 int dsf(int fd, int pm_family, int argc, char *argv[])
@@ -1169,7 +1315,9 @@ int set_flags(int fd, int pm_family, int argc, char *argv[])
 
 int main(int argc, char *argv[])
 {
-	int fd, pm_family;
+	int events_mcast_grp;
+	int pm_family;
+	int fd;
 
 	if (argc < 2)
 		syntax(argv);
@@ -1178,7 +1326,7 @@ int main(int argc, char *argv[])
 	if (fd == -1)
 		error(1, errno, "socket netlink");
 
-	pm_family = resolve_mptcp_pm_netlink(fd);
+	resolve_mptcp_pm_netlink(fd, &pm_family, &events_mcast_grp);
 
 	if (!strcmp(argv[1], "add"))
 		return add_addr(fd, pm_family, argc, argv);
@@ -1202,6 +1350,8 @@ int main(int argc, char *argv[])
 		return get_set_limits(fd, pm_family, argc, argv);
 	else if (!strcmp(argv[1], "set"))
 		return set_flags(fd, pm_family, argc, argv);
+	else if (!strcmp(argv[1], "events"))
+		return capture_events(fd, events_mcast_grp);
 
 	fprintf(stderr, "unknown sub-command: %s", argv[1]);
 	syntax(argv);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH mptcp-next v3 14/14] selftests: mptcp: functional tests for the userspace PM type
  2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
                   ` (12 preceding siblings ...)
  2022-01-28  0:38 ` [PATCH mptcp-next v3 13/14] mptcp: selftests: capture netlink events Kishen Maloor
@ 2022-01-28  0:38 ` Kishen Maloor
  2022-01-28  6:51   ` Geliang Tang
  13 siblings, 1 reply; 24+ messages in thread
From: Kishen Maloor @ 2022-01-28  0:38 UTC (permalink / raw)
  To: kishen.maloor, mptcp

This change adds a selftest script that performs a comprehensive
behavioral/functional test of all userspace PM capabilities by exercising
all the newly added APIs and changes to support said capabilities.

Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
---
v3: configure net.mptcp.userspace_pm_local_addr_max via sysctl on
the namespaces established for testing, read and verify the new
"server_side" attribute from netlink events upon connection
establishment, new test case for sysctl configured local_addr limit,
added descriptive remarks throughout script
---
 .../selftests/net/mptcp/userspace_pm.sh       | 656 ++++++++++++++++++
 1 file changed, 656 insertions(+)
 create mode 100755 tools/testing/selftests/net/mptcp/userspace_pm.sh

diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh
new file mode 100755
index 000000000000..cc6c03626c21
--- /dev/null
+++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh
@@ -0,0 +1,656 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+ip -Version > /dev/null 2>&1
+if [ $? -ne 0 ];then
+	echo "SKIP: Cannot not run test without ip tool"
+	exit 1
+fi
+
+ANNOUNCED=6        # MPTCP_EVENT_ANNOUNCED
+REMOVED=7          # MPTCP_EVENT_REMOVED
+SUB_ESTABLISHED=10 # MPTCP_EVENT_SUB_ESTABLISHED
+SUB_CLOSED=11      # MPTCP_EVENT_SUB_CLOSED
+
+AF_INET=2
+AF_INET6=10
+
+evts_pid=0
+client4_pid=0
+server4_pid=0
+client6_pid=0
+server6_pid=0
+client4_token=""
+server4_token=""
+client6_token=""
+server6_token=""
+client4_port=0;
+client6_port=0;
+app4_port=50002
+new4_port=50003
+app6_port=50004
+client_addr_id=${RANDOM:0:2}
+server_addr_id=${RANDOM:0:2}
+
+sec=$(date +%s)
+rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
+ns1="ns1-$rndh"
+ns2="ns2-$rndh"
+
+cleanup()
+{
+	echo "cleanup"
+
+	# Terminate the MPTCP connection and related processes
+	kill -SIGUSR1 $client4_pid > /dev/null 2>&1
+	kill $server4_pid > /dev/null 2>&1
+	kill -SIGUSR1 $client6_pid > /dev/null 2>&1
+	kill $server6_pid > /dev/null 2>&1
+
+	kill $evts_pid > /dev/null 2>&1
+
+	local netns
+	for netns in "$ns1" "$ns2" ;do
+		ip netns del $netns
+	done
+}
+
+trap cleanup EXIT
+
+# Create and configure network namespaces for testing
+for i in "$ns1" "$ns2" ;do
+	ip netns add $i || exit 1
+	ip -net $i link set lo up
+	ip netns exec $i sysctl -q net.mptcp.enabled=1
+	ip netns exec $i sysctl -q net.mptcp.pm_type=1
+	ip netns exec $i sysctl -q net.mptcp.userspace_pm_local_addr_max=2
+done
+
+#  "$ns1"              ns2
+#     ns1eth2    ns2eth1
+
+ip link add ns1eth2 netns "$ns1" type veth peer name ns2eth1 netns "$ns2"
+
+# Add IPv4/v6 addresses to the namespaces
+ip -net "$ns1" addr add 10.0.1.1/24 dev ns1eth2
+ip -net "$ns1" addr add 10.0.2.1/24 dev ns1eth2
+ip -net "$ns1" addr add dead:beef:1::1/64 dev ns1eth2 nodad
+ip -net "$ns1" addr add dead:beef:2::1/64 dev ns1eth2 nodad
+ip -net "$ns1" link set ns1eth2 up
+
+ip -net "$ns2" addr add 10.0.1.2/24 dev ns2eth1
+ip -net "$ns2" addr add 10.0.2.2/24 dev ns2eth1
+ip -net "$ns2" addr add dead:beef:1::2/64 dev ns2eth1 nodad
+ip -net "$ns2" addr add dead:beef:2::2/64 dev ns2eth1 nodad
+ip -net "$ns2" link set ns2eth1 up
+
+printf "Created network namespaces ns1, ns2         \t\t\t[OK]\n"
+
+make_file()
+{
+	# Store a chunk of data in a file to transmit over an MPTCP connection
+	local name=$1
+	local who=$2
+	local ksize=1
+
+	dd if=/dev/urandom of="$name" bs=1024 count=$ksize 2> /dev/null
+	echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name"
+}
+
+make_connection()
+{
+	local file=$(mktemp)
+	make_file "$file" "client"
+
+	local is_v6=$1
+	local app_port=$app4_port
+	local connect_addr="10.0.1.1"
+	local listen_addr="0.0.0.0"
+	if [ "$is_v6" = "v6" ]
+	then
+		connect_addr="dead:beef:1::1"
+		listen_addr="::"
+		app_port=$app6_port
+	else
+		is_v6="v4"
+	fi
+
+	# Capture netlink events over the two network namespaces running
+	# the MPTCP client and server
+	local client_evts=$(mktemp)
+	:>"$client_evts"
+	ip netns exec $ns2 ./pm_nl_ctl events >> "$client_evts" 2>&1 &
+	local client_evts_pid=$!
+	local server_evts=$(mktemp)
+	:>"$server_evts"
+	ip netns exec $ns1 ./pm_nl_ctl events >> "$server_evts" 2>&1 &
+	local server_evts_pid=$!
+	sleep 0.1
+
+	# Run the server
+	ip netns exec $ns1 \
+			./mptcp_connect -s MPTCP -w 300 -p $app_port -l $listen_addr 2>&1 > /dev/null &
+	local server_pid=$!
+	sleep 0.1
+
+	# Run the client, transfer $file and stay connected to the server
+	# to conduct tests
+	ip netns exec $ns2 \
+			./mptcp_connect -s MPTCP -w 300 -m sendfile -p $app_port $connect_addr 2>&1 > /dev/null < $file &
+	local client_pid=$!
+	sleep 0.1
+
+	# Capture client/server attributes from MPTCP connection netlink events
+	kill $client_evts_pid
+	local client_token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
+	local client_port=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
+	local client_serverside=$(sed -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
+
+	kill $server_evts_pid
+	local server_token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
+	local server_serverside=$(sed -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
+
+	rm -f "$client_evts" "$server_evts" "$file"
+
+	if [ $client_token != "" ] && [ $server_token != "" ] && [ $client_serverside = 0 ] && [ $server_serverside = 1 ]
+	then
+		printf "Established IP%s MPTCP Connection ns2 => ns1    \t\t[OK]\n" $is_v6
+	else
+		exit 1
+	fi
+
+	if [ "$is_v6" = "v6" ]
+	then
+		client6_token=$client_token
+		server6_token=$server_token
+		client6_port=$client_port
+		client6_pid=$client_pid
+		server6_pid=$server_pid
+	else
+		client4_token=$client_token
+		server4_token=$server_token
+		client4_port=$client_port
+		client4_pid=$client_pid
+		server4_pid=$server_pid
+	fi
+}
+
+verify_announce_event()
+{
+	local evt=$1
+	local e_type=$2
+	local e_token=$3
+	local e_addr=$4
+	local e_id=$5
+	local e_dport=$6
+	local e_af=$7
+
+	local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+	local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+	local addr=""
+	if [ "$e_af" = "v6" ]
+	then
+		addr=$(sed -n 's/.*\(daddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
+	else
+		addr=$(sed -n 's/.*\(daddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
+	fi
+	local dport=$(sed -n 's/.*\(dport:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+	local id=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$addr" = "$e_addr" ] && [ "$dport" = "$e_dport" ] && [ "$id" = "$e_id" ]
+	then
+		printf "[OK]\n"
+		return 0
+	fi
+	printf "[FAIL]\n"
+	exit 1
+}
+
+test_announce()
+{
+	local evts=$(mktemp)
+	# Capture events on the network namespace running the server
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
+	evts_pid=$!
+	sleep 0.1
+
+	# ADD_ADDR using an invalid token should result in no action
+	local invalid_token=$(( $client4_token - 1))
+	ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $invalid_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
+	local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+	printf "ADD_ADDR 10.0.2.2 (ns2) => ns1, invalid token    \t\t"
+        if [ "$type" = "" ]
+	then
+		printf "[OK]\n"
+	else
+		printf "[FAIL]\n"
+		exit 1
+	fi
+
+	# ADD_ADDR from the client to server machine reusing the subflow port
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
+	printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, reuse port \t\t" $client_addr_id
+	sleep 0.1
+	verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2" "$client_addr_id" "$client4_port"
+
+	# ADD_ADDR6 from the client to server machine reusing the subflow port
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl ann dead:beef:2::2 token $client6_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
+	printf "ADD_ADDR6 id:%d dead:beef:2::2 (ns2) => ns1, reuse port\t\t" $client_addr_id
+	sleep 0.1
+	verify_announce_event "$evts" "$ANNOUNCED" "$server6_token" "dead:beef:2::2" "$client_addr_id" "$client6_port" "v6"
+
+	# ADD_ADDR from the client to server machine using a new port
+	:>"$evts"
+	client_addr_id=$((client_addr_id+1))
+	ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 port $new4_port 2>&1 > /dev/null
+	printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, new port \t\t\t" $client_addr_id
+	sleep 0.1
+	verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2" "$client_addr_id" "$new4_port"
+
+	kill $evts_pid
+
+	# Capture events on the network namespace running the client
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
+	evts_pid=$!
+	sleep 0.1
+
+	# ADD_ADDR from the server to client machine reusing the subflow port
+	ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id dev ns1eth2 2>&1 > /dev/null
+	printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, reuse port \t\t" $server_addr_id
+	sleep 0.1
+	verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1" "$server_addr_id" "$app4_port"
+
+	# ADD_ADDR6 from the server to client machine reusing the subflow port
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl ann dead:beef:2::1 token $server6_token id $server_addr_id dev ns1eth2 2>&1 > /dev/null
+	printf "ADD_ADDR6 id:%d dead:beef:2::1 (ns1) => ns2, reuse port\t\t" $server_addr_id
+	sleep 0.1
+	verify_announce_event "$evts" "$ANNOUNCED" "$client6_token" "dead:beef:2::1" "$server_addr_id" "$app6_port" "v6"
+
+	# ADD_ADDR from the server to client machine using a new port
+	:>"$evts"
+	server_addr_id=$((server_addr_id+1))
+	ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id dev ns1eth2 port $new4_port 2>&1 > /dev/null
+	printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, new port \t\t\t" $server_addr_id
+	sleep 0.1
+	verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1" "$server_addr_id" "$new4_port"
+
+	kill $evts_pid
+	rm -f "$evts"
+}
+
+verify_remove_event()
+{
+	local evt=$1
+	local e_type=$2
+	local e_token=$3
+	local e_id=$4
+
+	local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+	local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+	local id=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$id" = "$e_id" ]
+	then
+		printf "[OK]\n"
+		return 0
+	fi
+	printf "[FAIL]\n"
+	exit 1
+}
+
+test_remove()
+{
+	local evts=$(mktemp)
+
+	# Capture events on the network namespace running the server
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
+	evts_pid=$!
+	sleep 0.1
+
+	# RM_ADDR using an invalid token should result in no action
+	local invalid_token=$(( $client4_token - 1 ))
+	ip netns exec $ns2 ./pm_nl_ctl rem token $invalid_token id $client_addr_id 2>&1 > /dev/null
+	printf "RM_ADDR id:%d ns2 => ns1, invalid token                    \t" $client_addr_id
+	local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+	if [ "$type" = "" ]
+	then
+		printf "[OK]\n"
+	else
+		printf "[FAIL]\n"
+	fi
+
+	# RM_ADDR using an invalid addr id should result in no action
+	local invalid_id=$(( $client_addr_id + 1 ))
+	ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $invalid_id 2>&1 > /dev/null
+	printf "RM_ADDR id:%d ns2 => ns1, invalid id                    \t" $invalid_id
+	type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+	if [ "$type" = "" ]
+	then
+		printf "[OK]\n"
+	else
+		printf "[FAIL]\n"
+	fi
+
+	# RM_ADDR from the client to server machine
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $client_addr_id 2>&1 > /dev/null
+	printf "RM_ADDR id:%d ns2 => ns1                                \t" $client_addr_id
+	sleep 0.1
+	verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id"
+
+	# RM_ADDR from the client to server machine
+	:>"$evts"
+	client_addr_id=$(( $client_addr_id - 1 ))
+	ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $client_addr_id 2>&1 > /dev/null
+	printf "RM_ADDR id:%d ns2 => ns1                                \t" $client_addr_id
+	sleep 0.1
+	verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id"
+
+	# RM_ADDR6 from the client to server machine
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl rem token $client6_token id $client_addr_id 2>&1 > /dev/null
+	printf "RM_ADDR6 id:%d ns2 => ns1                               \t" $client_addr_id
+	sleep 0.1
+	verify_remove_event "$evts" "$REMOVED" "$server6_token" "$client_addr_id"
+
+	kill $evts_pid
+
+	# Capture events on the network namespace running the client
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
+	evts_pid=$!
+	sleep 0.1
+
+	# RM_ADDR from the server to client machine
+	ip netns exec $ns1 ./pm_nl_ctl rem token $server4_token id $server_addr_id 2>&1 > /dev/null
+	printf "RM_ADDR id:%d ns1 => ns2                                \t" $server_addr_id
+	sleep 0.1
+	verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id"
+
+	# RM_ADDR from the server to client machine
+	:>"$evts"
+	server_addr_id=$(( $server_addr_id - 1 ))
+	ip netns exec $ns1 ./pm_nl_ctl rem token $server4_token id $server_addr_id 2>&1 > /dev/null
+	printf "RM_ADDR id:%d ns1 => ns2                                \t" $server_addr_id
+	sleep 0.1
+	verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id"
+
+	# RM_ADDR6 from the server to client machine
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl rem token $server6_token id $server_addr_id 2>&1 > /dev/null
+	printf "RM_ADDR6 id:%d ns1 => ns2                               \t" $server_addr_id
+	sleep 0.1
+	verify_remove_event "$evts" "$REMOVED" "$client6_token" "$server_addr_id"
+
+	kill $evts_pid
+	rm -f "$evts"
+}
+
+verify_subflow_events()
+{
+	local evt=$1
+	local e_type=$2
+	local e_token=$3
+	local e_family=$4
+	local e_saddr=$5
+	local e_daddr=$6
+	local e_dport=$7
+	local e_locid=$8
+	local e_remid=$9
+	shift 2
+	local e_from=$8
+	local e_to=$9
+
+	if [ "$e_type" = "$SUB_ESTABLISHED" ]
+	then
+		if [ "$e_family" = "$AF_INET6" ]
+		then
+			printf "CREATE_SUBFLOW6 %s (%s) => %s (%s)    " $e_saddr $e_from $e_daddr $e_to
+		else
+			printf "CREATE_SUBFLOW %s (%s) => %s (%s)         \t" $e_saddr $e_from $e_daddr $e_to
+		fi
+	else
+		if [ "$e_family" = "$AF_INET6" ]
+		then
+			printf "DESTROY_SUBFLOW6 %s (%s) => %s (%s)   " $e_saddr $e_from $e_daddr $e_to
+		else
+			printf "DESTROY_SUBFLOW %s (%s) => %s (%s)         \t" $e_saddr $e_from $e_daddr $e_to
+		fi
+	fi
+
+	local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+	local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+	local family=$(sed -n 's/.*\(family:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+	local dport=$(sed -n 's/.*\(dport:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+	local locid=$(sed -n 's/.*\(loc_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+	local remid=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
+	local saddr=""
+	local daddr=""
+	if [ "$family" = "$AF_INET6" ]
+	then
+		saddr=$(sed -n 's/.*\(saddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
+		daddr=$(sed -n 's/.*\(daddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
+	else
+		saddr=$(sed -n 's/.*\(saddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
+		daddr=$(sed -n 's/.*\(daddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
+	fi
+
+        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$daddr" = "$e_daddr" ] && [ "$e_dport" = "$dport" ] && [ "$family" = "$e_family" ] && [ "$saddr" = "$e_saddr" ] && [ "$e_locid" = "$locid" ] && [ "$e_remid" = "$remid" ]
+	then
+		printf "[OK]\n"
+		return 0
+	fi
+	printf "[FAIL]\n"
+	exit 1
+}
+
+test_subflows()
+{
+	local evts=$(mktemp)
+	# Capture events on the network namespace running the server
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
+	evts_pid=$!
+	sleep 0.1
+
+	# ADD_ADDR from client to server machine reusing the subflow port
+	ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id 2>&1 > /dev/null
+	sleep 0.1
+
+	# CREATE_SUBFLOW from server to client machine
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2 rport $client4_port token $server4_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2"
+
+	local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+
+	# DESTROY_SUBFLOW from server to client machine
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl dsf lip 10.0.2.1 lport $sport rip 10.0.2.2 rport $client4_port token $server4_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2"
+
+	# RM_ADDR from client to server machine
+	ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client4_token 2>&1 > /dev/null
+	sleep 0.1
+
+	# ADD_ADDR6 from client to server machine reusing the subflow port
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl ann dead:beef:2::2 token $client6_token id $client_addr_id 2>&1 > /dev/null
+	sleep 0.1
+
+	# CREATE_SUBFLOW6 from server to client machine
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl csf lip dead:beef:2::1 lid 23 rip dead:beef:2::2 rport $client6_port token $server6_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server6_token" "$AF_INET6" "dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23" "$client_addr_id" "ns1" "ns2"
+
+	local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+
+	# DESTROY_SUBFLOW6 from server to client machine
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl dsf lip dead:beef:2::1 lport $sport rip dead:beef:2::2 rport $client6_port token $server6_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_CLOSED" "$server6_token" "$AF_INET6" "dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23" "$client_addr_id" "ns1" "ns2"
+
+	# RM_ADDR from client to server machine
+	ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client6_token 2>&1 > /dev/null
+	sleep 0.1
+
+	# ADD_ADDR from client to server machine using a new port
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id port $new4_port 2>&1 > /dev/null
+	sleep 0.1
+
+	# CREATE_SUBFLOW from server to client machine
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2 rport $new4_port token $server4_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$new4_port" "23" "$client_addr_id" "ns1" "ns2"
+
+        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+
+	# DESTROY_SUBFLOW from server to client machine
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl dsf lip 10.0.2.1 lport $sport rip 10.0.2.2 rport $new4_port token $server4_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$new4_port" "23" "$client_addr_id" "ns1" "ns2"
+
+	# RM_ADDR from client to server machine
+	ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client4_token 2>&1 > /dev/null
+
+	kill $evts_pid
+
+	# Capture events on the network namespace running the client
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
+	evts_pid=$!
+	sleep 0.1
+
+	# ADD_ADDR from server to client machine reusing the subflow port
+	ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id 2>&1 > /dev/null
+	sleep 0.1
+
+	# CREATE_SUBFLOW from client to server machine
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport $app4_port token $client4_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1"
+
+        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+
+	# DESTROY_SUBFLOW from client to server machine
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl dsf lip 10.0.2.2 lport $sport rip 10.0.2.1 rport $app4_port token $client4_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1"
+
+	# RM_ADDR from server to client machine
+	ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server4_token 2>&1 > /dev/null
+	sleep 0.1
+
+	# ADD_ADDR6 from server to client machine reusing the subflow port
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl ann dead:beef:2::1 token $server6_token id $server_addr_id 2>&1 > /dev/null
+	sleep 0.1
+
+	# CREATE_SUBFLOW6 from client to server machine
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl csf lip dead:beef:2::2 lid 23 rip dead:beef:2::1 rport $app6_port token $client6_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client6_token" "$AF_INET6" "dead:beef:2::2" "dead:beef:2::1" "$app6_port" "23" "$server_addr_id" "ns2" "ns1"
+
+	local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+
+	# DESTROY_SUBFLOW6 from client to server machine
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl dsf lip dead:beef:2::2 lport $sport rip dead:beef:2::1 rport $app6_port token $client6_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_CLOSED" "$client6_token" "$AF_INET6" "dead:beef:2::2" "dead:beef:2::1" "$app6_port" "23" "$server_addr_id" "ns2" "ns1"
+
+	# RM_ADDR6 from server to client machine
+	ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server6_token 2>&1 > /dev/null
+	sleep 0.1
+
+	# ADD_ADDR from client to server machine using a new port
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id port $new4_port 2>&1 > /dev/null
+	sleep 0.1
+
+	# CREATE_SUBFLOW from client to server machine
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport $new4_port token $client4_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1"
+
+        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+
+	# DESTROY_SUBFLOW from client to server machine
+	:>"$evts"
+	ip netns exec $ns2 ./pm_nl_ctl dsf lip 10.0.2.2 lport $sport rip 10.0.2.1 rport $new4_port token $client4_token 2>&1 > /dev/null
+	sleep 0.1
+	verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1"
+
+	# RM_ADDR from server to client machine
+	ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server4_token 2>&1 > /dev/null
+
+	kill $evts_pid
+	rm -f "$evts"
+}
+
+test_local_addr_limit()
+{
+	printf "Test sysctl: net.mptcp.userspace_pm_local_addr_max=1\t\t"
+	# Limit the # of local addrs for both namespaces to 1
+	for i in "$ns1" "$ns2" ;do
+		ip netns exec $i sysctl -q net.mptcp.userspace_pm_local_addr_max=1
+	done
+
+	local evts=$(mktemp)
+	# Capture events from the network namespace running the server
+	:>"$evts"
+	ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
+	evts_pid=$!
+	sleep 0.1
+
+	:>"$evts"
+	# ADD_ADDR from the client to server machine -> success
+	ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 2>&1 > /dev/nulls
+	sleep 0.1
+	local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+        if [ "$type" = "" ]
+	then
+		printf "[FAIL]\n"
+		exit 1
+	fi
+
+	:>"$evts"
+	# ADD_ADDR from the client to server machine -> fail (limit already met)
+	client_addr_id=$((client_addr_id+1))
+	ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 port $new4_port 2>&1 > /dev/null
+	sleep 0.1
+	type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+        if [ "$type" = "" ]
+	then
+		printf "[OK]\n"
+	else
+		printf "[FAIL]\n"
+		exit 1
+	fi
+
+	kill $evts_pid
+	rm -f "$evts"
+}
+
+make_connection
+make_connection "v6"
+test_announce
+test_remove
+test_subflows
+test_local_addr_limit
+exit 0
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH mptcp-next v3 01/14] mptcp: add sysctl param to limit userspace PM activity
  2022-01-28  0:38 ` [PATCH mptcp-next v3 01/14] mptcp: add sysctl param to limit userspace PM activity Kishen Maloor
@ 2022-01-28  6:38   ` Geliang Tang
  2022-01-31 22:23     ` Kishen Maloor
  0 siblings, 1 reply; 24+ messages in thread
From: Geliang Tang @ 2022-01-28  6:38 UTC (permalink / raw)
  To: Kishen Maloor; +Cc: MPTCP Upstream

Hi Kishen,

Kishen Maloor <kishen.maloor@intel.com> 于2022年1月28日周五 08:39写道:
>
> This change adds a new configurable param userspace_pm_local_addr_max
> to set a limit on the number of local addresses that may be recorded
> by a userspace path manager in the context of any MPTCP connection.
> Consequently, it also limits the # of subflows, i.e. to as many that
> could be established via the bounded set of local addresses.
>
> The patch also ensures that other stats inside struct mptcp_pm_data
> that are updated along kernel code paths exercised by userspace PMs
> remain untouched for userspace PM managed sockets.
>
> Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
> ---
>  net/mptcp/ctrl.c       | 17 +++++++++++++++++
>  net/mptcp/pm.c         |  4 +++-
>  net/mptcp/pm_netlink.c |  3 +++
>  net/mptcp/protocol.h   |  1 +
>  4 files changed, 24 insertions(+), 1 deletion(-)
>
> diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c
> index ae20b7d92e28..dffc2da6d713 100644
> --- a/net/mptcp/ctrl.c
> +++ b/net/mptcp/ctrl.c
> @@ -19,6 +19,7 @@ static int mptcp_pernet_id;
>
>  #ifdef CONFIG_SYSCTL
>  static int mptcp_pm_type_max = __MPTCP_PM_TYPE_MAX;
> +static int mptcp_max_addrs = MPTCP_PM_MAX_ADDR_ID;
>  #endif
>
>  struct mptcp_pernet {
> @@ -32,6 +33,7 @@ struct mptcp_pernet {
>         u8 checksum_enabled;
>         u8 allow_join_initial_addr_port;
>         u8 pm_type;
> +       u8 userspace_pm_local_addr_max;
>  };
>
>  static struct mptcp_pernet *mptcp_get_pernet(const struct net *net)
> @@ -69,6 +71,11 @@ int mptcp_get_pm_type(const struct net *net)
>         return mptcp_get_pernet(net)->pm_type;
>  }
>
> +unsigned int mptcp_get_userspace_pm_local_addr_max(const struct net *net)
> +{
> +       return mptcp_get_pernet(net)->userspace_pm_local_addr_max;
> +}
> +
>  static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
>  {
>         pernet->mptcp_enabled = 1;
> @@ -77,6 +84,7 @@ static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
>         pernet->allow_join_initial_addr_port = 1;
>         pernet->stale_loss_cnt = 4;
>         pernet->pm_type = MPTCP_PM_TYPE_KERNEL;
> +       pernet->userspace_pm_local_addr_max = 10;
>  }
>
>  #ifdef CONFIG_SYSCTL
> @@ -128,6 +136,14 @@ static struct ctl_table mptcp_sysctl_table[] = {
>                 .extra1       = SYSCTL_ZERO,
>                 .extra2       = &mptcp_pm_type_max
>         },
> +       {
> +               .procname = "userspace_pm_local_addr_max",
> +               .maxlen = sizeof(u8),
> +               .mode = 0644,
> +               .proc_handler = proc_dou8vec_minmax,
> +               .extra1       = SYSCTL_ONE,
> +               .extra2       = &mptcp_max_addrs
> +       },
>         {}
>  };
>
> @@ -149,6 +165,7 @@ static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
>         table[3].data = &pernet->allow_join_initial_addr_port;
>         table[4].data = &pernet->stale_loss_cnt;
>         table[5].data = &pernet->pm_type;
> +       table[6].data = &pernet->userspace_pm_local_addr_max;
>
>         hdr = register_net_sysctl(net, MPTCP_SYSCTL_PATH, table);
>         if (!hdr)
> diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
> index e5d5cb847209..24839cd7025c 100644
> --- a/net/mptcp/pm.c
> +++ b/net/mptcp/pm.c
> @@ -182,7 +182,9 @@ void mptcp_pm_subflow_check_next(struct mptcp_sock *msk, const struct sock *ssk,
>         bool update_subflows;
>
>         update_subflows = (ssk->sk_state == TCP_CLOSE) &&
> -                         (subflow->request_join || subflow->mp_join);
> +                         (subflow->request_join || subflow->mp_join) &&
> +                         READ_ONCE(pm->pm_type) == MPTCP_PM_TYPE_KERNEL;

How about using mptcp_pm_is_userspace() instead for all the
MPTCP_PM_TYPE_KERNEL type check in this series?

Thanks,
-Geliang

> +
>         if (!READ_ONCE(pm->work_pending) && !update_subflows)
>                 return;
>
> diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
> index 126cc961a4fd..882988e43d76 100644
> --- a/net/mptcp/pm_netlink.c
> +++ b/net/mptcp/pm_netlink.c
> @@ -933,6 +933,9 @@ static void mptcp_pm_nl_rm_addr_or_subflow(struct mptcp_sock *msk,
>                 if (!removed)
>                         continue;
>
> +               if (READ_ONCE(msk->pm.pm_type) != MPTCP_PM_TYPE_KERNEL)
> +                       continue;
> +
>                 if (rm_type == MPTCP_MIB_RMADDR) {
>                         msk->pm.add_addr_accepted--;
>                         WRITE_ONCE(msk->pm.accept_addr, true);
> diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
> index 30006735afb7..6cfa8ec26482 100644
> --- a/net/mptcp/protocol.h
> +++ b/net/mptcp/protocol.h
> @@ -852,6 +852,7 @@ unsigned int mptcp_pm_get_add_addr_signal_max(struct mptcp_sock *msk);
>  unsigned int mptcp_pm_get_add_addr_accept_max(struct mptcp_sock *msk);
>  unsigned int mptcp_pm_get_subflows_max(struct mptcp_sock *msk);
>  unsigned int mptcp_pm_get_local_addr_max(struct mptcp_sock *msk);
> +unsigned int mptcp_get_userspace_pm_local_addr_max(const struct net *net);
>
>  void mptcp_sockopt_sync(struct mptcp_sock *msk, struct sock *ssk);
>  void mptcp_sockopt_sync_locked(struct mptcp_sock *msk, struct sock *ssk);
> --
> 2.31.1
>
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH mptcp-next v3 14/14] selftests: mptcp: functional tests for the userspace PM type
  2022-01-28  0:38 ` [PATCH mptcp-next v3 14/14] selftests: mptcp: functional tests for the userspace PM type Kishen Maloor
@ 2022-01-28  6:51   ` Geliang Tang
  2022-01-28 19:48     ` Mat Martineau
  0 siblings, 1 reply; 24+ messages in thread
From: Geliang Tang @ 2022-01-28  6:51 UTC (permalink / raw)
  To: Kishen Maloor; +Cc: MPTCP Upstream

Hi Kishen,

This patch will get many checkpatch.pl warnings:

 WARNING: line length of 103 exceeds 100 columns

Thanks,
-Geliang

Kishen Maloor <kishen.maloor@intel.com> 于2022年1月28日周五 08:39写道:
>
> This change adds a selftest script that performs a comprehensive
> behavioral/functional test of all userspace PM capabilities by exercising
> all the newly added APIs and changes to support said capabilities.
>
> Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
> ---
> v3: configure net.mptcp.userspace_pm_local_addr_max via sysctl on
> the namespaces established for testing, read and verify the new
> "server_side" attribute from netlink events upon connection
> establishment, new test case for sysctl configured local_addr limit,
> added descriptive remarks throughout script
> ---
>  .../selftests/net/mptcp/userspace_pm.sh       | 656 ++++++++++++++++++
>  1 file changed, 656 insertions(+)
>  create mode 100755 tools/testing/selftests/net/mptcp/userspace_pm.sh
>
> diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh
> new file mode 100755
> index 000000000000..cc6c03626c21
> --- /dev/null
> +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh
> @@ -0,0 +1,656 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +
> +ip -Version > /dev/null 2>&1
> +if [ $? -ne 0 ];then
> +       echo "SKIP: Cannot not run test without ip tool"
> +       exit 1
> +fi
> +
> +ANNOUNCED=6        # MPTCP_EVENT_ANNOUNCED
> +REMOVED=7          # MPTCP_EVENT_REMOVED
> +SUB_ESTABLISHED=10 # MPTCP_EVENT_SUB_ESTABLISHED
> +SUB_CLOSED=11      # MPTCP_EVENT_SUB_CLOSED
> +
> +AF_INET=2
> +AF_INET6=10
> +
> +evts_pid=0
> +client4_pid=0
> +server4_pid=0
> +client6_pid=0
> +server6_pid=0
> +client4_token=""
> +server4_token=""
> +client6_token=""
> +server6_token=""
> +client4_port=0;
> +client6_port=0;
> +app4_port=50002
> +new4_port=50003
> +app6_port=50004
> +client_addr_id=${RANDOM:0:2}
> +server_addr_id=${RANDOM:0:2}
> +
> +sec=$(date +%s)
> +rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
> +ns1="ns1-$rndh"
> +ns2="ns2-$rndh"
> +
> +cleanup()
> +{
> +       echo "cleanup"
> +
> +       # Terminate the MPTCP connection and related processes
> +       kill -SIGUSR1 $client4_pid > /dev/null 2>&1
> +       kill $server4_pid > /dev/null 2>&1
> +       kill -SIGUSR1 $client6_pid > /dev/null 2>&1
> +       kill $server6_pid > /dev/null 2>&1
> +
> +       kill $evts_pid > /dev/null 2>&1
> +
> +       local netns
> +       for netns in "$ns1" "$ns2" ;do
> +               ip netns del $netns
> +       done
> +}
> +
> +trap cleanup EXIT
> +
> +# Create and configure network namespaces for testing
> +for i in "$ns1" "$ns2" ;do
> +       ip netns add $i || exit 1
> +       ip -net $i link set lo up
> +       ip netns exec $i sysctl -q net.mptcp.enabled=1
> +       ip netns exec $i sysctl -q net.mptcp.pm_type=1
> +       ip netns exec $i sysctl -q net.mptcp.userspace_pm_local_addr_max=2
> +done
> +
> +#  "$ns1"              ns2
> +#     ns1eth2    ns2eth1
> +
> +ip link add ns1eth2 netns "$ns1" type veth peer name ns2eth1 netns "$ns2"
> +
> +# Add IPv4/v6 addresses to the namespaces
> +ip -net "$ns1" addr add 10.0.1.1/24 dev ns1eth2
> +ip -net "$ns1" addr add 10.0.2.1/24 dev ns1eth2
> +ip -net "$ns1" addr add dead:beef:1::1/64 dev ns1eth2 nodad
> +ip -net "$ns1" addr add dead:beef:2::1/64 dev ns1eth2 nodad
> +ip -net "$ns1" link set ns1eth2 up
> +
> +ip -net "$ns2" addr add 10.0.1.2/24 dev ns2eth1
> +ip -net "$ns2" addr add 10.0.2.2/24 dev ns2eth1
> +ip -net "$ns2" addr add dead:beef:1::2/64 dev ns2eth1 nodad
> +ip -net "$ns2" addr add dead:beef:2::2/64 dev ns2eth1 nodad
> +ip -net "$ns2" link set ns2eth1 up
> +
> +printf "Created network namespaces ns1, ns2         \t\t\t[OK]\n"
> +
> +make_file()
> +{
> +       # Store a chunk of data in a file to transmit over an MPTCP connection
> +       local name=$1
> +       local who=$2
> +       local ksize=1
> +
> +       dd if=/dev/urandom of="$name" bs=1024 count=$ksize 2> /dev/null
> +       echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name"
> +}
> +
> +make_connection()
> +{
> +       local file=$(mktemp)
> +       make_file "$file" "client"
> +
> +       local is_v6=$1
> +       local app_port=$app4_port
> +       local connect_addr="10.0.1.1"
> +       local listen_addr="0.0.0.0"
> +       if [ "$is_v6" = "v6" ]
> +       then
> +               connect_addr="dead:beef:1::1"
> +               listen_addr="::"
> +               app_port=$app6_port
> +       else
> +               is_v6="v4"
> +       fi
> +
> +       # Capture netlink events over the two network namespaces running
> +       # the MPTCP client and server
> +       local client_evts=$(mktemp)
> +       :>"$client_evts"
> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$client_evts" 2>&1 &
> +       local client_evts_pid=$!
> +       local server_evts=$(mktemp)
> +       :>"$server_evts"
> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$server_evts" 2>&1 &
> +       local server_evts_pid=$!
> +       sleep 0.1
> +
> +       # Run the server
> +       ip netns exec $ns1 \
> +                       ./mptcp_connect -s MPTCP -w 300 -p $app_port -l $listen_addr 2>&1 > /dev/null &
> +       local server_pid=$!
> +       sleep 0.1
> +
> +       # Run the client, transfer $file and stay connected to the server
> +       # to conduct tests
> +       ip netns exec $ns2 \
> +                       ./mptcp_connect -s MPTCP -w 300 -m sendfile -p $app_port $connect_addr 2>&1 > /dev/null < $file &
> +       local client_pid=$!
> +       sleep 0.1
> +
> +       # Capture client/server attributes from MPTCP connection netlink events
> +       kill $client_evts_pid
> +       local client_token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
> +       local client_port=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
> +       local client_serverside=$(sed -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
> +
> +       kill $server_evts_pid
> +       local server_token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
> +       local server_serverside=$(sed -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
> +
> +       rm -f "$client_evts" "$server_evts" "$file"
> +
> +       if [ $client_token != "" ] && [ $server_token != "" ] && [ $client_serverside = 0 ] && [ $server_serverside = 1 ]
> +       then
> +               printf "Established IP%s MPTCP Connection ns2 => ns1    \t\t[OK]\n" $is_v6
> +       else
> +               exit 1
> +       fi
> +
> +       if [ "$is_v6" = "v6" ]
> +       then
> +               client6_token=$client_token
> +               server6_token=$server_token
> +               client6_port=$client_port
> +               client6_pid=$client_pid
> +               server6_pid=$server_pid
> +       else
> +               client4_token=$client_token
> +               server4_token=$server_token
> +               client4_port=$client_port
> +               client4_pid=$client_pid
> +               server4_pid=$server_pid
> +       fi
> +}
> +
> +verify_announce_event()
> +{
> +       local evt=$1
> +       local e_type=$2
> +       local e_token=$3
> +       local e_addr=$4
> +       local e_id=$5
> +       local e_dport=$6
> +       local e_af=$7
> +
> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +       local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +       local addr=""
> +       if [ "$e_af" = "v6" ]
> +       then
> +               addr=$(sed -n 's/.*\(daddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
> +       else
> +               addr=$(sed -n 's/.*\(daddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
> +       fi
> +       local dport=$(sed -n 's/.*\(dport:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +       local id=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$addr" = "$e_addr" ] && [ "$dport" = "$e_dport" ] && [ "$id" = "$e_id" ]
> +       then
> +               printf "[OK]\n"
> +               return 0
> +       fi
> +       printf "[FAIL]\n"
> +       exit 1
> +}
> +
> +test_announce()
> +{
> +       local evts=$(mktemp)
> +       # Capture events on the network namespace running the server
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
> +       evts_pid=$!
> +       sleep 0.1
> +
> +       # ADD_ADDR using an invalid token should result in no action
> +       local invalid_token=$(( $client4_token - 1))
> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $invalid_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
> +       printf "ADD_ADDR 10.0.2.2 (ns2) => ns1, invalid token    \t\t"
> +        if [ "$type" = "" ]
> +       then
> +               printf "[OK]\n"
> +       else
> +               printf "[FAIL]\n"
> +               exit 1
> +       fi
> +
> +       # ADD_ADDR from the client to server machine reusing the subflow port
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
> +       printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, reuse port \t\t" $client_addr_id
> +       sleep 0.1
> +       verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2" "$client_addr_id" "$client4_port"
> +
> +       # ADD_ADDR6 from the client to server machine reusing the subflow port
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl ann dead:beef:2::2 token $client6_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
> +       printf "ADD_ADDR6 id:%d dead:beef:2::2 (ns2) => ns1, reuse port\t\t" $client_addr_id
> +       sleep 0.1
> +       verify_announce_event "$evts" "$ANNOUNCED" "$server6_token" "dead:beef:2::2" "$client_addr_id" "$client6_port" "v6"
> +
> +       # ADD_ADDR from the client to server machine using a new port
> +       :>"$evts"
> +       client_addr_id=$((client_addr_id+1))
> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 port $new4_port 2>&1 > /dev/null
> +       printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, new port \t\t\t" $client_addr_id
> +       sleep 0.1
> +       verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2" "$client_addr_id" "$new4_port"
> +
> +       kill $evts_pid
> +
> +       # Capture events on the network namespace running the client
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
> +       evts_pid=$!
> +       sleep 0.1
> +
> +       # ADD_ADDR from the server to client machine reusing the subflow port
> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id dev ns1eth2 2>&1 > /dev/null
> +       printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, reuse port \t\t" $server_addr_id
> +       sleep 0.1
> +       verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1" "$server_addr_id" "$app4_port"
> +
> +       # ADD_ADDR6 from the server to client machine reusing the subflow port
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl ann dead:beef:2::1 token $server6_token id $server_addr_id dev ns1eth2 2>&1 > /dev/null
> +       printf "ADD_ADDR6 id:%d dead:beef:2::1 (ns1) => ns2, reuse port\t\t" $server_addr_id
> +       sleep 0.1
> +       verify_announce_event "$evts" "$ANNOUNCED" "$client6_token" "dead:beef:2::1" "$server_addr_id" "$app6_port" "v6"
> +
> +       # ADD_ADDR from the server to client machine using a new port
> +       :>"$evts"
> +       server_addr_id=$((server_addr_id+1))
> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id dev ns1eth2 port $new4_port 2>&1 > /dev/null
> +       printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, new port \t\t\t" $server_addr_id
> +       sleep 0.1
> +       verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1" "$server_addr_id" "$new4_port"
> +
> +       kill $evts_pid
> +       rm -f "$evts"
> +}
> +
> +verify_remove_event()
> +{
> +       local evt=$1
> +       local e_type=$2
> +       local e_token=$3
> +       local e_id=$4
> +
> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +       local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +       local id=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$id" = "$e_id" ]
> +       then
> +               printf "[OK]\n"
> +               return 0
> +       fi
> +       printf "[FAIL]\n"
> +       exit 1
> +}
> +
> +test_remove()
> +{
> +       local evts=$(mktemp)
> +
> +       # Capture events on the network namespace running the server
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
> +       evts_pid=$!
> +       sleep 0.1
> +
> +       # RM_ADDR using an invalid token should result in no action
> +       local invalid_token=$(( $client4_token - 1 ))
> +       ip netns exec $ns2 ./pm_nl_ctl rem token $invalid_token id $client_addr_id 2>&1 > /dev/null
> +       printf "RM_ADDR id:%d ns2 => ns1, invalid token                    \t" $client_addr_id
> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
> +       if [ "$type" = "" ]
> +       then
> +               printf "[OK]\n"
> +       else
> +               printf "[FAIL]\n"
> +       fi
> +
> +       # RM_ADDR using an invalid addr id should result in no action
> +       local invalid_id=$(( $client_addr_id + 1 ))
> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $invalid_id 2>&1 > /dev/null
> +       printf "RM_ADDR id:%d ns2 => ns1, invalid id                    \t" $invalid_id
> +       type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
> +       if [ "$type" = "" ]
> +       then
> +               printf "[OK]\n"
> +       else
> +               printf "[FAIL]\n"
> +       fi
> +
> +       # RM_ADDR from the client to server machine
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $client_addr_id 2>&1 > /dev/null
> +       printf "RM_ADDR id:%d ns2 => ns1                                \t" $client_addr_id
> +       sleep 0.1
> +       verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id"
> +
> +       # RM_ADDR from the client to server machine
> +       :>"$evts"
> +       client_addr_id=$(( $client_addr_id - 1 ))
> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $client_addr_id 2>&1 > /dev/null
> +       printf "RM_ADDR id:%d ns2 => ns1                                \t" $client_addr_id
> +       sleep 0.1
> +       verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id"
> +
> +       # RM_ADDR6 from the client to server machine
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client6_token id $client_addr_id 2>&1 > /dev/null
> +       printf "RM_ADDR6 id:%d ns2 => ns1                               \t" $client_addr_id
> +       sleep 0.1
> +       verify_remove_event "$evts" "$REMOVED" "$server6_token" "$client_addr_id"
> +
> +       kill $evts_pid
> +
> +       # Capture events on the network namespace running the client
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
> +       evts_pid=$!
> +       sleep 0.1
> +
> +       # RM_ADDR from the server to client machine
> +       ip netns exec $ns1 ./pm_nl_ctl rem token $server4_token id $server_addr_id 2>&1 > /dev/null
> +       printf "RM_ADDR id:%d ns1 => ns2                                \t" $server_addr_id
> +       sleep 0.1
> +       verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id"
> +
> +       # RM_ADDR from the server to client machine
> +       :>"$evts"
> +       server_addr_id=$(( $server_addr_id - 1 ))
> +       ip netns exec $ns1 ./pm_nl_ctl rem token $server4_token id $server_addr_id 2>&1 > /dev/null
> +       printf "RM_ADDR id:%d ns1 => ns2                                \t" $server_addr_id
> +       sleep 0.1
> +       verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id"
> +
> +       # RM_ADDR6 from the server to client machine
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl rem token $server6_token id $server_addr_id 2>&1 > /dev/null
> +       printf "RM_ADDR6 id:%d ns1 => ns2                               \t" $server_addr_id
> +       sleep 0.1
> +       verify_remove_event "$evts" "$REMOVED" "$client6_token" "$server_addr_id"
> +
> +       kill $evts_pid
> +       rm -f "$evts"
> +}
> +
> +verify_subflow_events()
> +{
> +       local evt=$1
> +       local e_type=$2
> +       local e_token=$3
> +       local e_family=$4
> +       local e_saddr=$5
> +       local e_daddr=$6
> +       local e_dport=$7
> +       local e_locid=$8
> +       local e_remid=$9
> +       shift 2
> +       local e_from=$8
> +       local e_to=$9
> +
> +       if [ "$e_type" = "$SUB_ESTABLISHED" ]
> +       then
> +               if [ "$e_family" = "$AF_INET6" ]
> +               then
> +                       printf "CREATE_SUBFLOW6 %s (%s) => %s (%s)    " $e_saddr $e_from $e_daddr $e_to
> +               else
> +                       printf "CREATE_SUBFLOW %s (%s) => %s (%s)         \t" $e_saddr $e_from $e_daddr $e_to
> +               fi
> +       else
> +               if [ "$e_family" = "$AF_INET6" ]
> +               then
> +                       printf "DESTROY_SUBFLOW6 %s (%s) => %s (%s)   " $e_saddr $e_from $e_daddr $e_to
> +               else
> +                       printf "DESTROY_SUBFLOW %s (%s) => %s (%s)         \t" $e_saddr $e_from $e_daddr $e_to
> +               fi
> +       fi
> +
> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +       local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +       local family=$(sed -n 's/.*\(family:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +       local dport=$(sed -n 's/.*\(dport:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +       local locid=$(sed -n 's/.*\(loc_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +       local remid=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
> +       local saddr=""
> +       local daddr=""
> +       if [ "$family" = "$AF_INET6" ]
> +       then
> +               saddr=$(sed -n 's/.*\(saddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
> +               daddr=$(sed -n 's/.*\(daddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
> +       else
> +               saddr=$(sed -n 's/.*\(saddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
> +               daddr=$(sed -n 's/.*\(daddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
> +       fi
> +
> +        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$daddr" = "$e_daddr" ] && [ "$e_dport" = "$dport" ] && [ "$family" = "$e_family" ] && [ "$saddr" = "$e_saddr" ] && [ "$e_locid" = "$locid" ] && [ "$e_remid" = "$remid" ]
> +       then
> +               printf "[OK]\n"
> +               return 0
> +       fi
> +       printf "[FAIL]\n"
> +       exit 1
> +}
> +
> +test_subflows()
> +{
> +       local evts=$(mktemp)
> +       # Capture events on the network namespace running the server
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
> +       evts_pid=$!
> +       sleep 0.1
> +
> +       # ADD_ADDR from client to server machine reusing the subflow port
> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id 2>&1 > /dev/null
> +       sleep 0.1
> +
> +       # CREATE_SUBFLOW from server to client machine
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2 rport $client4_port token $server4_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2"
> +
> +       local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
> +
> +       # DESTROY_SUBFLOW from server to client machine
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl dsf lip 10.0.2.1 lport $sport rip 10.0.2.2 rport $client4_port token $server4_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2"
> +
> +       # RM_ADDR from client to server machine
> +       ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client4_token 2>&1 > /dev/null
> +       sleep 0.1
> +
> +       # ADD_ADDR6 from client to server machine reusing the subflow port
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl ann dead:beef:2::2 token $client6_token id $client_addr_id 2>&1 > /dev/null
> +       sleep 0.1
> +
> +       # CREATE_SUBFLOW6 from server to client machine
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl csf lip dead:beef:2::1 lid 23 rip dead:beef:2::2 rport $client6_port token $server6_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server6_token" "$AF_INET6" "dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23" "$client_addr_id" "ns1" "ns2"
> +
> +       local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
> +
> +       # DESTROY_SUBFLOW6 from server to client machine
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl dsf lip dead:beef:2::1 lport $sport rip dead:beef:2::2 rport $client6_port token $server6_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$server6_token" "$AF_INET6" "dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23" "$client_addr_id" "ns1" "ns2"
> +
> +       # RM_ADDR from client to server machine
> +       ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client6_token 2>&1 > /dev/null
> +       sleep 0.1
> +
> +       # ADD_ADDR from client to server machine using a new port
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id port $new4_port 2>&1 > /dev/null
> +       sleep 0.1
> +
> +       # CREATE_SUBFLOW from server to client machine
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2 rport $new4_port token $server4_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$new4_port" "23" "$client_addr_id" "ns1" "ns2"
> +
> +        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
> +
> +       # DESTROY_SUBFLOW from server to client machine
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl dsf lip 10.0.2.1 lport $sport rip 10.0.2.2 rport $new4_port token $server4_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$new4_port" "23" "$client_addr_id" "ns1" "ns2"
> +
> +       # RM_ADDR from client to server machine
> +       ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client4_token 2>&1 > /dev/null
> +
> +       kill $evts_pid
> +
> +       # Capture events on the network namespace running the client
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
> +       evts_pid=$!
> +       sleep 0.1
> +
> +       # ADD_ADDR from server to client machine reusing the subflow port
> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id 2>&1 > /dev/null
> +       sleep 0.1
> +
> +       # CREATE_SUBFLOW from client to server machine
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport $app4_port token $client4_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1"
> +
> +        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
> +
> +       # DESTROY_SUBFLOW from client to server machine
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl dsf lip 10.0.2.2 lport $sport rip 10.0.2.1 rport $app4_port token $client4_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1"
> +
> +       # RM_ADDR from server to client machine
> +       ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server4_token 2>&1 > /dev/null
> +       sleep 0.1
> +
> +       # ADD_ADDR6 from server to client machine reusing the subflow port
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl ann dead:beef:2::1 token $server6_token id $server_addr_id 2>&1 > /dev/null
> +       sleep 0.1
> +
> +       # CREATE_SUBFLOW6 from client to server machine
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl csf lip dead:beef:2::2 lid 23 rip dead:beef:2::1 rport $app6_port token $client6_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client6_token" "$AF_INET6" "dead:beef:2::2" "dead:beef:2::1" "$app6_port" "23" "$server_addr_id" "ns2" "ns1"
> +
> +       local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
> +
> +       # DESTROY_SUBFLOW6 from client to server machine
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl dsf lip dead:beef:2::2 lport $sport rip dead:beef:2::1 rport $app6_port token $client6_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$client6_token" "$AF_INET6" "dead:beef:2::2" "dead:beef:2::1" "$app6_port" "23" "$server_addr_id" "ns2" "ns1"
> +
> +       # RM_ADDR6 from server to client machine
> +       ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server6_token 2>&1 > /dev/null
> +       sleep 0.1
> +
> +       # ADD_ADDR from client to server machine using a new port
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id port $new4_port 2>&1 > /dev/null
> +       sleep 0.1
> +
> +       # CREATE_SUBFLOW from client to server machine
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport $new4_port token $client4_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1"
> +
> +        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
> +
> +       # DESTROY_SUBFLOW from client to server machine
> +       :>"$evts"
> +       ip netns exec $ns2 ./pm_nl_ctl dsf lip 10.0.2.2 lport $sport rip 10.0.2.1 rport $new4_port token $client4_token 2>&1 > /dev/null
> +       sleep 0.1
> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1"
> +
> +       # RM_ADDR from server to client machine
> +       ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server4_token 2>&1 > /dev/null
> +
> +       kill $evts_pid
> +       rm -f "$evts"
> +}
> +
> +test_local_addr_limit()
> +{
> +       printf "Test sysctl: net.mptcp.userspace_pm_local_addr_max=1\t\t"
> +       # Limit the # of local addrs for both namespaces to 1
> +       for i in "$ns1" "$ns2" ;do
> +               ip netns exec $i sysctl -q net.mptcp.userspace_pm_local_addr_max=1
> +       done
> +
> +       local evts=$(mktemp)
> +       # Capture events from the network namespace running the server
> +       :>"$evts"
> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
> +       evts_pid=$!
> +       sleep 0.1
> +
> +       :>"$evts"
> +       # ADD_ADDR from the client to server machine -> success
> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 2>&1 > /dev/nulls
> +       sleep 0.1
> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
> +        if [ "$type" = "" ]
> +       then
> +               printf "[FAIL]\n"
> +               exit 1
> +       fi
> +
> +       :>"$evts"
> +       # ADD_ADDR from the client to server machine -> fail (limit already met)
> +       client_addr_id=$((client_addr_id+1))
> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 port $new4_port 2>&1 > /dev/null
> +       sleep 0.1
> +       type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
> +        if [ "$type" = "" ]
> +       then
> +               printf "[OK]\n"
> +       else
> +               printf "[FAIL]\n"
> +               exit 1
> +       fi
> +
> +       kill $evts_pid
> +       rm -f "$evts"
> +}
> +
> +make_connection
> +make_connection "v6"
> +test_announce
> +test_remove
> +test_subflows
> +test_local_addr_limit
> +exit 0
> --
> 2.31.1
>
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH mptcp-next v3 14/14] selftests: mptcp: functional tests for the userspace PM type
  2022-01-28  6:51   ` Geliang Tang
@ 2022-01-28 19:48     ` Mat Martineau
  2022-01-31 22:24       ` Kishen Maloor
  0 siblings, 1 reply; 24+ messages in thread
From: Mat Martineau @ 2022-01-28 19:48 UTC (permalink / raw)
  To: Geliang Tang; +Cc: Kishen Maloor, MPTCP Upstream

[-- Attachment #1: Type: text/plain, Size: 30410 bytes --]

On Fri, 28 Jan 2022, Geliang Tang wrote:

> Hi Kishen,
>
> This patch will get many checkpatch.pl warnings:
>
> WARNING: line length of 103 exceeds 100 columns
>

Most of our selftest scripts exceed 100 columns, as do many in the net/ 
directory. I think the longer command lines end up more readable without 
wrapping, but some of the longer 'if' statements could be wrapped to help 
with readability.


> Thanks,
> -Geliang
>
> Kishen Maloor <kishen.maloor@intel.com> 于2022年1月28日周五 08:39写道:
>>
>> This change adds a selftest script that performs a comprehensive
>> behavioral/functional test of all userspace PM capabilities by exercising
>> all the newly added APIs and changes to support said capabilities.
>>
>> Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
>> ---
>> v3: configure net.mptcp.userspace_pm_local_addr_max via sysctl on
>> the namespaces established for testing, read and verify the new
>> "server_side" attribute from netlink events upon connection
>> establishment, new test case for sysctl configured local_addr limit,
>> added descriptive remarks throughout script
>> ---
>>  .../selftests/net/mptcp/userspace_pm.sh       | 656 ++++++++++++++++++
>>  1 file changed, 656 insertions(+)
>>  create mode 100755 tools/testing/selftests/net/mptcp/userspace_pm.sh
>>
>> diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh
>> new file mode 100755
>> index 000000000000..cc6c03626c21
>> --- /dev/null
>> +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh
>> @@ -0,0 +1,656 @@
>> +#!/bin/bash
>> +# SPDX-License-Identifier: GPL-2.0
>> +
>> +ip -Version > /dev/null 2>&1
>> +if [ $? -ne 0 ];then
>> +       echo "SKIP: Cannot not run test without ip tool"
>> +       exit 1
>> +fi
>> +
>> +ANNOUNCED=6        # MPTCP_EVENT_ANNOUNCED
>> +REMOVED=7          # MPTCP_EVENT_REMOVED
>> +SUB_ESTABLISHED=10 # MPTCP_EVENT_SUB_ESTABLISHED
>> +SUB_CLOSED=11      # MPTCP_EVENT_SUB_CLOSED
>> +
>> +AF_INET=2
>> +AF_INET6=10
>> +
>> +evts_pid=0
>> +client4_pid=0
>> +server4_pid=0
>> +client6_pid=0
>> +server6_pid=0
>> +client4_token=""
>> +server4_token=""
>> +client6_token=""
>> +server6_token=""
>> +client4_port=0;
>> +client6_port=0;
>> +app4_port=50002
>> +new4_port=50003
>> +app6_port=50004
>> +client_addr_id=${RANDOM:0:2}
>> +server_addr_id=${RANDOM:0:2}
>> +
>> +sec=$(date +%s)
>> +rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
>> +ns1="ns1-$rndh"
>> +ns2="ns2-$rndh"
>> +
>> +cleanup()
>> +{
>> +       echo "cleanup"
>> +
>> +       # Terminate the MPTCP connection and related processes
>> +       kill -SIGUSR1 $client4_pid > /dev/null 2>&1
>> +       kill $server4_pid > /dev/null 2>&1
>> +       kill -SIGUSR1 $client6_pid > /dev/null 2>&1
>> +       kill $server6_pid > /dev/null 2>&1
>> +
>> +       kill $evts_pid > /dev/null 2>&1
>> +
>> +       local netns
>> +       for netns in "$ns1" "$ns2" ;do
>> +               ip netns del $netns
>> +       done
>> +}
>> +
>> +trap cleanup EXIT
>> +
>> +# Create and configure network namespaces for testing
>> +for i in "$ns1" "$ns2" ;do
>> +       ip netns add $i || exit 1
>> +       ip -net $i link set lo up
>> +       ip netns exec $i sysctl -q net.mptcp.enabled=1
>> +       ip netns exec $i sysctl -q net.mptcp.pm_type=1
>> +       ip netns exec $i sysctl -q net.mptcp.userspace_pm_local_addr_max=2
>> +done
>> +
>> +#  "$ns1"              ns2
>> +#     ns1eth2    ns2eth1
>> +
>> +ip link add ns1eth2 netns "$ns1" type veth peer name ns2eth1 netns "$ns2"
>> +
>> +# Add IPv4/v6 addresses to the namespaces
>> +ip -net "$ns1" addr add 10.0.1.1/24 dev ns1eth2
>> +ip -net "$ns1" addr add 10.0.2.1/24 dev ns1eth2
>> +ip -net "$ns1" addr add dead:beef:1::1/64 dev ns1eth2 nodad
>> +ip -net "$ns1" addr add dead:beef:2::1/64 dev ns1eth2 nodad
>> +ip -net "$ns1" link set ns1eth2 up
>> +
>> +ip -net "$ns2" addr add 10.0.1.2/24 dev ns2eth1
>> +ip -net "$ns2" addr add 10.0.2.2/24 dev ns2eth1
>> +ip -net "$ns2" addr add dead:beef:1::2/64 dev ns2eth1 nodad
>> +ip -net "$ns2" addr add dead:beef:2::2/64 dev ns2eth1 nodad
>> +ip -net "$ns2" link set ns2eth1 up
>> +
>> +printf "Created network namespaces ns1, ns2         \t\t\t[OK]\n"
>> +
>> +make_file()
>> +{
>> +       # Store a chunk of data in a file to transmit over an MPTCP connection
>> +       local name=$1
>> +       local who=$2
>> +       local ksize=1
>> +
>> +       dd if=/dev/urandom of="$name" bs=1024 count=$ksize 2> /dev/null
>> +       echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name"
>> +}
>> +
>> +make_connection()
>> +{
>> +       local file=$(mktemp)
>> +       make_file "$file" "client"
>> +
>> +       local is_v6=$1
>> +       local app_port=$app4_port
>> +       local connect_addr="10.0.1.1"
>> +       local listen_addr="0.0.0.0"
>> +       if [ "$is_v6" = "v6" ]
>> +       then
>> +               connect_addr="dead:beef:1::1"
>> +               listen_addr="::"
>> +               app_port=$app6_port
>> +       else
>> +               is_v6="v4"
>> +       fi
>> +
>> +       # Capture netlink events over the two network namespaces running
>> +       # the MPTCP client and server
>> +       local client_evts=$(mktemp)
>> +       :>"$client_evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$client_evts" 2>&1 &
>> +       local client_evts_pid=$!
>> +       local server_evts=$(mktemp)
>> +       :>"$server_evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$server_evts" 2>&1 &
>> +       local server_evts_pid=$!
>> +       sleep 0.1
>> +
>> +       # Run the server
>> +       ip netns exec $ns1 \
>> +                       ./mptcp_connect -s MPTCP -w 300 -p $app_port -l $listen_addr 2>&1 > /dev/null &
>> +       local server_pid=$!
>> +       sleep 0.1
>> +
>> +       # Run the client, transfer $file and stay connected to the server
>> +       # to conduct tests
>> +       ip netns exec $ns2 \
>> +                       ./mptcp_connect -s MPTCP -w 300 -m sendfile -p $app_port $connect_addr 2>&1 > /dev/null < $file &
>> +       local client_pid=$!
>> +       sleep 0.1
>> +
>> +       # Capture client/server attributes from MPTCP connection netlink events
>> +       kill $client_evts_pid
>> +       local client_token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
>> +       local client_port=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
>> +       local client_serverside=$(sed -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
>> +
>> +       kill $server_evts_pid
>> +       local server_token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
>> +       local server_serverside=$(sed -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
>> +
>> +       rm -f "$client_evts" "$server_evts" "$file"
>> +
>> +       if [ $client_token != "" ] && [ $server_token != "" ] && [ $client_serverside = 0 ] && [ $server_serverside = 1 ]
>> +       then
>> +               printf "Established IP%s MPTCP Connection ns2 => ns1    \t\t[OK]\n" $is_v6
>> +       else
>> +               exit 1
>> +       fi
>> +
>> +       if [ "$is_v6" = "v6" ]
>> +       then
>> +               client6_token=$client_token
>> +               server6_token=$server_token
>> +               client6_port=$client_port
>> +               client6_pid=$client_pid
>> +               server6_pid=$server_pid
>> +       else
>> +               client4_token=$client_token
>> +               server4_token=$server_token
>> +               client4_port=$client_port
>> +               client4_pid=$client_pid
>> +               server4_pid=$server_pid
>> +       fi
>> +}
>> +
>> +verify_announce_event()
>> +{
>> +       local evt=$1
>> +       local e_type=$2
>> +       local e_token=$3
>> +       local e_addr=$4
>> +       local e_id=$5
>> +       local e_dport=$6
>> +       local e_af=$7
>> +
>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +       local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +       local addr=""
>> +       if [ "$e_af" = "v6" ]
>> +       then
>> +               addr=$(sed -n 's/.*\(daddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
>> +       else
>> +               addr=$(sed -n 's/.*\(daddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
>> +       fi
>> +       local dport=$(sed -n 's/.*\(dport:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +       local id=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$addr" = "$e_addr" ] && [ "$dport" = "$e_dport" ] && [ "$id" = "$e_id" ]

Here's one spot with leading spaces instead of tabs - looks like there are 
others too. Also an example of a line that could be wrapped :)

-Mat

>> +       then
>> +               printf "[OK]\n"
>> +               return 0
>> +       fi
>> +       printf "[FAIL]\n"
>> +       exit 1
>> +}
>> +
>> +test_announce()
>> +{
>> +       local evts=$(mktemp)
>> +       # Capture events on the network namespace running the server
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
>> +       evts_pid=$!
>> +       sleep 0.1
>> +
>> +       # ADD_ADDR using an invalid token should result in no action
>> +       local invalid_token=$(( $client4_token - 1))
>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $invalid_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>> +       printf "ADD_ADDR 10.0.2.2 (ns2) => ns1, invalid token    \t\t"
>> +        if [ "$type" = "" ]
>> +       then
>> +               printf "[OK]\n"
>> +       else
>> +               printf "[FAIL]\n"
>> +               exit 1
>> +       fi
>> +
>> +       # ADD_ADDR from the client to server machine reusing the subflow port
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
>> +       printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, reuse port \t\t" $client_addr_id
>> +       sleep 0.1
>> +       verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2" "$client_addr_id" "$client4_port"
>> +
>> +       # ADD_ADDR6 from the client to server machine reusing the subflow port
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl ann dead:beef:2::2 token $client6_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
>> +       printf "ADD_ADDR6 id:%d dead:beef:2::2 (ns2) => ns1, reuse port\t\t" $client_addr_id
>> +       sleep 0.1
>> +       verify_announce_event "$evts" "$ANNOUNCED" "$server6_token" "dead:beef:2::2" "$client_addr_id" "$client6_port" "v6"
>> +
>> +       # ADD_ADDR from the client to server machine using a new port
>> +       :>"$evts"
>> +       client_addr_id=$((client_addr_id+1))
>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 port $new4_port 2>&1 > /dev/null
>> +       printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, new port \t\t\t" $client_addr_id
>> +       sleep 0.1
>> +       verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2" "$client_addr_id" "$new4_port"
>> +
>> +       kill $evts_pid
>> +
>> +       # Capture events on the network namespace running the client
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
>> +       evts_pid=$!
>> +       sleep 0.1
>> +
>> +       # ADD_ADDR from the server to client machine reusing the subflow port
>> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id dev ns1eth2 2>&1 > /dev/null
>> +       printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, reuse port \t\t" $server_addr_id
>> +       sleep 0.1
>> +       verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1" "$server_addr_id" "$app4_port"
>> +
>> +       # ADD_ADDR6 from the server to client machine reusing the subflow port
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl ann dead:beef:2::1 token $server6_token id $server_addr_id dev ns1eth2 2>&1 > /dev/null
>> +       printf "ADD_ADDR6 id:%d dead:beef:2::1 (ns1) => ns2, reuse port\t\t" $server_addr_id
>> +       sleep 0.1
>> +       verify_announce_event "$evts" "$ANNOUNCED" "$client6_token" "dead:beef:2::1" "$server_addr_id" "$app6_port" "v6"
>> +
>> +       # ADD_ADDR from the server to client machine using a new port
>> +       :>"$evts"
>> +       server_addr_id=$((server_addr_id+1))
>> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id dev ns1eth2 port $new4_port 2>&1 > /dev/null
>> +       printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, new port \t\t\t" $server_addr_id
>> +       sleep 0.1
>> +       verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1" "$server_addr_id" "$new4_port"
>> +
>> +       kill $evts_pid
>> +       rm -f "$evts"
>> +}
>> +
>> +verify_remove_event()
>> +{
>> +       local evt=$1
>> +       local e_type=$2
>> +       local e_token=$3
>> +       local e_id=$4
>> +
>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +       local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +       local id=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$id" = "$e_id" ]
>> +       then
>> +               printf "[OK]\n"
>> +               return 0
>> +       fi
>> +       printf "[FAIL]\n"
>> +       exit 1
>> +}
>> +
>> +test_remove()
>> +{
>> +       local evts=$(mktemp)
>> +
>> +       # Capture events on the network namespace running the server
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
>> +       evts_pid=$!
>> +       sleep 0.1
>> +
>> +       # RM_ADDR using an invalid token should result in no action
>> +       local invalid_token=$(( $client4_token - 1 ))
>> +       ip netns exec $ns2 ./pm_nl_ctl rem token $invalid_token id $client_addr_id 2>&1 > /dev/null
>> +       printf "RM_ADDR id:%d ns2 => ns1, invalid token                    \t" $client_addr_id
>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>> +       if [ "$type" = "" ]
>> +       then
>> +               printf "[OK]\n"
>> +       else
>> +               printf "[FAIL]\n"
>> +       fi
>> +
>> +       # RM_ADDR using an invalid addr id should result in no action
>> +       local invalid_id=$(( $client_addr_id + 1 ))
>> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $invalid_id 2>&1 > /dev/null
>> +       printf "RM_ADDR id:%d ns2 => ns1, invalid id                    \t" $invalid_id
>> +       type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>> +       if [ "$type" = "" ]
>> +       then
>> +               printf "[OK]\n"
>> +       else
>> +               printf "[FAIL]\n"
>> +       fi
>> +
>> +       # RM_ADDR from the client to server machine
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $client_addr_id 2>&1 > /dev/null
>> +       printf "RM_ADDR id:%d ns2 => ns1                                \t" $client_addr_id
>> +       sleep 0.1
>> +       verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id"
>> +
>> +       # RM_ADDR from the client to server machine
>> +       :>"$evts"
>> +       client_addr_id=$(( $client_addr_id - 1 ))
>> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $client_addr_id 2>&1 > /dev/null
>> +       printf "RM_ADDR id:%d ns2 => ns1                                \t" $client_addr_id
>> +       sleep 0.1
>> +       verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id"
>> +
>> +       # RM_ADDR6 from the client to server machine
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client6_token id $client_addr_id 2>&1 > /dev/null
>> +       printf "RM_ADDR6 id:%d ns2 => ns1                               \t" $client_addr_id
>> +       sleep 0.1
>> +       verify_remove_event "$evts" "$REMOVED" "$server6_token" "$client_addr_id"
>> +
>> +       kill $evts_pid
>> +
>> +       # Capture events on the network namespace running the client
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
>> +       evts_pid=$!
>> +       sleep 0.1
>> +
>> +       # RM_ADDR from the server to client machine
>> +       ip netns exec $ns1 ./pm_nl_ctl rem token $server4_token id $server_addr_id 2>&1 > /dev/null
>> +       printf "RM_ADDR id:%d ns1 => ns2                                \t" $server_addr_id
>> +       sleep 0.1
>> +       verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id"
>> +
>> +       # RM_ADDR from the server to client machine
>> +       :>"$evts"
>> +       server_addr_id=$(( $server_addr_id - 1 ))
>> +       ip netns exec $ns1 ./pm_nl_ctl rem token $server4_token id $server_addr_id 2>&1 > /dev/null
>> +       printf "RM_ADDR id:%d ns1 => ns2                                \t" $server_addr_id
>> +       sleep 0.1
>> +       verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id"
>> +
>> +       # RM_ADDR6 from the server to client machine
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl rem token $server6_token id $server_addr_id 2>&1 > /dev/null
>> +       printf "RM_ADDR6 id:%d ns1 => ns2                               \t" $server_addr_id
>> +       sleep 0.1
>> +       verify_remove_event "$evts" "$REMOVED" "$client6_token" "$server_addr_id"
>> +
>> +       kill $evts_pid
>> +       rm -f "$evts"
>> +}
>> +
>> +verify_subflow_events()
>> +{
>> +       local evt=$1
>> +       local e_type=$2
>> +       local e_token=$3
>> +       local e_family=$4
>> +       local e_saddr=$5
>> +       local e_daddr=$6
>> +       local e_dport=$7
>> +       local e_locid=$8
>> +       local e_remid=$9
>> +       shift 2
>> +       local e_from=$8
>> +       local e_to=$9
>> +
>> +       if [ "$e_type" = "$SUB_ESTABLISHED" ]
>> +       then
>> +               if [ "$e_family" = "$AF_INET6" ]
>> +               then
>> +                       printf "CREATE_SUBFLOW6 %s (%s) => %s (%s)    " $e_saddr $e_from $e_daddr $e_to
>> +               else
>> +                       printf "CREATE_SUBFLOW %s (%s) => %s (%s)         \t" $e_saddr $e_from $e_daddr $e_to
>> +               fi
>> +       else
>> +               if [ "$e_family" = "$AF_INET6" ]
>> +               then
>> +                       printf "DESTROY_SUBFLOW6 %s (%s) => %s (%s)   " $e_saddr $e_from $e_daddr $e_to
>> +               else
>> +                       printf "DESTROY_SUBFLOW %s (%s) => %s (%s)         \t" $e_saddr $e_from $e_daddr $e_to
>> +               fi
>> +       fi
>> +
>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +       local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +       local family=$(sed -n 's/.*\(family:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +       local dport=$(sed -n 's/.*\(dport:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +       local locid=$(sed -n 's/.*\(loc_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +       local remid=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>> +       local saddr=""
>> +       local daddr=""
>> +       if [ "$family" = "$AF_INET6" ]
>> +       then
>> +               saddr=$(sed -n 's/.*\(saddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
>> +               daddr=$(sed -n 's/.*\(daddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
>> +       else
>> +               saddr=$(sed -n 's/.*\(saddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
>> +               daddr=$(sed -n 's/.*\(daddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
>> +       fi
>> +
>> +        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$daddr" = "$e_daddr" ] && [ "$e_dport" = "$dport" ] && [ "$family" = "$e_family" ] && [ "$saddr" = "$e_saddr" ] && [ "$e_locid" = "$locid" ] && [ "$e_remid" = "$remid" ]
>> +       then
>> +               printf "[OK]\n"
>> +               return 0
>> +       fi
>> +       printf "[FAIL]\n"
>> +       exit 1
>> +}
>> +
>> +test_subflows()
>> +{
>> +       local evts=$(mktemp)
>> +       # Capture events on the network namespace running the server
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
>> +       evts_pid=$!
>> +       sleep 0.1
>> +
>> +       # ADD_ADDR from client to server machine reusing the subflow port
>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id 2>&1 > /dev/null
>> +       sleep 0.1
>> +
>> +       # CREATE_SUBFLOW from server to client machine
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2 rport $client4_port token $server4_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2"
>> +
>> +       local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>> +
>> +       # DESTROY_SUBFLOW from server to client machine
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl dsf lip 10.0.2.1 lport $sport rip 10.0.2.2 rport $client4_port token $server4_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2"
>> +
>> +       # RM_ADDR from client to server machine
>> +       ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client4_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +
>> +       # ADD_ADDR6 from client to server machine reusing the subflow port
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl ann dead:beef:2::2 token $client6_token id $client_addr_id 2>&1 > /dev/null
>> +       sleep 0.1
>> +
>> +       # CREATE_SUBFLOW6 from server to client machine
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl csf lip dead:beef:2::1 lid 23 rip dead:beef:2::2 rport $client6_port token $server6_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server6_token" "$AF_INET6" "dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23" "$client_addr_id" "ns1" "ns2"
>> +
>> +       local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>> +
>> +       # DESTROY_SUBFLOW6 from server to client machine
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl dsf lip dead:beef:2::1 lport $sport rip dead:beef:2::2 rport $client6_port token $server6_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$server6_token" "$AF_INET6" "dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23" "$client_addr_id" "ns1" "ns2"
>> +
>> +       # RM_ADDR from client to server machine
>> +       ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client6_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +
>> +       # ADD_ADDR from client to server machine using a new port
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id port $new4_port 2>&1 > /dev/null
>> +       sleep 0.1
>> +
>> +       # CREATE_SUBFLOW from server to client machine
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2 rport $new4_port token $server4_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$new4_port" "23" "$client_addr_id" "ns1" "ns2"
>> +
>> +        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>> +
>> +       # DESTROY_SUBFLOW from server to client machine
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl dsf lip 10.0.2.1 lport $sport rip 10.0.2.2 rport $new4_port token $server4_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$new4_port" "23" "$client_addr_id" "ns1" "ns2"
>> +
>> +       # RM_ADDR from client to server machine
>> +       ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client4_token 2>&1 > /dev/null
>> +
>> +       kill $evts_pid
>> +
>> +       # Capture events on the network namespace running the client
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
>> +       evts_pid=$!
>> +       sleep 0.1
>> +
>> +       # ADD_ADDR from server to client machine reusing the subflow port
>> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id 2>&1 > /dev/null
>> +       sleep 0.1
>> +
>> +       # CREATE_SUBFLOW from client to server machine
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport $app4_port token $client4_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1"
>> +
>> +        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>> +
>> +       # DESTROY_SUBFLOW from client to server machine
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl dsf lip 10.0.2.2 lport $sport rip 10.0.2.1 rport $app4_port token $client4_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1"
>> +
>> +       # RM_ADDR from server to client machine
>> +       ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server4_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +
>> +       # ADD_ADDR6 from server to client machine reusing the subflow port
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl ann dead:beef:2::1 token $server6_token id $server_addr_id 2>&1 > /dev/null
>> +       sleep 0.1
>> +
>> +       # CREATE_SUBFLOW6 from client to server machine
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl csf lip dead:beef:2::2 lid 23 rip dead:beef:2::1 rport $app6_port token $client6_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client6_token" "$AF_INET6" "dead:beef:2::2" "dead:beef:2::1" "$app6_port" "23" "$server_addr_id" "ns2" "ns1"
>> +
>> +       local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>> +
>> +       # DESTROY_SUBFLOW6 from client to server machine
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl dsf lip dead:beef:2::2 lport $sport rip dead:beef:2::1 rport $app6_port token $client6_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$client6_token" "$AF_INET6" "dead:beef:2::2" "dead:beef:2::1" "$app6_port" "23" "$server_addr_id" "ns2" "ns1"
>> +
>> +       # RM_ADDR6 from server to client machine
>> +       ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server6_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +
>> +       # ADD_ADDR from client to server machine using a new port
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id port $new4_port 2>&1 > /dev/null
>> +       sleep 0.1
>> +
>> +       # CREATE_SUBFLOW from client to server machine
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport $new4_port token $client4_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1"
>> +
>> +        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>> +
>> +       # DESTROY_SUBFLOW from client to server machine
>> +       :>"$evts"
>> +       ip netns exec $ns2 ./pm_nl_ctl dsf lip 10.0.2.2 lport $sport rip 10.0.2.1 rport $new4_port token $client4_token 2>&1 > /dev/null
>> +       sleep 0.1
>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1"
>> +
>> +       # RM_ADDR from server to client machine
>> +       ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server4_token 2>&1 > /dev/null
>> +
>> +       kill $evts_pid
>> +       rm -f "$evts"
>> +}
>> +
>> +test_local_addr_limit()
>> +{
>> +       printf "Test sysctl: net.mptcp.userspace_pm_local_addr_max=1\t\t"
>> +       # Limit the # of local addrs for both namespaces to 1
>> +       for i in "$ns1" "$ns2" ;do
>> +               ip netns exec $i sysctl -q net.mptcp.userspace_pm_local_addr_max=1
>> +       done
>> +
>> +       local evts=$(mktemp)
>> +       # Capture events from the network namespace running the server
>> +       :>"$evts"
>> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
>> +       evts_pid=$!
>> +       sleep 0.1
>> +
>> +       :>"$evts"
>> +       # ADD_ADDR from the client to server machine -> success
>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 2>&1 > /dev/nulls
>> +       sleep 0.1
>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>> +        if [ "$type" = "" ]
>> +       then
>> +               printf "[FAIL]\n"
>> +               exit 1
>> +       fi
>> +
>> +       :>"$evts"
>> +       # ADD_ADDR from the client to server machine -> fail (limit already met)
>> +       client_addr_id=$((client_addr_id+1))
>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 port $new4_port 2>&1 > /dev/null
>> +       sleep 0.1
>> +       type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>> +        if [ "$type" = "" ]
>> +       then
>> +               printf "[OK]\n"
>> +       else
>> +               printf "[FAIL]\n"
>> +               exit 1
>> +       fi
>> +
>> +       kill $evts_pid
>> +       rm -f "$evts"
>> +}
>> +
>> +make_connection
>> +make_connection "v6"
>> +test_announce
>> +test_remove
>> +test_subflows
>> +test_local_addr_limit
>> +exit 0
>> --
>> 2.31.1
>>
>>
>
>

--
Mat Martineau
Intel

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH mptcp-next v3 01/14] mptcp: add sysctl param to limit userspace PM activity
  2022-01-28  6:38   ` Geliang Tang
@ 2022-01-31 22:23     ` Kishen Maloor
  2022-02-01 11:49       ` Paolo Abeni
  0 siblings, 1 reply; 24+ messages in thread
From: Kishen Maloor @ 2022-01-31 22:23 UTC (permalink / raw)
  To: Geliang Tang; +Cc: MPTCP Upstream

On 1/27/22 10:38 PM, Geliang Tang wrote:
> Hi Kishen,
> 
> Kishen Maloor <kishen.maloor@intel.com> 于2022年1月28日周五 08:39写道:
>>
>> This change adds a new configurable param userspace_pm_local_addr_max
>> to set a limit on the number of local addresses that may be recorded
>> by a userspace path manager in the context of any MPTCP connection.
>> Consequently, it also limits the # of subflows, i.e. to as many that
>> could be established via the bounded set of local addresses.
>>
>> The patch also ensures that other stats inside struct mptcp_pm_data
>> that are updated along kernel code paths exercised by userspace PMs
>> remain untouched for userspace PM managed sockets.
>>
>> Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
>> ---
>>  net/mptcp/ctrl.c       | 17 +++++++++++++++++
>>  net/mptcp/pm.c         |  4 +++-
>>  net/mptcp/pm_netlink.c |  3 +++
>>  net/mptcp/protocol.h   |  1 +
>>  4 files changed, 24 insertions(+), 1 deletion(-)
>>
>> diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c
>> index ae20b7d92e28..dffc2da6d713 100644
>> --- a/net/mptcp/ctrl.c
>> +++ b/net/mptcp/ctrl.c
>> @@ -19,6 +19,7 @@ static int mptcp_pernet_id;
>>
>>  #ifdef CONFIG_SYSCTL
>>  static int mptcp_pm_type_max = __MPTCP_PM_TYPE_MAX;
>> +static int mptcp_max_addrs = MPTCP_PM_MAX_ADDR_ID;
>>  #endif
>>
>>  struct mptcp_pernet {
>> @@ -32,6 +33,7 @@ struct mptcp_pernet {
>>         u8 checksum_enabled;
>>         u8 allow_join_initial_addr_port;
>>         u8 pm_type;
>> +       u8 userspace_pm_local_addr_max;
>>  };
>>
>>  static struct mptcp_pernet *mptcp_get_pernet(const struct net *net)
>> @@ -69,6 +71,11 @@ int mptcp_get_pm_type(const struct net *net)
>>         return mptcp_get_pernet(net)->pm_type;
>>  }
>>
>> +unsigned int mptcp_get_userspace_pm_local_addr_max(const struct net *net)
>> +{
>> +       return mptcp_get_pernet(net)->userspace_pm_local_addr_max;
>> +}
>> +
>>  static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
>>  {
>>         pernet->mptcp_enabled = 1;
>> @@ -77,6 +84,7 @@ static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
>>         pernet->allow_join_initial_addr_port = 1;
>>         pernet->stale_loss_cnt = 4;
>>         pernet->pm_type = MPTCP_PM_TYPE_KERNEL;
>> +       pernet->userspace_pm_local_addr_max = 10;
>>  }
>>
>>  #ifdef CONFIG_SYSCTL
>> @@ -128,6 +136,14 @@ static struct ctl_table mptcp_sysctl_table[] = {
>>                 .extra1       = SYSCTL_ZERO,
>>                 .extra2       = &mptcp_pm_type_max
>>         },
>> +       {
>> +               .procname = "userspace_pm_local_addr_max",
>> +               .maxlen = sizeof(u8),
>> +               .mode = 0644,
>> +               .proc_handler = proc_dou8vec_minmax,
>> +               .extra1       = SYSCTL_ONE,
>> +               .extra2       = &mptcp_max_addrs
>> +       },
>>         {}
>>  };
>>
>> @@ -149,6 +165,7 @@ static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
>>         table[3].data = &pernet->allow_join_initial_addr_port;
>>         table[4].data = &pernet->stale_loss_cnt;
>>         table[5].data = &pernet->pm_type;
>> +       table[6].data = &pernet->userspace_pm_local_addr_max;
>>
>>         hdr = register_net_sysctl(net, MPTCP_SYSCTL_PATH, table);
>>         if (!hdr)
>> diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
>> index e5d5cb847209..24839cd7025c 100644
>> --- a/net/mptcp/pm.c
>> +++ b/net/mptcp/pm.c
>> @@ -182,7 +182,9 @@ void mptcp_pm_subflow_check_next(struct mptcp_sock *msk, const struct sock *ssk,
>>         bool update_subflows;
>>
>>         update_subflows = (ssk->sk_state == TCP_CLOSE) &&
>> -                         (subflow->request_join || subflow->mp_join);
>> +                         (subflow->request_join || subflow->mp_join) &&
>> +                         READ_ONCE(pm->pm_type) == MPTCP_PM_TYPE_KERNEL;
> 
> How about using mptcp_pm_is_userspace() instead for all the
> MPTCP_PM_TYPE_KERNEL type check in this series?

Most PM related code paths in the kernel largely drive the in-kernel PM's flow and only a 
small subset of those are shared with the userspace PM. Among those shared code paths, I 
believe there's only one spot in my patches that has a userspace PM specific branch for
which there's an explicit pm_type check against MPTCP_PM_TYPE_USERSPACE. The few remaining 
checks are against MPTCP_PM_TYPE_KERNEL to bypass some in-kernel PM specific
behaviors in *all* "non-kernel" PMs. As we may have > 2 PM types by construction, the code
does not assume the userspace PM when pm_type != MPTCP_PM_TYPE_KERNEL.

> 
> Thanks,
> -Geliang
> 
>> +
>>         if (!READ_ONCE(pm->work_pending) && !update_subflows)
>>                 return;
>>
>> diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
>> index 126cc961a4fd..882988e43d76 100644
>> --- a/net/mptcp/pm_netlink.c
>> +++ b/net/mptcp/pm_netlink.c
>> @@ -933,6 +933,9 @@ static void mptcp_pm_nl_rm_addr_or_subflow(struct mptcp_sock *msk,
>>                 if (!removed)
>>                         continue;
>>
>> +               if (READ_ONCE(msk->pm.pm_type) != MPTCP_PM_TYPE_KERNEL)
>> +                       continue;
>> +
>>                 if (rm_type == MPTCP_MIB_RMADDR) {
>>                         msk->pm.add_addr_accepted--;
>>                         WRITE_ONCE(msk->pm.accept_addr, true);
>> diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
>> index 30006735afb7..6cfa8ec26482 100644
>> --- a/net/mptcp/protocol.h
>> +++ b/net/mptcp/protocol.h
>> @@ -852,6 +852,7 @@ unsigned int mptcp_pm_get_add_addr_signal_max(struct mptcp_sock *msk);
>>  unsigned int mptcp_pm_get_add_addr_accept_max(struct mptcp_sock *msk);
>>  unsigned int mptcp_pm_get_subflows_max(struct mptcp_sock *msk);
>>  unsigned int mptcp_pm_get_local_addr_max(struct mptcp_sock *msk);
>> +unsigned int mptcp_get_userspace_pm_local_addr_max(const struct net *net);
>>
>>  void mptcp_sockopt_sync(struct mptcp_sock *msk, struct sock *ssk);
>>  void mptcp_sockopt_sync_locked(struct mptcp_sock *msk, struct sock *ssk);
>> --
>> 2.31.1
>>
>>


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH mptcp-next v3 14/14] selftests: mptcp: functional tests for the userspace PM type
  2022-01-28 19:48     ` Mat Martineau
@ 2022-01-31 22:24       ` Kishen Maloor
  0 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-01-31 22:24 UTC (permalink / raw)
  To: Mat Martineau, Geliang Tang; +Cc: MPTCP Upstream

On 1/28/22 11:48 AM, Mat Martineau wrote:
> On Fri, 28 Jan 2022, Geliang Tang wrote:
> 
>> Hi Kishen,
>>
>> This patch will get many checkpatch.pl warnings:
>>
>> WARNING: line length of 103 exceeds 100 columns
>>
> 
> Most of our selftest scripts exceed 100 columns, as do many in the net/ directory. I think the longer command lines end up more readable without wrapping, but some of the longer 'if' statements could be wrapped to help with readability.
> 

Thanks, I will try to wrap some of the longer lines in the script to get in the
vicinity of 100 cols (even if slightly higher). 

> 
>> Thanks,
>> -Geliang
>>
>> Kishen Maloor <kishen.maloor@intel.com> 于2022年1月28日周五 08:39写道:
>>>
>>> This change adds a selftest script that performs a comprehensive
>>> behavioral/functional test of all userspace PM capabilities by exercising
>>> all the newly added APIs and changes to support said capabilities.
>>>
>>> Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
>>> ---
>>> v3: configure net.mptcp.userspace_pm_local_addr_max via sysctl on
>>> the namespaces established for testing, read and verify the new
>>> "server_side" attribute from netlink events upon connection
>>> establishment, new test case for sysctl configured local_addr limit,
>>> added descriptive remarks throughout script
>>> ---
>>>  .../selftests/net/mptcp/userspace_pm.sh       | 656 ++++++++++++++++++
>>>  1 file changed, 656 insertions(+)
>>>  create mode 100755 tools/testing/selftests/net/mptcp/userspace_pm.sh
>>>
>>> diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh
>>> new file mode 100755
>>> index 000000000000..cc6c03626c21
>>> --- /dev/null
>>> +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh
>>> @@ -0,0 +1,656 @@
>>> +#!/bin/bash
>>> +# SPDX-License-Identifier: GPL-2.0
>>> +
>>> +ip -Version > /dev/null 2>&1
>>> +if [ $? -ne 0 ];then
>>> +       echo "SKIP: Cannot not run test without ip tool"
>>> +       exit 1
>>> +fi
>>> +
>>> +ANNOUNCED=6        # MPTCP_EVENT_ANNOUNCED
>>> +REMOVED=7          # MPTCP_EVENT_REMOVED
>>> +SUB_ESTABLISHED=10 # MPTCP_EVENT_SUB_ESTABLISHED
>>> +SUB_CLOSED=11      # MPTCP_EVENT_SUB_CLOSED
>>> +
>>> +AF_INET=2
>>> +AF_INET6=10
>>> +
>>> +evts_pid=0
>>> +client4_pid=0
>>> +server4_pid=0
>>> +client6_pid=0
>>> +server6_pid=0
>>> +client4_token=""
>>> +server4_token=""
>>> +client6_token=""
>>> +server6_token=""
>>> +client4_port=0;
>>> +client6_port=0;
>>> +app4_port=50002
>>> +new4_port=50003
>>> +app6_port=50004
>>> +client_addr_id=${RANDOM:0:2}
>>> +server_addr_id=${RANDOM:0:2}
>>> +
>>> +sec=$(date +%s)
>>> +rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
>>> +ns1="ns1-$rndh"
>>> +ns2="ns2-$rndh"
>>> +
>>> +cleanup()
>>> +{
>>> +       echo "cleanup"
>>> +
>>> +       # Terminate the MPTCP connection and related processes
>>> +       kill -SIGUSR1 $client4_pid > /dev/null 2>&1
>>> +       kill $server4_pid > /dev/null 2>&1
>>> +       kill -SIGUSR1 $client6_pid > /dev/null 2>&1
>>> +       kill $server6_pid > /dev/null 2>&1
>>> +
>>> +       kill $evts_pid > /dev/null 2>&1
>>> +
>>> +       local netns
>>> +       for netns in "$ns1" "$ns2" ;do
>>> +               ip netns del $netns
>>> +       done
>>> +}
>>> +
>>> +trap cleanup EXIT
>>> +
>>> +# Create and configure network namespaces for testing
>>> +for i in "$ns1" "$ns2" ;do
>>> +       ip netns add $i || exit 1
>>> +       ip -net $i link set lo up
>>> +       ip netns exec $i sysctl -q net.mptcp.enabled=1
>>> +       ip netns exec $i sysctl -q net.mptcp.pm_type=1
>>> +       ip netns exec $i sysctl -q net.mptcp.userspace_pm_local_addr_max=2
>>> +done
>>> +
>>> +#  "$ns1"              ns2
>>> +#     ns1eth2    ns2eth1
>>> +
>>> +ip link add ns1eth2 netns "$ns1" type veth peer name ns2eth1 netns "$ns2"
>>> +
>>> +# Add IPv4/v6 addresses to the namespaces
>>> +ip -net "$ns1" addr add 10.0.1.1/24 dev ns1eth2
>>> +ip -net "$ns1" addr add 10.0.2.1/24 dev ns1eth2
>>> +ip -net "$ns1" addr add dead:beef:1::1/64 dev ns1eth2 nodad
>>> +ip -net "$ns1" addr add dead:beef:2::1/64 dev ns1eth2 nodad
>>> +ip -net "$ns1" link set ns1eth2 up
>>> +
>>> +ip -net "$ns2" addr add 10.0.1.2/24 dev ns2eth1
>>> +ip -net "$ns2" addr add 10.0.2.2/24 dev ns2eth1
>>> +ip -net "$ns2" addr add dead:beef:1::2/64 dev ns2eth1 nodad
>>> +ip -net "$ns2" addr add dead:beef:2::2/64 dev ns2eth1 nodad
>>> +ip -net "$ns2" link set ns2eth1 up
>>> +
>>> +printf "Created network namespaces ns1, ns2         \t\t\t[OK]\n"
>>> +
>>> +make_file()
>>> +{
>>> +       # Store a chunk of data in a file to transmit over an MPTCP connection
>>> +       local name=$1
>>> +       local who=$2
>>> +       local ksize=1
>>> +
>>> +       dd if=/dev/urandom of="$name" bs=1024 count=$ksize 2> /dev/null
>>> +       echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name"
>>> +}
>>> +
>>> +make_connection()
>>> +{
>>> +       local file=$(mktemp)
>>> +       make_file "$file" "client"
>>> +
>>> +       local is_v6=$1
>>> +       local app_port=$app4_port
>>> +       local connect_addr="10.0.1.1"
>>> +       local listen_addr="0.0.0.0"
>>> +       if [ "$is_v6" = "v6" ]
>>> +       then
>>> +               connect_addr="dead:beef:1::1"
>>> +               listen_addr="::"
>>> +               app_port=$app6_port
>>> +       else
>>> +               is_v6="v4"
>>> +       fi
>>> +
>>> +       # Capture netlink events over the two network namespaces running
>>> +       # the MPTCP client and server
>>> +       local client_evts=$(mktemp)
>>> +       :>"$client_evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$client_evts" 2>&1 &
>>> +       local client_evts_pid=$!
>>> +       local server_evts=$(mktemp)
>>> +       :>"$server_evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$server_evts" 2>&1 &
>>> +       local server_evts_pid=$!
>>> +       sleep 0.1
>>> +
>>> +       # Run the server
>>> +       ip netns exec $ns1 \
>>> +                       ./mptcp_connect -s MPTCP -w 300 -p $app_port -l $listen_addr 2>&1 > /dev/null &
>>> +       local server_pid=$!
>>> +       sleep 0.1
>>> +
>>> +       # Run the client, transfer $file and stay connected to the server
>>> +       # to conduct tests
>>> +       ip netns exec $ns2 \
>>> +                       ./mptcp_connect -s MPTCP -w 300 -m sendfile -p $app_port $connect_addr 2>&1 > /dev/null < $file &
>>> +       local client_pid=$!
>>> +       sleep 0.1
>>> +
>>> +       # Capture client/server attributes from MPTCP connection netlink events
>>> +       kill $client_evts_pid
>>> +       local client_token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
>>> +       local client_port=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
>>> +       local client_serverside=$(sed -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
>>> +
>>> +       kill $server_evts_pid
>>> +       local server_token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
>>> +       local server_serverside=$(sed -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
>>> +
>>> +       rm -f "$client_evts" "$server_evts" "$file"
>>> +
>>> +       if [ $client_token != "" ] && [ $server_token != "" ] && [ $client_serverside = 0 ] && [ $server_serverside = 1 ]
>>> +       then
>>> +               printf "Established IP%s MPTCP Connection ns2 => ns1    \t\t[OK]\n" $is_v6
>>> +       else
>>> +               exit 1
>>> +       fi
>>> +
>>> +       if [ "$is_v6" = "v6" ]
>>> +       then
>>> +               client6_token=$client_token
>>> +               server6_token=$server_token
>>> +               client6_port=$client_port
>>> +               client6_pid=$client_pid
>>> +               server6_pid=$server_pid
>>> +       else
>>> +               client4_token=$client_token
>>> +               server4_token=$server_token
>>> +               client4_port=$client_port
>>> +               client4_pid=$client_pid
>>> +               server4_pid=$server_pid
>>> +       fi
>>> +}
>>> +
>>> +verify_announce_event()
>>> +{
>>> +       local evt=$1
>>> +       local e_type=$2
>>> +       local e_token=$3
>>> +       local e_addr=$4
>>> +       local e_id=$5
>>> +       local e_dport=$6
>>> +       local e_af=$7
>>> +
>>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +       local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +       local addr=""
>>> +       if [ "$e_af" = "v6" ]
>>> +       then
>>> +               addr=$(sed -n 's/.*\(daddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
>>> +       else
>>> +               addr=$(sed -n 's/.*\(daddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
>>> +       fi
>>> +       local dport=$(sed -n 's/.*\(dport:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +       local id=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$addr" = "$e_addr" ] && [ "$dport" = "$e_dport" ] && [ "$id" = "$e_id" ]
> 
> Here's one spot with leading spaces instead of tabs - looks like there are others too. Also an example of a line that could be wrapped :)
> 
> -Mat
> 
>>> +       then
>>> +               printf "[OK]\n"
>>> +               return 0
>>> +       fi
>>> +       printf "[FAIL]\n"
>>> +       exit 1
>>> +}
>>> +
>>> +test_announce()
>>> +{
>>> +       local evts=$(mktemp)
>>> +       # Capture events on the network namespace running the server
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
>>> +       evts_pid=$!
>>> +       sleep 0.1
>>> +
>>> +       # ADD_ADDR using an invalid token should result in no action
>>> +       local invalid_token=$(( $client4_token - 1))
>>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $invalid_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
>>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>>> +       printf "ADD_ADDR 10.0.2.2 (ns2) => ns1, invalid token    \t\t"
>>> +        if [ "$type" = "" ]
>>> +       then
>>> +               printf "[OK]\n"
>>> +       else
>>> +               printf "[FAIL]\n"
>>> +               exit 1
>>> +       fi
>>> +
>>> +       # ADD_ADDR from the client to server machine reusing the subflow port
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
>>> +       printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, reuse port \t\t" $client_addr_id
>>> +       sleep 0.1
>>> +       verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2" "$client_addr_id" "$client4_port"
>>> +
>>> +       # ADD_ADDR6 from the client to server machine reusing the subflow port
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl ann dead:beef:2::2 token $client6_token id $client_addr_id dev ns2eth1 2>&1 > /dev/null
>>> +       printf "ADD_ADDR6 id:%d dead:beef:2::2 (ns2) => ns1, reuse port\t\t" $client_addr_id
>>> +       sleep 0.1
>>> +       verify_announce_event "$evts" "$ANNOUNCED" "$server6_token" "dead:beef:2::2" "$client_addr_id" "$client6_port" "v6"
>>> +
>>> +       # ADD_ADDR from the client to server machine using a new port
>>> +       :>"$evts"
>>> +       client_addr_id=$((client_addr_id+1))
>>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 port $new4_port 2>&1 > /dev/null
>>> +       printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, new port \t\t\t" $client_addr_id
>>> +       sleep 0.1
>>> +       verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2" "$client_addr_id" "$new4_port"
>>> +
>>> +       kill $evts_pid
>>> +
>>> +       # Capture events on the network namespace running the client
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
>>> +       evts_pid=$!
>>> +       sleep 0.1
>>> +
>>> +       # ADD_ADDR from the server to client machine reusing the subflow port
>>> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id dev ns1eth2 2>&1 > /dev/null
>>> +       printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, reuse port \t\t" $server_addr_id
>>> +       sleep 0.1
>>> +       verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1" "$server_addr_id" "$app4_port"
>>> +
>>> +       # ADD_ADDR6 from the server to client machine reusing the subflow port
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl ann dead:beef:2::1 token $server6_token id $server_addr_id dev ns1eth2 2>&1 > /dev/null
>>> +       printf "ADD_ADDR6 id:%d dead:beef:2::1 (ns1) => ns2, reuse port\t\t" $server_addr_id
>>> +       sleep 0.1
>>> +       verify_announce_event "$evts" "$ANNOUNCED" "$client6_token" "dead:beef:2::1" "$server_addr_id" "$app6_port" "v6"
>>> +
>>> +       # ADD_ADDR from the server to client machine using a new port
>>> +       :>"$evts"
>>> +       server_addr_id=$((server_addr_id+1))
>>> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id dev ns1eth2 port $new4_port 2>&1 > /dev/null
>>> +       printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, new port \t\t\t" $server_addr_id
>>> +       sleep 0.1
>>> +       verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1" "$server_addr_id" "$new4_port"
>>> +
>>> +       kill $evts_pid
>>> +       rm -f "$evts"
>>> +}
>>> +
>>> +verify_remove_event()
>>> +{
>>> +       local evt=$1
>>> +       local e_type=$2
>>> +       local e_token=$3
>>> +       local e_id=$4
>>> +
>>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +       local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +       local id=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$id" = "$e_id" ]
>>> +       then
>>> +               printf "[OK]\n"
>>> +               return 0
>>> +       fi
>>> +       printf "[FAIL]\n"
>>> +       exit 1
>>> +}
>>> +
>>> +test_remove()
>>> +{
>>> +       local evts=$(mktemp)
>>> +
>>> +       # Capture events on the network namespace running the server
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
>>> +       evts_pid=$!
>>> +       sleep 0.1
>>> +
>>> +       # RM_ADDR using an invalid token should result in no action
>>> +       local invalid_token=$(( $client4_token - 1 ))
>>> +       ip netns exec $ns2 ./pm_nl_ctl rem token $invalid_token id $client_addr_id 2>&1 > /dev/null
>>> +       printf "RM_ADDR id:%d ns2 => ns1, invalid token                    \t" $client_addr_id
>>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>>> +       if [ "$type" = "" ]
>>> +       then
>>> +               printf "[OK]\n"
>>> +       else
>>> +               printf "[FAIL]\n"
>>> +       fi
>>> +
>>> +       # RM_ADDR using an invalid addr id should result in no action
>>> +       local invalid_id=$(( $client_addr_id + 1 ))
>>> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $invalid_id 2>&1 > /dev/null
>>> +       printf "RM_ADDR id:%d ns2 => ns1, invalid id                    \t" $invalid_id
>>> +       type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>>> +       if [ "$type" = "" ]
>>> +       then
>>> +               printf "[OK]\n"
>>> +       else
>>> +               printf "[FAIL]\n"
>>> +       fi
>>> +
>>> +       # RM_ADDR from the client to server machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $client_addr_id 2>&1 > /dev/null
>>> +       printf "RM_ADDR id:%d ns2 => ns1                                \t" $client_addr_id
>>> +       sleep 0.1
>>> +       verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id"
>>> +
>>> +       # RM_ADDR from the client to server machine
>>> +       :>"$evts"
>>> +       client_addr_id=$(( $client_addr_id - 1 ))
>>> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client4_token id $client_addr_id 2>&1 > /dev/null
>>> +       printf "RM_ADDR id:%d ns2 => ns1                                \t" $client_addr_id
>>> +       sleep 0.1
>>> +       verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id"
>>> +
>>> +       # RM_ADDR6 from the client to server machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl rem token $client6_token id $client_addr_id 2>&1 > /dev/null
>>> +       printf "RM_ADDR6 id:%d ns2 => ns1                               \t" $client_addr_id
>>> +       sleep 0.1
>>> +       verify_remove_event "$evts" "$REMOVED" "$server6_token" "$client_addr_id"
>>> +
>>> +       kill $evts_pid
>>> +
>>> +       # Capture events on the network namespace running the client
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
>>> +       evts_pid=$!
>>> +       sleep 0.1
>>> +
>>> +       # RM_ADDR from the server to client machine
>>> +       ip netns exec $ns1 ./pm_nl_ctl rem token $server4_token id $server_addr_id 2>&1 > /dev/null
>>> +       printf "RM_ADDR id:%d ns1 => ns2                                \t" $server_addr_id
>>> +       sleep 0.1
>>> +       verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id"
>>> +
>>> +       # RM_ADDR from the server to client machine
>>> +       :>"$evts"
>>> +       server_addr_id=$(( $server_addr_id - 1 ))
>>> +       ip netns exec $ns1 ./pm_nl_ctl rem token $server4_token id $server_addr_id 2>&1 > /dev/null
>>> +       printf "RM_ADDR id:%d ns1 => ns2                                \t" $server_addr_id
>>> +       sleep 0.1
>>> +       verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id"
>>> +
>>> +       # RM_ADDR6 from the server to client machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl rem token $server6_token id $server_addr_id 2>&1 > /dev/null
>>> +       printf "RM_ADDR6 id:%d ns1 => ns2                               \t" $server_addr_id
>>> +       sleep 0.1
>>> +       verify_remove_event "$evts" "$REMOVED" "$client6_token" "$server_addr_id"
>>> +
>>> +       kill $evts_pid
>>> +       rm -f "$evts"
>>> +}
>>> +
>>> +verify_subflow_events()
>>> +{
>>> +       local evt=$1
>>> +       local e_type=$2
>>> +       local e_token=$3
>>> +       local e_family=$4
>>> +       local e_saddr=$5
>>> +       local e_daddr=$6
>>> +       local e_dport=$7
>>> +       local e_locid=$8
>>> +       local e_remid=$9
>>> +       shift 2
>>> +       local e_from=$8
>>> +       local e_to=$9
>>> +
>>> +       if [ "$e_type" = "$SUB_ESTABLISHED" ]
>>> +       then
>>> +               if [ "$e_family" = "$AF_INET6" ]
>>> +               then
>>> +                       printf "CREATE_SUBFLOW6 %s (%s) => %s (%s)    " $e_saddr $e_from $e_daddr $e_to
>>> +               else
>>> +                       printf "CREATE_SUBFLOW %s (%s) => %s (%s)         \t" $e_saddr $e_from $e_daddr $e_to
>>> +               fi
>>> +       else
>>> +               if [ "$e_family" = "$AF_INET6" ]
>>> +               then
>>> +                       printf "DESTROY_SUBFLOW6 %s (%s) => %s (%s)   " $e_saddr $e_from $e_daddr $e_to
>>> +               else
>>> +                       printf "DESTROY_SUBFLOW %s (%s) => %s (%s)         \t" $e_saddr $e_from $e_daddr $e_to
>>> +               fi
>>> +       fi
>>> +
>>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +       local token=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +       local family=$(sed -n 's/.*\(family:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +       local dport=$(sed -n 's/.*\(dport:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +       local locid=$(sed -n 's/.*\(loc_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +       local remid=$(sed -n 's/.*\(rem_id:\)\([[:digit:]]*\).*$/\2/p;q' "$evt")
>>> +       local saddr=""
>>> +       local daddr=""
>>> +       if [ "$family" = "$AF_INET6" ]
>>> +       then
>>> +               saddr=$(sed -n 's/.*\(saddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
>>> +               daddr=$(sed -n 's/.*\(daddr6:\)\([0-9a-f:.]*\).*$/\2/p;q' "$evt")
>>> +       else
>>> +               saddr=$(sed -n 's/.*\(saddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
>>> +               daddr=$(sed -n 's/.*\(daddr4:\)\([0-9.]*\).*$/\2/p;q' "$evt")
>>> +       fi
>>> +
>>> +        if [ "$type" = "$e_type" ] && [ "$token" = "$e_token" ] && [ "$daddr" = "$e_daddr" ] && [ "$e_dport" = "$dport" ] && [ "$family" = "$e_family" ] && [ "$saddr" = "$e_saddr" ] && [ "$e_locid" = "$locid" ] && [ "$e_remid" = "$remid" ]
>>> +       then
>>> +               printf "[OK]\n"
>>> +               return 0
>>> +       fi
>>> +       printf "[FAIL]\n"
>>> +       exit 1
>>> +}
>>> +
>>> +test_subflows()
>>> +{
>>> +       local evts=$(mktemp)
>>> +       # Capture events on the network namespace running the server
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
>>> +       evts_pid=$!
>>> +       sleep 0.1
>>> +
>>> +       # ADD_ADDR from client to server machine reusing the subflow port
>>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +
>>> +       # CREATE_SUBFLOW from server to client machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2 rport $client4_port token $server4_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2"
>>> +
>>> +       local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>>> +
>>> +       # DESTROY_SUBFLOW from server to client machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl dsf lip 10.0.2.1 lport $sport rip 10.0.2.2 rport $client4_port token $server4_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2"
>>> +
>>> +       # RM_ADDR from client to server machine
>>> +       ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client4_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +
>>> +       # ADD_ADDR6 from client to server machine reusing the subflow port
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl ann dead:beef:2::2 token $client6_token id $client_addr_id 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +
>>> +       # CREATE_SUBFLOW6 from server to client machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl csf lip dead:beef:2::1 lid 23 rip dead:beef:2::2 rport $client6_port token $server6_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server6_token" "$AF_INET6" "dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23" "$client_addr_id" "ns1" "ns2"
>>> +
>>> +       local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>>> +
>>> +       # DESTROY_SUBFLOW6 from server to client machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl dsf lip dead:beef:2::1 lport $sport rip dead:beef:2::2 rport $client6_port token $server6_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$server6_token" "$AF_INET6" "dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23" "$client_addr_id" "ns1" "ns2"
>>> +
>>> +       # RM_ADDR from client to server machine
>>> +       ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client6_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +
>>> +       # ADD_ADDR from client to server machine using a new port
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id port $new4_port 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +
>>> +       # CREATE_SUBFLOW from server to client machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2 rport $new4_port token $server4_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$new4_port" "23" "$client_addr_id" "ns1" "ns2"
>>> +
>>> +        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>>> +
>>> +       # DESTROY_SUBFLOW from server to client machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl dsf lip 10.0.2.1 lport $sport rip 10.0.2.2 rport $new4_port token $server4_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1" "10.0.2.2" "$new4_port" "23" "$client_addr_id" "ns1" "ns2"
>>> +
>>> +       # RM_ADDR from client to server machine
>>> +       ip netns exec $ns2 ./pm_nl_ctl rem id $client_addr_id token $client4_token 2>&1 > /dev/null
>>> +
>>> +       kill $evts_pid
>>> +
>>> +       # Capture events on the network namespace running the client
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl events >> "$evts" 2>&1 &
>>> +       evts_pid=$!
>>> +       sleep 0.1
>>> +
>>> +       # ADD_ADDR from server to client machine reusing the subflow port
>>> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +
>>> +       # CREATE_SUBFLOW from client to server machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport $app4_port token $client4_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1"
>>> +
>>> +        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>>> +
>>> +       # DESTROY_SUBFLOW from client to server machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl dsf lip 10.0.2.2 lport $sport rip 10.0.2.1 rport $app4_port token $client4_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1"
>>> +
>>> +       # RM_ADDR from server to client machine
>>> +       ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server4_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +
>>> +       # ADD_ADDR6 from server to client machine reusing the subflow port
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl ann dead:beef:2::1 token $server6_token id $server_addr_id 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +
>>> +       # CREATE_SUBFLOW6 from client to server machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl csf lip dead:beef:2::2 lid 23 rip dead:beef:2::1 rport $app6_port token $client6_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client6_token" "$AF_INET6" "dead:beef:2::2" "dead:beef:2::1" "$app6_port" "23" "$server_addr_id" "ns2" "ns1"
>>> +
>>> +       local sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>>> +
>>> +       # DESTROY_SUBFLOW6 from client to server machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl dsf lip dead:beef:2::2 lport $sport rip dead:beef:2::1 rport $app6_port token $client6_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$client6_token" "$AF_INET6" "dead:beef:2::2" "dead:beef:2::1" "$app6_port" "23" "$server_addr_id" "ns2" "ns1"
>>> +
>>> +       # RM_ADDR6 from server to client machine
>>> +       ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server6_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +
>>> +       # ADD_ADDR from client to server machine using a new port
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl ann 10.0.2.1 token $server4_token id $server_addr_id port $new4_port 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +
>>> +       # CREATE_SUBFLOW from client to server machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport $new4_port token $client4_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1"
>>> +
>>> +        sport=$(sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>>> +
>>> +       # DESTROY_SUBFLOW from client to server machine
>>> +       :>"$evts"
>>> +       ip netns exec $ns2 ./pm_nl_ctl dsf lip 10.0.2.2 lport $sport rip 10.0.2.1 rport $new4_port token $client4_token 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2" "10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1"
>>> +
>>> +       # RM_ADDR from server to client machine
>>> +       ip netns exec $ns1 ./pm_nl_ctl rem id $server_addr_id token $server4_token 2>&1 > /dev/null
>>> +
>>> +       kill $evts_pid
>>> +       rm -f "$evts"
>>> +}
>>> +
>>> +test_local_addr_limit()
>>> +{
>>> +       printf "Test sysctl: net.mptcp.userspace_pm_local_addr_max=1\t\t"
>>> +       # Limit the # of local addrs for both namespaces to 1
>>> +       for i in "$ns1" "$ns2" ;do
>>> +               ip netns exec $i sysctl -q net.mptcp.userspace_pm_local_addr_max=1
>>> +       done
>>> +
>>> +       local evts=$(mktemp)
>>> +       # Capture events from the network namespace running the server
>>> +       :>"$evts"
>>> +       ip netns exec $ns1 ./pm_nl_ctl events >> "$evts" 2>&1 &
>>> +       evts_pid=$!
>>> +       sleep 0.1
>>> +
>>> +       :>"$evts"
>>> +       # ADD_ADDR from the client to server machine -> success
>>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 2>&1 > /dev/nulls
>>> +       sleep 0.1
>>> +       local type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>>> +        if [ "$type" = "" ]
>>> +       then
>>> +               printf "[FAIL]\n"
>>> +               exit 1
>>> +       fi
>>> +
>>> +       :>"$evts"
>>> +       # ADD_ADDR from the client to server machine -> fail (limit already met)
>>> +       client_addr_id=$((client_addr_id+1))
>>> +       ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id $client_addr_id dev ns2eth1 port $new4_port 2>&1 > /dev/null
>>> +       sleep 0.1
>>> +       type=$(sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
>>> +        if [ "$type" = "" ]
>>> +       then
>>> +               printf "[OK]\n"
>>> +       else
>>> +               printf "[FAIL]\n"
>>> +               exit 1
>>> +       fi
>>> +
>>> +       kill $evts_pid
>>> +       rm -f "$evts"
>>> +}
>>> +
>>> +make_connection
>>> +make_connection "v6"
>>> +test_announce
>>> +test_remove
>>> +test_subflows
>>> +test_local_addr_limit
>>> +exit 0
>>> -- 
>>> 2.31.1
>>>
>>>
>>
>>
> 
> -- 
> Mat Martineau
> Intel


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH mptcp-next v3 01/14] mptcp: add sysctl param to limit userspace PM activity
  2022-01-31 22:23     ` Kishen Maloor
@ 2022-02-01 11:49       ` Paolo Abeni
  2022-02-01 21:20         ` Kishen Maloor
  0 siblings, 1 reply; 24+ messages in thread
From: Paolo Abeni @ 2022-02-01 11:49 UTC (permalink / raw)
  To: Kishen Maloor, Geliang Tang; +Cc: MPTCP Upstream

On Mon, 2022-01-31 at 14:23 -0800, Kishen Maloor wrote:
> On 1/27/22 10:38 PM, Geliang Tang wrote:
> > Hi Kishen,
> > 
> > Kishen Maloor <kishen.maloor@intel.com> 于2022年1月28日周五 08:39写道:
> > > 
> > > This change adds a new configurable param userspace_pm_local_addr_max
> > > to set a limit on the number of local addresses that may be recorded
> > > by a userspace path manager in the context of any MPTCP connection.
> > > Consequently, it also limits the # of subflows, i.e. to as many that
> > > could be established via the bounded set of local addresses.
> > > 
> > > The patch also ensures that other stats inside struct mptcp_pm_data
> > > that are updated along kernel code paths exercised by userspace PMs
> > > remain untouched for userspace PM managed sockets.
> > > 
> > > Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
> > > ---
> > >  net/mptcp/ctrl.c       | 17 +++++++++++++++++
> > >  net/mptcp/pm.c         |  4 +++-
> > >  net/mptcp/pm_netlink.c |  3 +++
> > >  net/mptcp/protocol.h   |  1 +
> > >  4 files changed, 24 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c
> > > index ae20b7d92e28..dffc2da6d713 100644
> > > --- a/net/mptcp/ctrl.c
> > > +++ b/net/mptcp/ctrl.c
> > > @@ -19,6 +19,7 @@ static int mptcp_pernet_id;
> > > 
> > >  #ifdef CONFIG_SYSCTL
> > >  static int mptcp_pm_type_max = __MPTCP_PM_TYPE_MAX;
> > > +static int mptcp_max_addrs = MPTCP_PM_MAX_ADDR_ID;
> > >  #endif
> > > 
> > >  struct mptcp_pernet {
> > > @@ -32,6 +33,7 @@ struct mptcp_pernet {
> > >         u8 checksum_enabled;
> > >         u8 allow_join_initial_addr_port;
> > >         u8 pm_type;
> > > +       u8 userspace_pm_local_addr_max;
> > >  };
> > > 
> > >  static struct mptcp_pernet *mptcp_get_pernet(const struct net *net)
> > > @@ -69,6 +71,11 @@ int mptcp_get_pm_type(const struct net *net)
> > >         return mptcp_get_pernet(net)->pm_type;
> > >  }
> > > 
> > > +unsigned int mptcp_get_userspace_pm_local_addr_max(const struct net *net)
> > > +{
> > > +       return mptcp_get_pernet(net)->userspace_pm_local_addr_max;
> > > +}
> > > +
> > >  static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
> > >  {
> > >         pernet->mptcp_enabled = 1;
> > > @@ -77,6 +84,7 @@ static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
> > >         pernet->allow_join_initial_addr_port = 1;
> > >         pernet->stale_loss_cnt = 4;
> > >         pernet->pm_type = MPTCP_PM_TYPE_KERNEL;
> > > +       pernet->userspace_pm_local_addr_max = 10;
> > >  }
> > > 
> > >  #ifdef CONFIG_SYSCTL
> > > @@ -128,6 +136,14 @@ static struct ctl_table mptcp_sysctl_table[] = {
> > >                 .extra1       = SYSCTL_ZERO,
> > >                 .extra2       = &mptcp_pm_type_max
> > >         },
> > > +       {
> > > +               .procname = "userspace_pm_local_addr_max",
> > > +               .maxlen = sizeof(u8),
> > > +               .mode = 0644,
> > > +               .proc_handler = proc_dou8vec_minmax,
> > > +               .extra1       = SYSCTL_ONE,
> > > +               .extra2       = &mptcp_max_addrs
> > > +       },
> > >         {}
> > >  };
> > > 
> > > @@ -149,6 +165,7 @@ static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
> > >         table[3].data = &pernet->allow_join_initial_addr_port;
> > >         table[4].data = &pernet->stale_loss_cnt;
> > >         table[5].data = &pernet->pm_type;
> > > +       table[6].data = &pernet->userspace_pm_local_addr_max;
> > > 
> > >         hdr = register_net_sysctl(net, MPTCP_SYSCTL_PATH, table);
> > >         if (!hdr)
> > > diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
> > > index e5d5cb847209..24839cd7025c 100644
> > > --- a/net/mptcp/pm.c
> > > +++ b/net/mptcp/pm.c
> > > @@ -182,7 +182,9 @@ void mptcp_pm_subflow_check_next(struct mptcp_sock *msk, const struct sock *ssk,
> > >         bool update_subflows;
> > > 
> > >         update_subflows = (ssk->sk_state == TCP_CLOSE) &&
> > > -                         (subflow->request_join || subflow->mp_join);
> > > +                         (subflow->request_join || subflow->mp_join) &&
> > > +                         READ_ONCE(pm->pm_type) == MPTCP_PM_TYPE_KERNEL;
> > 
> > How about using mptcp_pm_is_userspace() instead for all the
> > MPTCP_PM_TYPE_KERNEL type check in this series?
> 
> Most PM related code paths in the kernel largely drive the in-kernel PM's flow and only a 
> small subset of those are shared with the userspace PM. Among those shared code paths, I 
> believe there's only one spot in my patches that has a userspace PM specific branch for
> which there's an explicit pm_type check against MPTCP_PM_TYPE_USERSPACE. The few remaining 
> checks are against MPTCP_PM_TYPE_KERNEL to bypass some in-kernel PM specific
> behaviors in *all* "non-kernel" PMs. As we may have > 2 PM types by construction, the code
> does not assume the userspace PM when pm_type != MPTCP_PM_TYPE_KERNEL.

You can introduce and use 'mptcp_pm_is_kernespace()'. There are a few
more chance to use it in later patches.

Thanks!

Paolo


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH mptcp-next v3 03/14] mptcp: handle local addrs announced by userspace PMs
  2022-01-28  0:38 ` [PATCH mptcp-next v3 03/14] mptcp: handle local addrs announced " Kishen Maloor
@ 2022-02-01 11:58   ` Paolo Abeni
  2022-02-01 21:21     ` Kishen Maloor
  0 siblings, 1 reply; 24+ messages in thread
From: Paolo Abeni @ 2022-02-01 11:58 UTC (permalink / raw)
  To: Kishen Maloor, mptcp

On Thu, 2022-01-27 at 19:38 -0500, Kishen Maloor wrote:
> This change adds a new internal function to store/retrieve local
> addrs announced by userspace PM implementations to/from its kernel
> context. The function captures the requirements of three scenarios:
> 1) ADD_ADDR announcements (which require that a local id be
> provided), 2) retrieving the local id associated with an address,
> also where one may need to be assigned, and 3) reissuance of
> ADD_ADDRs when there's a successful match of addr/id.
> 
> The list of all stored local addr entries is held under the
> MPTCP sock structure. This list, if not released by the REMOVE_ADDR
> flow is freed while the sock is destructed.
> 
> Additionally, this function enforces the kernel imposed limit on
> the number of local addresses that may be used over a connection.
> 
> Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
> ---
> v3: incorporate the new sysctl configurable limit on the # of local
> addresses that may be populated by userspace PMs

I think here we could use omem instead and avoid introducing another -
possibly hard to configure properly - sysctl.

/P


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH mptcp-next v3 01/14] mptcp: add sysctl param to limit userspace PM activity
  2022-02-01 11:49       ` Paolo Abeni
@ 2022-02-01 21:20         ` Kishen Maloor
  0 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-02-01 21:20 UTC (permalink / raw)
  To: Paolo Abeni, Geliang Tang; +Cc: MPTCP Upstream

On 2/1/22 3:49 AM, Paolo Abeni wrote:
> On Mon, 2022-01-31 at 14:23 -0800, Kishen Maloor wrote:
>> On 1/27/22 10:38 PM, Geliang Tang wrote:
>>> Hi Kishen,
>>>
>>> Kishen Maloor <kishen.maloor@intel.com> 于2022年1月28日周五 08:39写道:
>>>>
>>>> This change adds a new configurable param userspace_pm_local_addr_max
>>>> to set a limit on the number of local addresses that may be recorded
>>>> by a userspace path manager in the context of any MPTCP connection.
>>>> Consequently, it also limits the # of subflows, i.e. to as many that
>>>> could be established via the bounded set of local addresses.
>>>>
>>>> The patch also ensures that other stats inside struct mptcp_pm_data
>>>> that are updated along kernel code paths exercised by userspace PMs
>>>> remain untouched for userspace PM managed sockets.
>>>>
>>>> Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
>>>> ---
>>>>  net/mptcp/ctrl.c       | 17 +++++++++++++++++
>>>>  net/mptcp/pm.c         |  4 +++-
>>>>  net/mptcp/pm_netlink.c |  3 +++
>>>>  net/mptcp/protocol.h   |  1 +
>>>>  4 files changed, 24 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c
>>>> index ae20b7d92e28..dffc2da6d713 100644
>>>> --- a/net/mptcp/ctrl.c
>>>> +++ b/net/mptcp/ctrl.c
>>>> @@ -19,6 +19,7 @@ static int mptcp_pernet_id;
>>>>
>>>>  #ifdef CONFIG_SYSCTL
>>>>  static int mptcp_pm_type_max = __MPTCP_PM_TYPE_MAX;
>>>> +static int mptcp_max_addrs = MPTCP_PM_MAX_ADDR_ID;
>>>>  #endif
>>>>
>>>>  struct mptcp_pernet {
>>>> @@ -32,6 +33,7 @@ struct mptcp_pernet {
>>>>         u8 checksum_enabled;
>>>>         u8 allow_join_initial_addr_port;
>>>>         u8 pm_type;
>>>> +       u8 userspace_pm_local_addr_max;
>>>>  };
>>>>
>>>>  static struct mptcp_pernet *mptcp_get_pernet(const struct net *net)
>>>> @@ -69,6 +71,11 @@ int mptcp_get_pm_type(const struct net *net)
>>>>         return mptcp_get_pernet(net)->pm_type;
>>>>  }
>>>>
>>>> +unsigned int mptcp_get_userspace_pm_local_addr_max(const struct net *net)
>>>> +{
>>>> +       return mptcp_get_pernet(net)->userspace_pm_local_addr_max;
>>>> +}
>>>> +
>>>>  static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
>>>>  {
>>>>         pernet->mptcp_enabled = 1;
>>>> @@ -77,6 +84,7 @@ static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
>>>>         pernet->allow_join_initial_addr_port = 1;
>>>>         pernet->stale_loss_cnt = 4;
>>>>         pernet->pm_type = MPTCP_PM_TYPE_KERNEL;
>>>> +       pernet->userspace_pm_local_addr_max = 10;
>>>>  }
>>>>
>>>>  #ifdef CONFIG_SYSCTL
>>>> @@ -128,6 +136,14 @@ static struct ctl_table mptcp_sysctl_table[] = {
>>>>                 .extra1       = SYSCTL_ZERO,
>>>>                 .extra2       = &mptcp_pm_type_max
>>>>         },
>>>> +       {
>>>> +               .procname = "userspace_pm_local_addr_max",
>>>> +               .maxlen = sizeof(u8),
>>>> +               .mode = 0644,
>>>> +               .proc_handler = proc_dou8vec_minmax,
>>>> +               .extra1       = SYSCTL_ONE,
>>>> +               .extra2       = &mptcp_max_addrs
>>>> +       },
>>>>         {}
>>>>  };
>>>>
>>>> @@ -149,6 +165,7 @@ static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
>>>>         table[3].data = &pernet->allow_join_initial_addr_port;
>>>>         table[4].data = &pernet->stale_loss_cnt;
>>>>         table[5].data = &pernet->pm_type;
>>>> +       table[6].data = &pernet->userspace_pm_local_addr_max;
>>>>
>>>>         hdr = register_net_sysctl(net, MPTCP_SYSCTL_PATH, table);
>>>>         if (!hdr)
>>>> diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
>>>> index e5d5cb847209..24839cd7025c 100644
>>>> --- a/net/mptcp/pm.c
>>>> +++ b/net/mptcp/pm.c
>>>> @@ -182,7 +182,9 @@ void mptcp_pm_subflow_check_next(struct mptcp_sock *msk, const struct sock *ssk,
>>>>         bool update_subflows;
>>>>
>>>>         update_subflows = (ssk->sk_state == TCP_CLOSE) &&
>>>> -                         (subflow->request_join || subflow->mp_join);
>>>> +                         (subflow->request_join || subflow->mp_join) &&
>>>> +                         READ_ONCE(pm->pm_type) == MPTCP_PM_TYPE_KERNEL;
>>>
>>> How about using mptcp_pm_is_userspace() instead for all the
>>> MPTCP_PM_TYPE_KERNEL type check in this series?
>>
>> Most PM related code paths in the kernel largely drive the in-kernel PM's flow and only a 
>> small subset of those are shared with the userspace PM. Among those shared code paths, I 
>> believe there's only one spot in my patches that has a userspace PM specific branch for
>> which there's an explicit pm_type check against MPTCP_PM_TYPE_USERSPACE. The few remaining 
>> checks are against MPTCP_PM_TYPE_KERNEL to bypass some in-kernel PM specific
>> behaviors in *all* "non-kernel" PMs. As we may have > 2 PM types by construction, the code
>> does not assume the userspace PM when pm_type != MPTCP_PM_TYPE_KERNEL.
> 
> You can introduce and use 'mptcp_pm_is_kernespace()'. There are a few
> more chance to use it in later patches.

Sure, I can introduce that API, thanks!

> 
> Thanks!
> 
> Paolo
> 


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH mptcp-next v3 03/14] mptcp: handle local addrs announced by userspace PMs
  2022-02-01 11:58   ` Paolo Abeni
@ 2022-02-01 21:21     ` Kishen Maloor
  0 siblings, 0 replies; 24+ messages in thread
From: Kishen Maloor @ 2022-02-01 21:21 UTC (permalink / raw)
  To: Paolo Abeni, mptcp

On 2/1/22 3:58 AM, Paolo Abeni wrote:
> On Thu, 2022-01-27 at 19:38 -0500, Kishen Maloor wrote:
>> This change adds a new internal function to store/retrieve local
>> addrs announced by userspace PM implementations to/from its kernel
>> context. The function captures the requirements of three scenarios:
>> 1) ADD_ADDR announcements (which require that a local id be
>> provided), 2) retrieving the local id associated with an address,
>> also where one may need to be assigned, and 3) reissuance of
>> ADD_ADDRs when there's a successful match of addr/id.
>>
>> The list of all stored local addr entries is held under the
>> MPTCP sock structure. This list, if not released by the REMOVE_ADDR
>> flow is freed while the sock is destructed.
>>
>> Additionally, this function enforces the kernel imposed limit on
>> the number of local addresses that may be used over a connection.
>>
>> Signed-off-by: Kishen Maloor <kishen.maloor@intel.com>
>> ---
>> v3: incorporate the new sysctl configurable limit on the # of local
>> addresses that may be populated by userspace PMs
> 
> I think here we could use omem instead and avoid introducing another -
> possibly hard to configure properly - sysctl.
> 

I will look into using omem here as an alternative limit/bound to
the sysctl param.

> /P
> 


^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2022-02-01 21:21 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-28  0:38 [PATCH mptcp-next v3 00/14] mptcp: APIs and self-tests for userspace path management Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 01/14] mptcp: add sysctl param to limit userspace PM activity Kishen Maloor
2022-01-28  6:38   ` Geliang Tang
2022-01-31 22:23     ` Kishen Maloor
2022-02-01 11:49       ` Paolo Abeni
2022-02-01 21:20         ` Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 02/14] mptcp: allow ADD_ADDR reissuance by userspace PMs Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 03/14] mptcp: handle local addrs announced " Kishen Maloor
2022-02-01 11:58   ` Paolo Abeni
2022-02-01 21:21     ` Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 04/14] mptcp: read attributes of addr entries managed " Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 05/14] mptcp: netlink: split mptcp_pm_parse_addr into two functions Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 06/14] mptcp: netlink: Add MPTCP_PM_CMD_ANNOUNCE Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 07/14] mptcp: selftests: support MPTCP_PM_CMD_ANNOUNCE Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 08/14] mptcp: netlink: Add MPTCP_PM_CMD_REMOVE Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 09/14] mptcp: selftests: support MPTCP_PM_CMD_REMOVE Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 10/14] mptcp: netlink: allow userspace-driven subflow establishment Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 11/14] mptcp: selftests: support MPTCP_PM_CMD_SUBFLOW_CREATE Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 12/14] mptcp: selftests: support MPTCP_PM_CMD_SUBFLOW_DESTROY Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 13/14] mptcp: selftests: capture netlink events Kishen Maloor
2022-01-28  0:38 ` [PATCH mptcp-next v3 14/14] selftests: mptcp: functional tests for the userspace PM type Kishen Maloor
2022-01-28  6:51   ` Geliang Tang
2022-01-28 19:48     ` Mat Martineau
2022-01-31 22:24       ` Kishen Maloor

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.