From patchwork Mon Feb 17 18:28:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Abeni X-Patchwork-Id: 1239496 X-Patchwork-Delegate: pabeni@redhat.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.01.org (client-ip=2001:19d0:306:5::1; helo=ml01.01.org; envelope-from=mptcp-bounces@lists.01.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=FbdyQUcJ; dkim-atps=neutral Received: from ml01.01.org (ml01.01.org [IPv6:2001:19d0:306:5::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48Lsv54Jrsz9sRY for ; Tue, 18 Feb 2020 05:28:49 +1100 (AEDT) Received: from ml01.vlan13.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id D85BB10FC358C; Mon, 17 Feb 2020 10:32:04 -0800 (PST) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=207.211.31.81; helo=us-smtp-delivery-1.mimecast.com; envelope-from=pabeni@redhat.com; receiver= Received: from us-smtp-delivery-1.mimecast.com (us-smtp-1.mimecast.com [207.211.31.81]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id B804610FC3419 for ; Mon, 17 Feb 2020 10:32:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1581964124; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=aJf9c/QtRRf+D7F6jMJqsvqf2RFtJ7ROLKlsX+U86i0=; b=FbdyQUcJrkOVLiPCRBeVrHZX+qfTccrcGaEndyQgDY/udvQ34gIcqbDMSOSM5d6nA4Q+98 Tm9YdFRDT28u1QRxnbGHotnLZXCDAG44wmCHiEgJziXiizzAjPNjyqlMeSP9ANCIVdaIbx h0PQbSYFPOhuj7TqInl1wIF1bAAx47M= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-235-yLLB_qE0Px2PW0mZ4RnR2g-1; Mon, 17 Feb 2020 13:28:43 -0500 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 29B6C107ACCD for ; Mon, 17 Feb 2020 18:28:42 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-116-153.ams2.redhat.com [10.36.116.153]) by smtp.corp.redhat.com (Postfix) with ESMTP id 880C485735 for ; Mon, 17 Feb 2020 18:28:41 +0000 (UTC) From: Paolo Abeni To: mptcp@lists.01.org Date: Mon, 17 Feb 2020 19:28:27 +0100 Message-Id: <0155ff6dc2c1125c897273d55526856df8a184d9.1581963738.git.pabeni@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-MC-Unique: yLLB_qE0Px2PW0mZ4RnR2g-1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Message-ID-Hash: JACYLRGNRHPEAP4U2VU6Y6SFDN2GQQFR X-Message-ID-Hash: JACYLRGNRHPEAP4U2VU6Y6SFDN2GQQFR X-MailFrom: pabeni@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.1.1 Precedence: list Subject: [MPTCP] [PATCH 1/7] Squash-to: "mptcp: Add ADD_ADDR handling" List-Id: Discussions regarding MPTCP upstreaming Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: Drop addr_signal flag: it belong to the PM and needs additional locking Signed-off-by: Paolo Abeni --- net/mptcp/options.c | 5 ----- net/mptcp/protocol.h | 1 - 2 files changed, 6 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 439bb14e29dc..331b020d64ae 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -438,9 +438,6 @@ static bool mptcp_established_options_addr(struct sock *sk, if (!msk) return false; - if (!msk->addr_signal) - return false; - id = 0; memset(&saddr, 0, sizeof(saddr)); @@ -463,8 +460,6 @@ static bool mptcp_established_options_addr(struct sock *sk, } #endif - msk->addr_signal = 0; - return true; } diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index fa707a57f000..65ba8ddb62e6 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -91,7 +91,6 @@ struct mptcp_sock { struct skb_ext *cached_ext; /* for the next sendmsg */ struct socket *subflow; /* outgoing connect/listener/!mp_capable */ struct sock *first; - u8 addr_signal; }; #define mptcp_for_each_subflow(__msk, __subflow) \ From patchwork Mon Feb 17 18:28:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Abeni X-Patchwork-Id: 1239501 X-Patchwork-Delegate: pabeni@redhat.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.01.org (client-ip=198.145.21.10; helo=ml01.01.org; envelope-from=mptcp-bounces@lists.01.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=D9akrSxa; dkim-atps=neutral Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48LsvK1gvqz9sRJ for ; Tue, 18 Feb 2020 05:29:01 +1100 (AEDT) Received: from ml01.vlan13.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 18B5B10FC358D; Mon, 17 Feb 2020 10:32:16 -0800 (PST) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=207.211.31.120; helo=us-smtp-1.mimecast.com; envelope-from=pabeni@redhat.com; receiver= Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [207.211.31.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id B4B5310FC3419 for ; Mon, 17 Feb 2020 10:32:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1581964130; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=rJwzyfDe0m31ydWl2VBbc2zCA6lGFTlFGE9uYo7pPkU=; b=D9akrSxaP+zwyb/AvWj5bo61HAH5enia0uxp7G4bOIQ8k0X8iPfNJ8uEQooU+e1VBoCQ3b MYAl6URSJRDzvWHya/T5P3EY0xuI80COPHVO864THWr6s+fLOn8GAi9d/8azlD3Dm2XCQY cye+NSvGYeoTYRV5uptR6wIUlfpGOm8= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-419-02QaftaFNDq7hOM95s2BAw-1; Mon, 17 Feb 2020 13:28:44 -0500 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 5C0CB8017CC for ; Mon, 17 Feb 2020 18:28:43 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-116-153.ams2.redhat.com [10.36.116.153]) by smtp.corp.redhat.com (Postfix) with ESMTP id 826B785735 for ; Mon, 17 Feb 2020 18:28:42 +0000 (UTC) From: Paolo Abeni To: mptcp@lists.01.org Date: Mon, 17 Feb 2020 19:28:28 +0100 Message-Id: <5819c30a9cd47afa706a471949eb43fbbc1b72f6.1581963738.git.pabeni@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-MC-Unique: 02QaftaFNDq7hOM95s2BAw-1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Message-ID-Hash: 2MBD7E6XNHEZ3IIBNOYZEEVT3TYM7HFE X-Message-ID-Hash: 2MBD7E6XNHEZ3IIBNOYZEEVT3TYM7HFE X-MailFrom: pabeni@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.1.1 Precedence: list Subject: [MPTCP] [PATCH 2/7] Squash-to: "mptcp: Add path manager interface" List-Id: Discussions regarding MPTCP upstreaming Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: Factor out PM addr info to simplify PM data definition and simplify the PM hooks/APIs. The PM will carry a single local and remote addrs - the one currently being processed, if any. It's up to the PM impl, e.g. netlink, maintain additional per msk data, if needed. Account the number of created subflow and received addresses, to enforce limits. Add a spinlock to protect PM datas, so that we can manipulate them from subflow BH. Delegate events handling to a workqueue, so that PM events can be processed with both the above spinlock and the msk socket lock held. The PM impl should hook inside the worker. Signed-off-by: Paolo Abeni --- net/mptcp/options.c | 45 +++++++++-------- net/mptcp/pm.c | 112 ++++++++++++++----------------------------- net/mptcp/protocol.c | 2 + net/mptcp/protocol.h | 98 ++++++++++++++++++++++--------------- 4 files changed, 122 insertions(+), 135 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 6a2d6cea7d54..13fe6245cfcf 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -432,34 +432,30 @@ static bool mptcp_established_options_addr(struct sock *sk, { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); struct mptcp_sock *msk = mptcp_sk(subflow->conn); - struct sockaddr_storage saddr; - u8 id; + struct mptcp_addr_info saddr; + int len; if (!msk) return false; - if (!msk->pm.fully_established) + if (!mptcp_pm_should_signal(msk) || + !(mptcp_pm_addr_signal(msk, remaining, &saddr))) return false; - if (mptcp_pm_addr_signal(msk, &id, &saddr)) + len = mptcp_add_addr_len(saddr.family); + if (remaining < len) return false; - if (saddr.ss_family == AF_INET) { - if (remaining < TCPOLEN_MPTCP_ADD_ADDR) - return false; + *size = len; + opts->addr_id = saddr.id; + if (saddr.family == AF_INET) { opts->suboptions |= OPTION_MPTCP_ADD_ADDR; - opts->addr_id = id; - opts->addr = ((struct sockaddr_in *)&saddr)->sin_addr; - *size = TCPOLEN_MPTCP_ADD_ADDR; + opts->addr = saddr.addr; } #if IS_ENABLED(CONFIG_MPTCP_IPV6) - else if (saddr.ss_family == AF_INET6) { - if (remaining < TCPOLEN_MPTCP_ADD_ADDR6) - return false; + else if (saddr.family == AF_INET6) { opts->suboptions |= OPTION_MPTCP_ADD_ADDR6; - opts->addr_id = id; - opts->addr6 = ((struct sockaddr_in6 *)&saddr)->sin6_addr; - *size = TCPOLEN_MPTCP_ADD_ADDR6; + opts->addr6 = saddr.addr6; } #endif @@ -557,13 +553,20 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb, return; if (msk && mp_opt->add_addr) { - if (mp_opt->family == MPTCP_ADDR_IPVERSION_4) - mptcp_pm_add_addr(msk, &mp_opt->addr, mp_opt->addr_id); + struct mptcp_addr_info addr; + + addr.id = mp_opt->addr_id; + if (mp_opt->family == MPTCP_ADDR_IPVERSION_4) { + addr.family = AF_INET; + addr.addr = mp_opt->addr; + } #if IS_ENABLED(CONFIG_MPTCP_IPV6) - else if (mp_opt->family == MPTCP_ADDR_IPVERSION_6) - mptcp_pm_add_addr6(msk, &mp_opt->addr6, - mp_opt->addr_id); + else if (mp_opt->family == MPTCP_ADDR_IPVERSION_6) { + addr.family = AF_INET6; + addr.addr6 = mp_opt->addr6; + } #endif + mptcp_pm_add_addr(msk, &addr); mp_opt->add_addr = 0; } diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index c9f508451f2e..ca6596bd7eab 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -8,38 +8,22 @@ #include #include "protocol.h" -/* path manager command handlers */ - -int mptcp_pm_announce_addr(u32 token, u8 local_id, struct in_addr *addr) -{ - return -ENOTSUPP; -} - -#if IS_ENABLED(CONFIG_MPTCP_IPV6) -int mptcp_pm_announce_addr6(u32 token, u8 local_id, struct in6_addr *addr) -{ - return -ENOTSUPP; -} -#endif +static struct workqueue_struct *pm_wq; -int mptcp_pm_remove_addr(u32 token, u8 local_id) -{ - return -ENOTSUPP; -} +/* path manager command handlers */ -int mptcp_pm_create_subflow(u32 token, u8 remote_id, struct in_addr *addr) +int mptcp_pm_announce_addr(struct mptcp_sock *msk, + const struct mptcp_addr_info *addr) { return -ENOTSUPP; } -#if IS_ENABLED(CONFIG_MPTCP_IPV6) -int mptcp_pm_create_subflow6(u32 token, u8 remote_id, struct in6_addr *addr) +int mptcp_pm_remove_addr(struct mptcp_sock *msk, u8 local_id) { return -ENOTSUPP; } -#endif -int mptcp_pm_remove_subflow(u32 token, u8 remote_id) +int mptcp_pm_remove_subflow(struct mptcp_sock *msk, u8 remote_id) { return -ENOTSUPP; } @@ -50,10 +34,9 @@ void mptcp_pm_new_connection(struct mptcp_sock *msk, int server_side) { struct mptcp_pm_data *pm = &msk->pm; - pr_debug("msk=%p, token=%u", msk, msk->token); + pr_debug("msk=%p, token=%u side=%d", msk, msk->token, server_side); - pm->server_side = server_side; - pm->token = msk->token; + WRITE_ONCE(pm->server_side, server_side); } void mptcp_pm_fully_established(struct mptcp_sock *msk) @@ -61,8 +44,6 @@ void mptcp_pm_fully_established(struct mptcp_sock *msk) struct mptcp_pm_data *pm = &msk->pm; pr_debug("msk=%p", msk); - - pm->fully_established = 1; } void mptcp_pm_connection_closed(struct mptcp_sock *msk) @@ -70,7 +51,8 @@ void mptcp_pm_connection_closed(struct mptcp_sock *msk) pr_debug("msk=%p", msk); } -void mptcp_pm_subflow_established(struct mptcp_sock *msk, u8 id) +void mptcp_pm_subflow_established(struct mptcp_sock *msk, + struct mptcp_subflow_context *subflow) { pr_debug("msk=%p", msk); } @@ -80,71 +62,49 @@ void mptcp_pm_subflow_closed(struct mptcp_sock *msk, u8 id) pr_debug("msk=%p", msk); } -void mptcp_pm_add_addr(struct mptcp_sock *msk, const struct in_addr *addr, - u8 id) +void mptcp_pm_add_addr(struct mptcp_sock *msk, + const struct mptcp_addr_info *addr) { struct mptcp_pm_data *pm = &msk->pm; - pr_debug("msk=%p, addr=%x, remote_id=%d", msk, addr->s_addr, id); - - pm->remote_addr = *addr; - pm->remote_id = id; - pm->remote_family = AF_INET; - pm->remote_valid = 1; -} - -void mptcp_pm_add_addr6(struct mptcp_sock *msk, const struct in6_addr *addr, - u8 id) -{ - pr_debug("msk=%p", msk); + pr_debug("msk=%p, remote_id=%d", msk, addr->id); } /* path manager helpers */ -int mptcp_pm_addr_signal(struct mptcp_sock *msk, u8 *id, - struct sockaddr_storage *saddr) +int mptcp_pm_addr_signal(struct mptcp_sock *msk, unsigned int remaining, + struct mptcp_addr_info *saddr) { -#if IS_ENABLED(CONFIG_MPTCP_IPV6) - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)saddr; -#endif - struct sockaddr_in *addr = (struct sockaddr_in *)saddr; - - if (!msk->pm.local_valid) - return -1; - - if (msk->pm.local_family == AF_INET) { - addr->sin_family = msk->pm.local_family; - addr->sin_addr = msk->pm.local_addr; -#if IS_ENABLED(CONFIG_MPTCP_IPV6) - } else if (msk->pm.local_family == AF_INET6) { - addr6->sin6_family = msk->pm.local_family; - addr6->sin6_addr = msk->pm.local_addr6; -#endif - } else { - return -1; - } - *id = msk->pm.local_id; - return 0; } -int mptcp_pm_get_local_id(struct request_sock *req, struct sock *sk, - const struct sk_buff *skb) +int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc) { - struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req); - struct mptcp_sock *msk = mptcp_sk(sk); - - if (!msk->pm.local_valid) - return -1; + return 0; +} - /* @@ check if address actually matches... */ +static void pm_worker(struct work_struct *work) +{ +} - pr_debug("msk=%p, addr_id=%d", msk, msk->pm.local_id); - subflow_req->local_id = msk->pm.local_id; +void mptcp_pm_data_init(struct mptcp_sock *msk) +{ + msk->pm.add_addr_signaled = 0; + msk->pm.add_addr_accepted = 0; + msk->pm.local_addr_used = 0; + WRITE_ONCE(msk->pm.work_pending, false); + WRITE_ONCE(msk->pm.addr_signal, false); + WRITE_ONCE(msk->pm.fully_established, false); + WRITE_ONCE(msk->pm.accept_addr, false); + msk->pm.status = MPTCP_PM_IDLE; - return 0; + spin_lock_init(&msk->pm.lock); + INIT_WORK(&msk->pm.work, pm_worker); } void mptcp_pm_init(void) { + pm_wq = alloc_workqueue("pm_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 8); + if (!pm_wq) + panic("Failed to allocate workqueue"); } diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 620af9d1c6bc..a30d26661b61 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -605,6 +605,8 @@ static int __mptcp_init_sock(struct sock *sk) msk->first = NULL; + mptcp_pm_data_init(msk); + return 0; } diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 1d658d9aac36..09a261f68c1d 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -76,32 +76,47 @@ static inline __be32 mptcp_option(u8 subopt, u8 len, u8 nib, u8 field) ((nib & 0xF) << 8) | field); } -struct mptcp_pm_data { - u8 local_valid; - u8 local_id; - sa_family_t local_family; - union { - struct in_addr local_addr; -#if IS_ENABLED(CONFIG_MPTCP_IPV6) - struct in6_addr local_addr6; -#endif - }; - u8 remote_valid; - u8 remote_id; - sa_family_t remote_family; +#define MPTCP_PM_MAX_ADDR 4 + +struct mptcp_addr_info { + sa_family_t family; + __be16 port; + u8 id; union { - struct in_addr remote_addr; + struct in_addr addr; #if IS_ENABLED(CONFIG_MPTCP_IPV6) - struct in6_addr remote_addr6; + struct in6_addr addr6; #endif }; - u8 server_side : 1, - fully_established : 1; +}; - /* for interim path manager */ - struct work_struct addr_work; - struct work_struct subflow_work; - u32 token; +enum mptcp_pm_status { + MPTCP_PM_IDLE, + MPTCP_PM_ADD_ADDR, + MPTCP_PM_ESTABLISHED, + MPTCP_PM_SUBFLOW_ESTABLISHED, +}; + +struct mptcp_pm_data { + struct mptcp_addr_info local; + struct mptcp_addr_info remote; + + spinlock_t lock; /*protects the whole PM data */ + + bool addr_signal; + bool server_side; + bool fully_established; + bool work_pending; + bool accept_addr; + u8 add_addr_signaled; + u8 add_addr_accepted; + u8 local_addr_used; + u8 add_addr_signal_max; + u8 add_addr_accept_max; + u8 local_addr_max; + enum mptcp_pm_status status; + + struct work_struct work; }; /* MPTCP connection sock */ @@ -273,29 +288,36 @@ void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u32 nonce1, u32 nonce2, void *hash_out); void mptcp_pm_init(void); +void mptcp_pm_data_init(struct mptcp_sock *msk); void mptcp_pm_new_connection(struct mptcp_sock *msk, int server_side); void mptcp_pm_fully_established(struct mptcp_sock *msk); void mptcp_pm_connection_closed(struct mptcp_sock *msk); -void mptcp_pm_subflow_established(struct mptcp_sock *msk, u8 id); +void mptcp_pm_subflow_established(struct mptcp_sock *msk, + struct mptcp_subflow_context *subflow); void mptcp_pm_subflow_closed(struct mptcp_sock *msk, u8 id); -void mptcp_pm_add_addr(struct mptcp_sock *msk, const struct in_addr *addr, - u8 id); -void mptcp_pm_add_addr6(struct mptcp_sock *msk, const struct in6_addr *addr, - u8 id); +void mptcp_pm_add_addr(struct mptcp_sock *msk, + const struct mptcp_addr_info *addr); -int mptcp_pm_announce_addr(u32 token, u8 local_id, struct in_addr *addr); -int mptcp_pm_create_subflow(u32 token, u8 remote_id, struct in_addr *addr); -#if IS_ENABLED(CONFIG_MPTCP_IPV6) -int mptcp_pm_announce_addr6(u32 token, u8 local_id, struct in6_addr *addr); -int mptcp_pm_create_subflow6(u32 token, u8 remote_id, struct in6_addr *addr); -#endif -int mptcp_pm_remove_addr(u32 token, u8 local_id); -int mptcp_pm_remove_subflow(u32 token, u8 remote_id); +int mptcp_pm_announce_addr(struct mptcp_sock *msk, + const struct mptcp_addr_info *addr); +int mptcp_pm_remove_addr(struct mptcp_sock *msk, u8 local_id); +int mptcp_pm_remove_subflow(struct mptcp_sock *msk, u8 remote_id); + +static inline bool mptcp_pm_should_signal(struct mptcp_sock *msk) +{ + return READ_ONCE(msk->pm.addr_signal); +} + +static inline unsigned int mptcp_add_addr_len(int family) +{ + if (family == AF_INET) + return OPTION_MPTCP_ADD_ADDR; + return OPTION_MPTCP_ADD_ADDR6; +} -int mptcp_pm_addr_signal(struct mptcp_sock *msk, u8 *id, - struct sockaddr_storage *saddr); -int mptcp_pm_get_local_id(struct request_sock *req, struct sock *sk, - const struct sk_buff *skb); +int mptcp_pm_addr_signal(struct mptcp_sock *msk, unsigned int remaining, + struct mptcp_addr_info *saddr); +int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc); static inline struct mptcp_ext *mptcp_get_ext(struct sk_buff *skb) { From patchwork Mon Feb 17 18:28:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Abeni X-Patchwork-Id: 1239497 X-Patchwork-Delegate: pabeni@redhat.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.01.org (client-ip=2001:19d0:306:5::1; helo=ml01.01.org; envelope-from=mptcp-bounces@lists.01.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=A0Ydc08v; dkim-atps=neutral Received: from ml01.01.org (ml01.01.org [IPv6:2001:19d0:306:5::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48Lsv74N73z9sRJ for ; Tue, 18 Feb 2020 05:28:51 +1100 (AEDT) Received: from ml01.vlan13.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id F179910FC3593; Mon, 17 Feb 2020 10:32:06 -0800 (PST) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=207.211.31.120; helo=us-smtp-1.mimecast.com; envelope-from=pabeni@redhat.com; receiver= Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [207.211.31.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 86F5310FC3585 for ; Mon, 17 Feb 2020 10:32:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1581964126; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8sD5ChUxX/Uvc2Qx8uPyiUd4FBejHabB1H5ZOhF1i1c=; b=A0Ydc08vz93+7+/aoKL1586qBg/IaA9wp8hgFe4PWyc+1tDdtZ1Fy5l9APaNdj5lYyVXB2 3M06RhriNJ4gjOPUpS9LMHwGcuMH94/5nOE8wFBMC5xgiPyiMOXfSf0m/K3fMprO1C6Kaz 4WaCnefaYbFACnOpSd04BLcqqWf6r1M= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-255-18_mEZg9PCqMbAVlvd7N1Q-1; Mon, 17 Feb 2020 13:28:45 -0500 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 50B78107ACC5 for ; Mon, 17 Feb 2020 18:28:44 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-116-153.ams2.redhat.com [10.36.116.153]) by smtp.corp.redhat.com (Postfix) with ESMTP id B172885735 for ; Mon, 17 Feb 2020 18:28:43 +0000 (UTC) From: Paolo Abeni To: mptcp@lists.01.org Date: Mon, 17 Feb 2020 19:28:29 +0100 Message-Id: In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-MC-Unique: 18_mEZg9PCqMbAVlvd7N1Q-1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Message-ID-Hash: 2BB6FLFFUXXHNWF3AKYZVZSPWCCCMP6F X-Message-ID-Hash: 2BB6FLFFUXXHNWF3AKYZVZSPWCCCMP6F X-MailFrom: pabeni@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.1.1 Precedence: list Subject: [MPTCP] [PATCH 3/7] Squash-to: "mptcp: Add handling of incoming MP_JOIN requests" List-Id: Discussions regarding MPTCP upstreaming Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: Update mptcp_pm_get_local_id() callers to new API. Hook mptcp_pm_subflow_established() when MP_JOIN completes successfully Signed-off-by: Paolo Abeni --- net/mptcp/options.c | 7 +++++-- net/mptcp/subflow.c | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 43bfeb3fa753..c2e0576065b6 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -128,7 +128,6 @@ void mptcp_parse_option(const struct sk_buff *skb, const unsigned char *ptr, } break; - case MPTCPOPT_DSS: pr_debug("DSS"); ptr++; @@ -565,8 +564,12 @@ static bool check_fourth_ack(struct mptcp_subflow_context *subflow, TCP_SKB_CB(skb)->seq != subflow->ssn_offset + 1)) return true; - if (mp_opt->use_ack) + if (mp_opt->use_ack) { subflow->fourth_ack = 1; + if (subflow->mp_join) + mptcp_pm_subflow_established(mptcp_sk(subflow->conn), + subflow); + } if (subflow->can_ack) return true; diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index a8dedbd8d458..5710b439e418 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -55,6 +55,7 @@ static bool subflow_token_join_request(struct request_sock *req, struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req); u8 hmac[MPTCPOPT_HMAC_LEN]; struct mptcp_sock *msk; + int local_id; msk = mptcp_token_get_sock(subflow_req->token); if (!msk) { @@ -63,10 +64,12 @@ static bool subflow_token_join_request(struct request_sock *req, return false; } - if (mptcp_pm_get_local_id(req, (struct sock *)msk, skb)) { + local_id = mptcp_pm_get_local_id(msk, (struct sock_common *)req); + if (local_id < 0) { sock_put((struct sock *)msk); return false; } + subflow_req->local_id = local_id; get_random_bytes(&subflow_req->local_nonce, sizeof(u32)); From patchwork Mon Feb 17 18:28:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Abeni X-Patchwork-Id: 1239500 X-Patchwork-Delegate: pabeni@redhat.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.01.org (client-ip=198.145.21.10; helo=ml01.01.org; envelope-from=mptcp-bounces@lists.01.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=hs5eCZfz; dkim-atps=neutral Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48LsvK1gxcz9sRY for ; Tue, 18 Feb 2020 05:29:01 +1100 (AEDT) Received: from ml01.vlan13.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 21CC910FC3599; Mon, 17 Feb 2020 10:32:16 -0800 (PST) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=205.139.110.120; helo=us-smtp-1.mimecast.com; envelope-from=pabeni@redhat.com; receiver= Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [205.139.110.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id D4DA610FC3584 for ; Mon, 17 Feb 2020 10:32:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1581964130; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZOTbJ/TkDvRD+E6f5SvhYRvLuugr8SyQKWeWz1sd3EE=; b=hs5eCZfz1Oy+lTnA4VLhv9mjp+4bvnuChV/NWrxywu7sER2O+Vm39z1X7U/A1rpEp/4S02 LeWV2vKtPHy+KRrCDh5uqiw0V0DSl6muZ2ctS40abNvQNnvdMBC02AZs7oITDgdZBg7yKU P7eeJFGKiUJJsZ14ShO9rAIeXqSXJdg= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-134-Agd0Pj6gPpuNNHRJDwLK8Q-1; Mon, 17 Feb 2020 13:28:46 -0500 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 48475801E67 for ; Mon, 17 Feb 2020 18:28:45 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-116-153.ams2.redhat.com [10.36.116.153]) by smtp.corp.redhat.com (Postfix) with ESMTP id AA67A8B54B for ; Mon, 17 Feb 2020 18:28:44 +0000 (UTC) From: Paolo Abeni To: mptcp@lists.01.org Date: Mon, 17 Feb 2020 19:28:30 +0100 Message-Id: In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-MC-Unique: Agd0Pj6gPpuNNHRJDwLK8Q-1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Message-ID-Hash: EW3UUCC2N4247I3ZMBHMTGKUMAXNGVVQ X-Message-ID-Hash: EW3UUCC2N4247I3ZMBHMTGKUMAXNGVVQ X-MailFrom: pabeni@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.1.1 Precedence: list Subject: [MPTCP] [PATCH 4/7] Squash-to: "mptcp: Add handling of outgoing MP_JOIN requests" List-Id: Discussions regarding MPTCP upstreaming Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: record local id for outgoing MP_JOIN connection. join_id is the local address id, set as such in mptcp_syn_options() Signed-off-by: Paolo Abeni --- net/mptcp/options.c | 2 +- net/mptcp/protocol.h | 6 +++-- net/mptcp/subflow.c | 58 ++++++++++++++++++++++++++++++++------------ 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 29ccff13412e..2716cbc0953f 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -309,7 +309,7 @@ bool mptcp_syn_options(struct sock *sk, const struct sk_buff *skb, pr_debug("remote_token=%u, nonce=%u", subflow->remote_token, subflow->local_nonce); opts->suboptions = OPTION_MPTCP_MPJ_SYN; - opts->join_id = subflow->remote_id; + opts->join_id = subflow->local_id; opts->token = subflow->remote_token; opts->nonce = subflow->local_nonce; opts->backup = subflow->request_bkup; diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index a64a12a02d19..d16bc5a94f56 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -256,8 +256,10 @@ mptcp_subflow_get_mapped_dsn(const struct mptcp_subflow_context *subflow) int mptcp_is_enabled(struct net *net); bool mptcp_subflow_data_available(struct sock *sk); void mptcp_subflow_init(void); -int mptcp_subflow_connect(struct sock *sk, struct sockaddr *local, - struct sockaddr *remote, u8 remote_id); + +/* called with sk socket lock held */ +int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc, + const struct mptcp_addr_info *remote); int mptcp_subflow_create_socket(struct sock *sk, struct socket **new_sock); static inline void mptcp_subflow_tcp_fallback(struct sock *sk, diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 14c168df264d..6d45659f0802 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -24,19 +24,31 @@ static int subflow_rebuild_header(struct sock *sk) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); - int err = 0; + int local_id, err = 0; if (subflow->request_mptcp && !subflow->token) { pr_debug("subflow=%p", sk); err = mptcp_token_new_connect(sk); } else if (subflow->request_join && !subflow->local_nonce) { + struct mptcp_sock *msk = (struct mptcp_sock *)subflow->conn; + pr_debug("subflow=%p", sk); do { get_random_bytes(&subflow->local_nonce, sizeof(u32)); } while (!subflow->local_nonce); + + if (subflow->local_id) + goto out; + + local_id = mptcp_pm_get_local_id(msk, (struct sock_common *)sk); + if (local_id < 0) + return -EINVAL; + + subflow->local_id = local_id; } +out: if (err) return err; @@ -761,59 +773,73 @@ void mptcpv6_handle_mapped(struct sock *sk, bool mapped) } #endif -int mptcp_subflow_connect(struct sock *sk, struct sockaddr *local, - struct sockaddr *remote, u8 remote_id) +static void mptcp_info2sockaddr(const struct mptcp_addr_info *info, + struct sockaddr_storage *addr) +{ + memset(addr, 0, sizeof(*addr)); + addr->ss_family = info->family; + if (addr->ss_family == AF_INET) { + struct sockaddr_in *in_addr = (struct sockaddr_in *)addr; + + in_addr->sin_addr = info->addr; + in_addr->sin_port = info->port; + } else if (addr->ss_family == AF_INET6) { + struct sockaddr_in6 *in6_addr = (struct sockaddr_in6 *)addr; + + in6_addr->sin6_addr = info->addr6; + in6_addr->sin6_port = info->port; + } +} + +int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc, + const struct mptcp_addr_info *remote) { struct mptcp_sock *msk = mptcp_sk(sk); struct mptcp_subflow_context *subflow; + struct sockaddr_storage addr; struct socket *sf; u32 remote_token; int addrlen; int err; - lock_sock(sk); - if (sk->sk_state != TCP_ESTABLISHED) { - release_sock(sk); + if (sk->sk_state != TCP_ESTABLISHED) return -ENOTCONN; - } err = mptcp_subflow_create_socket(sk, &sf); - if (err) { - release_sock(sk); + if (err) return err; - } subflow = mptcp_subflow_ctx(sf->sk); subflow->remote_key = msk->remote_key; subflow->local_key = msk->local_key; subflow->token = msk->token; + mptcp_info2sockaddr(loc, &addr); addrlen = sizeof(struct sockaddr_in); #if IS_ENABLED(CONFIG_MPTCP_IPV6) - if (local->sa_family == AF_INET6) + if (loc->family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); #endif - err = kernel_bind(sf, local, addrlen); + err = kernel_bind(sf, (struct sockaddr *)&addr, addrlen); if (err) goto failed; mptcp_crypto_key_sha(subflow->remote_key, &remote_token, NULL); pr_debug("msk=%p remote_token=%u", msk, remote_token); subflow->remote_token = remote_token; - subflow->remote_id = remote_id; + subflow->local_id = loc->id; subflow->request_join = 1; subflow->request_bkup = 1; + mptcp_info2sockaddr(remote, &addr); - err = kernel_connect(sf, remote, addrlen, O_NONBLOCK); + err = kernel_connect(sf, (struct sockaddr *)&addr, addrlen, O_NONBLOCK); if (err && err != -EINPROGRESS) goto failed; - release_sock(sk); return err; failed: list_del_init(&subflow->node); - release_sock(sk); sock_release(sf); return err; } From patchwork Mon Feb 17 18:28:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Abeni X-Patchwork-Id: 1239498 X-Patchwork-Delegate: pabeni@redhat.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.01.org (client-ip=2001:19d0:306:5::1; helo=ml01.01.org; envelope-from=mptcp-bounces@lists.01.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=XEFw0O/f; dkim-atps=neutral Received: from ml01.01.org (ml01.01.org [IPv6:2001:19d0:306:5::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48Lsv959Lnz9s29 for ; Tue, 18 Feb 2020 05:28:53 +1100 (AEDT) Received: from ml01.vlan13.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 0A2EE10FC358C; Mon, 17 Feb 2020 10:32:09 -0800 (PST) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=205.139.110.61; helo=us-smtp-delivery-1.mimecast.com; envelope-from=pabeni@redhat.com; receiver= Received: from us-smtp-delivery-1.mimecast.com (us-smtp-2.mimecast.com [205.139.110.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id B2E2210FC3581 for ; Mon, 17 Feb 2020 10:32:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1581964128; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=on36tQwHiC8AhbWhHGWpqde4Rog/LjvqHtkUkqcchWI=; b=XEFw0O/fjd6AkdZTjG2jUlpyHxYiAMqTUMFoMdAmPy7J+DM2OPywtNNmH4G02JQG33ZpFs /KW8V6YSaL7+G6TTMM/gM+PMh9fsRlzY8XY8xnRNiV1bGZqT3R3y9COrueRG6NFDV/vRpr j4FyFGPpxKq7dUpnIt5EbMoXME9m2WY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-208-cd9ph6O_Mi6HlfUt0HkKaA-1; Mon, 17 Feb 2020 13:28:47 -0500 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 3ED9018B5FA4 for ; Mon, 17 Feb 2020 18:28:46 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-116-153.ams2.redhat.com [10.36.116.153]) by smtp.corp.redhat.com (Postfix) with ESMTP id A2AC485735 for ; Mon, 17 Feb 2020 18:28:45 +0000 (UTC) From: Paolo Abeni To: mptcp@lists.01.org Date: Mon, 17 Feb 2020 19:28:31 +0100 Message-Id: In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-MC-Unique: cd9ph6O_Mi6HlfUt0HkKaA-1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Message-ID-Hash: HYNIJK6325D72CVQJSYUG46OMOE34JX7 X-Message-ID-Hash: HYNIJK6325D72CVQJSYUG46OMOE34JX7 X-MailFrom: pabeni@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.1.1 Precedence: list Subject: [MPTCP] [PATCH 5/7] Squash-to: "mptcp: Implement path manager interface commands" List-Id: Discussions regarding MPTCP upstreaming Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: Implement stubs for PM events delegating the action to the work queue. This allows acquiring whatever lock is needed to perform the actual implementation. Try to avoid scheduling the worker if no action is needed/possible. I relies on the accounting info included into the PM struct and on great deal of double-checked locking [anti-]pattern. RFC -> v1: - simplify/cleanup mptcp_pm_work_pending() - Mat - likewise simplify/cleanup mptcp_pm_add_addr() Signed-off-by: Paolo Abeni --- net/mptcp/pm.c | 195 ++++++++++++++++++++++--------------------------- 1 file changed, 89 insertions(+), 106 deletions(-) diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index fb66758e3f61..033393176096 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -15,117 +15,17 @@ static struct workqueue_struct *pm_wq; int mptcp_pm_announce_addr(struct mptcp_sock *msk, const struct mptcp_addr_info *addr) { - struct mptcp_sock *msk = mptcp_token_get_sock(token); - int err = 0; + pr_debug("msk=%p, local_id=%d", msk, addr->id); - if (!msk) - return -EINVAL; - - if (msk->pm.local_valid) { - err = -EBADR; - goto announce_put; - } - - pr_debug("msk=%p, local_id=%d", msk, local_id); - msk->pm.local_valid = 1; - msk->pm.local_id = local_id; - msk->pm.local_family = AF_INET; - msk->pm.local_addr = *addr; - msk->addr_signal = 1; - -announce_put: - sock_put((struct sock *)msk); - return err; -} - -int mptcp_pm_remove_addr(struct mptcp_sock *msk, u8 local_id) -{ - struct mptcp_sock *msk = mptcp_token_get_sock(token); - - if (!msk) - return -EINVAL; - - pr_debug("msk=%p", msk); - msk->pm.local_valid = 0; - - sock_put((struct sock *)msk); + msk->pm.local = *addr; + WRITE_ONCE(msk->pm.addr_signal, true); return 0; } -int mptcp_pm_create_subflow(u32 token, u8 remote_id, struct in6_addr *addr) -{ - struct mptcp_sock *msk = mptcp_token_get_sock(token); - struct sockaddr_in6 remote; - struct sockaddr_in6 local; - struct sock *sk; - int err; - - pr_debug("msk=%p", msk); - - sk = (struct sock *)msk; - if (!msk->pm.remote_valid || remote_id != msk->pm.remote_id) { - err = -EBADR; - goto create_put; - } - - local.sin_family = AF_INET; - local.sin_port = 0; - if (addr) - local.sin_addr = *addr; - else - local.sin_addr.s_addr = htonl(INADDR_ANY); - - remote.sin_family = msk->pm.remote_family; - remote.sin_port = inet_sk(sk)->inet_dport; - remote.sin_addr = msk->pm.remote_addr; - - err = mptcp_subflow_connect(sk, (struct sockaddr *)&local, - (struct sockaddr *)&remote, remote_id); - -create_put: - sock_put(sk); - return err; -} - -#if IS_ENABLED(CONFIG_MPTCP_IPV6) -int mptcp_pm_create_subflow6(u32 token, u8 remote_id, struct in6_addr *addr) +int mptcp_pm_remove_addr(struct mptcp_sock *msk, u8 local_id) { - struct mptcp_sock *msk = mptcp_token_get_sock(token); - struct sockaddr_in6 remote; - struct sockaddr_in6 local; - struct sock *sk; - int err; - - if (!msk) - return -EINVAL; - - pr_debug("msk=%p", msk); - sk = (struct sock *)msk; - - if (!msk->pm.remote_valid || remote_id != msk->pm.remote_id) { - err = -EBADR; - goto create_put; - } - - local.sin6_family = AF_INET6; - local.sin6_port = 0; - if (addr) - local.sin6_addr = *addr; - else - local.sin6_addr = in6addr_any; - - remote.sin6_family = msk->pm.remote_family; - remote.sin6_port = inet_sk(sk)->inet_dport; - remote.sin6_addr = msk->pm.remote_addr6; - - err = mptcp_subflow_connect(sk, (struct sockaddr *)&local, - (struct sockaddr *)&remote, remote_id); - -create_put: - sock_put(sk); - return err; + return -ENOTSUPP; } -#endif int mptcp_pm_remove_subflow(struct mptcp_sock *msk, u8 remote_id) { @@ -143,11 +43,36 @@ void mptcp_pm_new_connection(struct mptcp_sock *msk, int server_side) WRITE_ONCE(pm->server_side, server_side); } +static bool mptcp_pm_schedule_work(struct mptcp_sock *msk, + enum mptcp_pm_status new_status) +{ + if (msk->pm.status != MPTCP_PM_IDLE) + return false; + + if (queue_work(pm_wq, &msk->pm.work)) { + msk->pm.status = new_status; + sock_hold((struct sock *)msk); + return true; + } + return false; +} + void mptcp_pm_fully_established(struct mptcp_sock *msk) { struct mptcp_pm_data *pm = &msk->pm; pr_debug("msk=%p", msk); + + /* try to avoid acquiring the lock below */ + if (READ_ONCE(pm->fully_established)) + return; + + spin_lock_bh(&pm->lock); + if (!READ_ONCE(pm->fully_established) && + mptcp_pm_schedule_work(msk, MPTCP_PM_ESTABLISHED)) + WRITE_ONCE(pm->fully_established, true); + + spin_unlock_bh(&pm->lock); } void mptcp_pm_connection_closed(struct mptcp_sock *msk) @@ -158,7 +83,19 @@ void mptcp_pm_connection_closed(struct mptcp_sock *msk) void mptcp_pm_subflow_established(struct mptcp_sock *msk, struct mptcp_subflow_context *subflow) { + struct mptcp_pm_data *pm = &msk->pm; + pr_debug("msk=%p", msk); + + if (!READ_ONCE(pm->work_pending)) + return; + + spin_lock_bh(&pm->lock); + + if (READ_ONCE(pm->work_pending)) + mptcp_pm_schedule_work(msk, MPTCP_PM_SUBFLOW_ESTABLISHED); + + spin_unlock_bh(&pm->lock); } void mptcp_pm_subflow_closed(struct mptcp_sock *msk, u8 id) @@ -172,6 +109,21 @@ void mptcp_pm_add_addr(struct mptcp_sock *msk, struct mptcp_pm_data *pm = &msk->pm; pr_debug("msk=%p, remote_id=%d", msk, addr->id); + + /* avoid acquiring the lock if there is no room for fouther addresses */ + if (READ_ONCE(pm->accept_addr)) + return; + + spin_lock_bh(&pm->lock); + + /* be sure there is something to signal re-checking under PM lock */ + if (READ_ONCE(pm->accept_addr) && + mptcp_pm_schedule_work(msk, MPTCP_PM_ADD_ADDR)) { + pm->add_addr_accepted++; + pm->remote = *addr; + } + + spin_unlock_bh(&pm->lock); } /* path manager helpers */ @@ -179,7 +131,27 @@ void mptcp_pm_add_addr(struct mptcp_sock *msk, int mptcp_pm_addr_signal(struct mptcp_sock *msk, unsigned int remaining, struct mptcp_addr_info *saddr) { - return 0; + struct mptcp_addr_info addr; + int ret = -EINVAL; + + spin_lock_bh(&msk->pm.lock); + + /* double check after the lock is acquired */ + if (!mptcp_pm_should_signal(msk)) + goto out_unlock; + + /* load real data */ + memset(&addr, 0, sizeof(addr)); + + if (remaining < mptcp_add_addr_len(saddr->family)) + goto out_unlock; + + WRITE_ONCE(msk->pm.addr_signal, false); + ret = 0; + +out_unlock: + spin_unlock_bh(&msk->pm.lock); + return ret; } int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc) @@ -189,6 +161,17 @@ int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc) static void pm_worker(struct work_struct *work) { + struct mptcp_pm_data *pm = container_of(work, struct mptcp_pm_data, + work); + struct mptcp_sock *msk = container_of(pm, struct mptcp_sock, pm); + struct sock *sk = (struct sock *)msk; + + switch (pm->status) { + default: + break; + } + + sock_put(sk); } void mptcp_pm_data_init(struct mptcp_sock *msk) From patchwork Mon Feb 17 18:28:32 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Abeni X-Patchwork-Id: 1239502 X-Patchwork-Delegate: pabeni@redhat.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.01.org (client-ip=198.145.21.10; helo=ml01.01.org; envelope-from=mptcp-bounces@lists.01.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=eJTRORTT; dkim-atps=neutral Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48LsvK27k8z9sRt for ; Tue, 18 Feb 2020 05:29:01 +1100 (AEDT) Received: from ml01.vlan13.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 38E0F10FC35A7; Mon, 17 Feb 2020 10:32:16 -0800 (PST) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=207.211.31.120; helo=us-smtp-1.mimecast.com; envelope-from=pabeni@redhat.com; receiver= Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [207.211.31.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 2138B10FC3419 for ; Mon, 17 Feb 2020 10:32:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1581964134; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=FeM10k71gVXShCpotY08/2ZwT7qHs+KmcX+gBw39X9M=; b=eJTRORTTksUTTLJW0JS2jdWL36gh//DWWGqIofjqW5Ed0wgUI0fcrFvFYkjRXiqoXa9YMc LPEDqx/zya9VWjb171ws7uaAFW4fIqpf6o0qunpNpdazrImzc7x10COZjFrf5hLftssLXv 3X2r0khpR/us7pMt3yfYuNkP0FSO59Q= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-317-ET_6F3F0N7WhqwWpCxmIbg-1; Mon, 17 Feb 2020 13:28:48 -0500 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 694E08010F9 for ; Mon, 17 Feb 2020 18:28:47 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-116-153.ams2.redhat.com [10.36.116.153]) by smtp.corp.redhat.com (Postfix) with ESMTP id 98F6485735 for ; Mon, 17 Feb 2020 18:28:46 +0000 (UTC) From: Paolo Abeni To: mptcp@lists.01.org Date: Mon, 17 Feb 2020 19:28:32 +0100 Message-Id: In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-MC-Unique: ET_6F3F0N7WhqwWpCxmIbg-1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Message-ID-Hash: H3XF4CB74LIJXX4DAPJTNYP6XHVSZLXX X-Message-ID-Hash: H3XF4CB74LIJXX4DAPJTNYP6XHVSZLXX X-MailFrom: pabeni@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.1.1 Precedence: list Subject: [MPTCP] [PATCH 6/7] mptcp: add netlink based PM List-Id: Discussions regarding MPTCP upstreaming Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: Expose to U/S a netlink family to control the PM, setting: - list of local addresses to be signaled. - list of local addresses used to created subflows. - maximum number of add_addr option to react When the msk is fully established, the PM netlink attempts to create subflow for each addr in 'local' list, waiting for each connection to be completed before attempting the next one. After exausting the 'local' list, the PM tries to announce the 'signal' list via the ADD_ADDR option. Since we currenlty lack the ADD_ADDR echo (and related event) only the first addr is sent. Idea is to add an additional PM hook for ADD_ADDR echo, to allow the PM netlink announcing multiple addresses, in sequence. RFC -> v1: - simplified NL API - reduced {WRITE,READ}_ONCE boilerplate due to PM changes - add check for duplicate addresses Signed-off-by: Paolo Abeni --- include/uapi/linux/mptcp.h | 51 +++ net/mptcp/Makefile | 3 +- net/mptcp/pm.c | 18 +- net/mptcp/pm_netlink.c | 799 +++++++++++++++++++++++++++++++++++++ net/mptcp/protocol.h | 7 + 5 files changed, 876 insertions(+), 2 deletions(-) create mode 100644 net/mptcp/pm_netlink.c diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h index 3912a9808fa2..e560df80d45d 100644 --- a/include/uapi/linux/mptcp.h +++ b/include/uapi/linux/mptcp.h @@ -31,4 +31,55 @@ enum { }; #define MPTCP_SUBFLOW_MAX (__MPTCP_SUBFLOW_MAX - 1) + +/* netlink interface */ +#define MPTCP_PM_NAME "mptcp_pm" +#define MPTCP_PM_CMD_GRP_NAME "mptcp_pm_cmds" +#define MPTCP_PM_VER 0x1 + +/* + * ATTR types defined for MPTCP + */ +enum mptcp_pm_attrs { + MPTCP_PM_ATTR_UNSPEC, + + MPTCP_PM_ATTR_ADDR, /* nested address */ + MPTCP_PM_ATTR_RCV_ADD_ADDRS, /* u32 */ + + __MPTCP_PM_ATTR_MAX +}; + +#define MPTCP_PM_ATTR_MAX (__MPTCP_PM_ATTR_MAX - 1) + +enum mptcp_pm_addr_addrs { + MPTCP_PM_ADDR_ATTR_UNSPEC, + + MPTCP_PM_ADDR_ATTR_FAMILY, /* u16 */ + MPTCP_PM_ADDR_ATTR_ID, /* u8 */ + MPTCP_PM_ADDR_ATTR_ADDR4, /* struct in_addr */ + MPTCP_PM_ADDR_ATTR_ADDR6, /* struct in6_addr */ + MPTCP_PM_ADDR_ATTR_PORT, + MPTCP_PM_ADDR_ATTR_FLAGS, /* u32 */ + + __MPTCP_PM_ADDR_ATTR_MAX +}; + +#define MPTCP_PM_ADDR_ATTR_MAX (__MPTCP_PM_ADDR_ATTR_MAX - 1) + +#define MPTCP_PM_ADDR_FLAG_SIGNAL (1 << 0) +#define MPTCP_PM_ADDR_FLAG_SUBFLOW (1 << 1) + +enum { + MPTCP_CMD_UNSPEC, + + MPTCP_CMD_ADD_ADDR, + MPTCP_CMD_DEL_ADDR, + MPTCP_CMD_GET_ADDR, + MPTCP_CMD_FLUSH_ADDRS, + MPTCP_CMD_SET_RCV_ADD_ADDRS, + MPTCP_CMD_GET_RCV_ADD_ADDRS, + + __MPTCP_CMD_AFTER_LAST +}; + #endif /* _UAPI_MPTCP_H */ diff --git a/net/mptcp/Makefile b/net/mptcp/Makefile index faebe8ec9f73..baa0640527c7 100644 --- a/net/mptcp/Makefile +++ b/net/mptcp/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_MPTCP) += mptcp.o -mptcp-y := protocol.o subflow.o options.o token.o crypto.o ctrl.o pm.o diag.o mib.o +mptcp-y := protocol.o subflow.o options.o token.o crypto.o ctrl.o pm.o diag.o \ + mib.o pm_netlink.o diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index 033393176096..859a88559327 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -156,7 +156,7 @@ int mptcp_pm_addr_signal(struct mptcp_sock *msk, unsigned int remaining, int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc) { - return 0; + return mptcp_pm_nl_get_local_id(msk, skc); } static void pm_worker(struct work_struct *work) @@ -167,6 +167,18 @@ static void pm_worker(struct work_struct *work) struct sock *sk = (struct sock *)msk; switch (pm->status) { + case MPTCP_PM_ADD_ADDR: + mptcp_pm_nl_add_addr(msk); + break; + + case MPTCP_PM_ESTABLISHED: + mptcp_pm_nl_fully_established(msk); + break; + + case MPTCP_PM_SUBFLOW_ESTABLISHED: + mptcp_pm_nl_subflow_established(msk); + break; + default: break; } @@ -187,6 +199,8 @@ void mptcp_pm_data_init(struct mptcp_sock *msk) spin_lock_init(&msk->pm.lock); INIT_WORK(&msk->pm.work, pm_worker); + + mptcp_pm_nl_data_init(msk); } void mptcp_pm_init(void) @@ -194,4 +208,6 @@ void mptcp_pm_init(void) pm_wq = alloc_workqueue("pm_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 8); if (!pm_wq) panic("Failed to allocate workqueue"); + + mptcp_pm_nl_init(); } diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c new file mode 100644 index 000000000000..eb8c4d7ffdb0 --- /dev/null +++ b/net/mptcp/pm_netlink.c @@ -0,0 +1,799 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Multipath TCP + * + * Copyright (c) 2020, Red Hat, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "protocol.h" + +/* forward declaration */ +static struct genl_family mptcp_genl_family; + +static int pm_nl_pernet_id; + +struct mptcp_pm_addr_entry { + struct list_head list; + unsigned int flags; + struct mptcp_addr_info addr; + struct rcu_head rcu; +}; + +struct pm_nl_pernet { + /* protects pernet updates */ + spinlock_t lock; + struct list_head addr_list; + unsigned int addrs; + unsigned int add_addr_signal_max; + unsigned int add_addr_accept_max; + unsigned int local_addr_max; + unsigned int next_id; +}; + +#define MPTCP_PM_ADDR_MAX 8 + +static bool addresses_equal(const struct mptcp_addr_info *a, + struct mptcp_addr_info *b, bool use_port) +{ + bool addr_equals; + + if (a->family != b->family) + return false; + + if (a->family == AF_INET) + addr_equals = !memcmp(&a->addr, &b->addr, sizeof(b->addr)); + else + addr_equals = !memcmp(&a->addr6, &b->addr6, sizeof(b->addr6)); + + if (!addr_equals) + return false; + if (!use_port) + return true; + + return a->port == b->port; +} + +static void local_address(const struct sock_common *skc, + struct mptcp_addr_info *addr) +{ + addr->family = skc->skc_family; + if (addr->family == AF_INET) + addr->addr.s_addr = skc->skc_rcv_saddr; +#if IS_ENABLED(CONFIG_IPV6) + else if (addr->family == AF_INET6) + addr->addr6 = skc->skc_v6_rcv_saddr; +#endif +} + +static void remote_address(const struct sock_common *skc, + struct mptcp_addr_info *addr) +{ + addr->family = skc->skc_family; + addr->port = skc->skc_dport; + if (addr->family == AF_INET) + addr->addr.s_addr = skc->skc_daddr; +#if IS_ENABLED(CONFIG_IPV6) + else if (addr->family == AF_INET6) + addr->addr6 = skc->skc_v6_daddr; +#endif +} + +static bool lookup_subflow_by_saddr(const struct mptcp_sock *msk, + struct mptcp_addr_info *saddr) +{ + struct mptcp_subflow_context *subflow; + struct mptcp_addr_info cur; + struct sock_common *skc; + + list_for_each_entry(subflow, &msk->conn_list, node) { + skc = (struct sock_common *)mptcp_subflow_tcp_sock(subflow); + + local_address(skc, &cur); + if (addresses_equal(&cur, saddr, false)) + return true; + } + + return false; +} + +static struct mptcp_pm_addr_entry * +pick_local_address(const struct pm_nl_pernet *pernet, + const struct mptcp_sock *msk) +{ + struct mptcp_pm_addr_entry *entry, *ret; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &pernet->addr_list, list) { + if (!(entry->flags & MPTCP_PM_ADDR_FLAG_SUBFLOW)) + continue; + if (entry->addr.family == ((struct sock *)msk)->sk_family && + !lookup_subflow_by_saddr(msk, &entry->addr)) { + ret = entry; + break; + } + } + rcu_read_unlock(); + return ret; +} + +static struct mptcp_pm_addr_entry * +pick_signal_address(struct pm_nl_pernet *pernet, unsigned int pos) +{ + struct mptcp_pm_addr_entry *entry, *ret = NULL; + int i = 0; + + rcu_read_lock(); + /* do not keep any additional per socket state, just signal + * the address list in order. + * Note: removal from the local address list during the msk life-cycle + * can lead to additional addresses not being announced. + */ + list_for_each_entry_rcu(entry, &pernet->addr_list, list) { + if (!(entry->flags & MPTCP_PM_ADDR_FLAG_SIGNAL)) + continue; + if (++i == pos) { + ret = entry; + break; + } + } + rcu_read_unlock(); + return ret; +} + +static void check_work_pending(struct mptcp_sock *msk) +{ + if (msk->pm.local_addr_used == msk->pm.add_addr_signal_max && + msk->pm.local_addr_used == msk->pm.local_addr_max) + WRITE_ONCE(msk->pm.work_pending, false); +} + +static void mptcp_pm_create_subflow_or_signal(struct mptcp_sock *msk) +{ + struct sock *sk = (struct sock *)msk; + struct mptcp_pm_addr_entry *local; + struct mptcp_addr_info remote; + struct pm_nl_pernet *pernet; + + pernet = net_generic(sock_net((struct sock *)msk), pm_nl_pernet_id); + + lock_sock(sk); + + spin_lock_bh(&msk->pm.lock); + + /* check first if should create a new subflow */ + if (msk->pm.local_addr_used < msk->pm.local_addr_max) { + remote_address((struct sock_common *)sk, &remote); + + local = pick_local_address(pernet, msk); + if (local) { + msk->pm.local_addr_used++; + check_work_pending(msk); + spin_unlock_bh(&msk->pm.lock); + __mptcp_subflow_connect(sk, &local->addr, &remote); + release_sock(sk); + return; + } + + /* lookup failed, avoid fourther attempts later */ + msk->pm.local_addr_used = msk->pm.local_addr_max; + check_work_pending(msk); + } + + /* check for announce */ + if (msk->pm.add_addr_signaled < msk->pm.add_addr_signal_max) { + local = pick_signal_address(pernet, msk->pm.add_addr_signaled); + + if (local) { + msk->pm.local_addr_used++; + mptcp_pm_announce_addr(msk, &local->addr); + } else { + /* pick failed, avoid fourther attempts later */ + msk->pm.local_addr_used = msk->pm.add_addr_signal_max; + } + + check_work_pending(msk); + } + spin_unlock_bh(&msk->pm.lock); + release_sock(sk); +} + +void mptcp_pm_nl_fully_established(struct mptcp_sock *msk) +{ + mptcp_pm_create_subflow_or_signal(msk); +} + +void mptcp_pm_nl_subflow_established(struct mptcp_sock *msk) +{ + mptcp_pm_create_subflow_or_signal(msk); +} + +void mptcp_pm_nl_add_addr(struct mptcp_sock *msk) +{ + struct sock *sk = (struct sock *)msk; + struct mptcp_addr_info remote; + struct mptcp_addr_info local; + struct pm_nl_pernet *pernet; + + pernet = net_generic(sock_net((struct sock *)msk), pm_nl_pernet_id); + + spin_lock_bh(&msk->pm.lock); + if (msk->pm.add_addr_accepted >= msk->pm.add_addr_accept_max) { + spin_unlock_bh(&msk->pm.lock); + return; + } + + /* connect to the specified remote address, using whatever + * local address the routing configuration will pick. + */ + remote = msk->pm.remote; + memset(&local, 0, sizeof(local)); + local.family = remote.family; + if (++msk->pm.add_addr_accepted == msk->pm.add_addr_accept_max) + WRITE_ONCE(msk->pm.accept_addr, false); + spin_unlock_bh(&msk->pm.lock); + + lock_sock(sk); + __mptcp_subflow_connect((struct sock *)msk, &local, &remote); + release_sock(sk); +} + +static int mptcp_pm_nl_append_new_addr(struct pm_nl_pernet *pernet, + struct mptcp_pm_addr_entry *entry) +{ + struct mptcp_pm_addr_entry *cur; + int ret = -EINVAL; + + spin_lock_bh(&pernet->lock); + /* to keep the code simple, don't do IDR-like allocation for address ID, + * just bail when we exceed limits + */ + if (pernet->next_id > 255) + goto out; + if (pernet->addrs >= MPTCP_PM_ADDR_MAX) + goto out; + + /* do not insert duplicate address, differentiate on port only + * singled addresses + */ + list_for_each_entry(cur, &pernet->addr_list, list) { + if (addresses_equal(&cur->addr, &entry->addr, + entry->flags == MPTCP_PM_ADDR_FLAG_SIGNAL && + cur->flags == MPTCP_PM_ADDR_FLAG_SIGNAL)) + goto out; + } + + if (entry->flags & MPTCP_PM_ADDR_FLAG_SIGNAL) + pernet->add_addr_signal_max++; + if (entry->flags & MPTCP_PM_ADDR_FLAG_SUBFLOW) + pernet->local_addr_max++; + + entry->addr.id = pernet->next_id++; + pernet->addrs++; + list_add_tail_rcu(&entry->list, &pernet->addr_list); + ret = entry->addr.id; + +out: + spin_unlock_bh(&pernet->lock); + return ret; +} + +int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct sock_common *skc) +{ + struct mptcp_pm_addr_entry *entry; + struct mptcp_addr_info skc_local; + struct mptcp_addr_info msk_local; + struct pm_nl_pernet *pernet; + int ret = -1; + + if (WARN_ON_ONCE(!msk)) + return -1; + + /* The 0 ID mapping is defined by the first subflow, copied into the msk + * addr + */ + local_address((struct sock_common *)msk, &msk_local); + local_address((struct sock_common *)msk, &skc_local); + if (addresses_equal(&msk_local, &skc_local, false)) + return 0; + + pernet = net_generic(sock_net((struct sock *)msk), pm_nl_pernet_id); + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &pernet->addr_list, list) { + if (addresses_equal(&entry->addr, &skc_local, false)) { + 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_KERNEL); + if (!entry) + return -ENOMEM; + + entry->flags = 0; + entry->addr = skc_local; + ret = mptcp_pm_nl_append_new_addr(pernet, entry); + if (ret < 0) + kfree(entry); + + return ret; +} + +void mptcp_pm_nl_data_init(struct mptcp_sock *msk) +{ + struct pm_nl_pernet *pernet; + + pernet = net_generic(sock_net((struct sock *)msk), pm_nl_pernet_id); + + msk->pm.add_addr_signal_max = READ_ONCE(pernet->add_addr_signal_max); + msk->pm.add_addr_accept_max = READ_ONCE(pernet->add_addr_accept_max); + msk->pm.local_addr_max = READ_ONCE(pernet->local_addr_max); +} + +#define MPTCP_PM_CMD_GRP_OFFSET 0 + +static const struct genl_multicast_group mptcp_pm_mcgrps[] = { + [MPTCP_PM_CMD_GRP_OFFSET] = { .name = MPTCP_PM_CMD_GRP_NAME, }, +}; + +static const struct nla_policy +mptcp_pm_addr_policy[MPTCP_PM_ADDR_ATTR_MAX + 1] = { + [MPTCP_PM_ADDR_ATTR_FAMILY] = { .type = NLA_U16, }, + [MPTCP_PM_ADDR_ATTR_ID] = { .type = NLA_U8, }, + [MPTCP_PM_ADDR_ATTR_ADDR4] = { .type = NLA_U32, }, + [MPTCP_PM_ADDR_ATTR_ADDR6] = { .type = NLA_EXACT_LEN, + .len = sizeof(struct in6_addr), }, + [MPTCP_PM_ADDR_ATTR_PORT] = { .type = NLA_U16 }, + [MPTCP_PM_ADDR_ATTR_FLAGS] = { .type = NLA_U32 }, +}; + +static const struct nla_policy mptcp_pm_policy[MPTCP_PM_ATTR_MAX + 1] = { + [MPTCP_PM_ATTR_ADDR] = + NLA_POLICY_NESTED(mptcp_pm_addr_policy), + [MPTCP_PM_ATTR_RCV_ADD_ADDRS] = { .type = NLA_U32, }, +}; + +static int mptcp_pm_family_to_addr(int family) +{ +#if IS_ENABLED(CONFIG_MPTCP_IPV6) + if (family == AF_INET6) + return MPTCP_PM_ADDR_ATTR_ADDR6; +#endif + 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) +{ + struct nlattr *tb[MPTCP_PM_ADDR_ATTR_MAX + 1]; + int err, addr_addr; + + if (!attr) { + NL_SET_ERR_MSG(info->extack, "missing address info"); + return -EINVAL; + } + + /* no validation needed - was already done via nested policy */ + err = nla_parse_nested_deprecated(tb, MPTCP_PM_ADDR_ATTR_MAX, attr, + mptcp_pm_addr_policy, info->extack); + if (err) + return err; + + memset(entry, 0, sizeof(*entry)); + if (!tb[MPTCP_PM_ADDR_ATTR_FAMILY]) { + if (!require_family) + goto skip_family; + + 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 +#if IS_ENABLED(CONFIG_MPTCP_IPV6) + && entry->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); + if (!tb[addr_addr]) { + NL_SET_ERR_MSG_ATTR(info->extack, attr, + "missing address data"); + return -EINVAL; + } + +#if IS_ENABLED(CONFIG_MPTCP_IPV6) + if (entry->addr.family == AF_INET6) + entry->addr.addr6 = nla_get_in6_addr(tb[addr_addr]); + else +#endif + entry->addr.addr.s_addr = nla_get_in_addr(tb[addr_addr]); + +skip_family: + if (tb[MPTCP_PM_ADDR_ATTR_ID]) + entry->addr.id = nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_ID]); + else + entry->addr.id = 0; + + if (tb[MPTCP_PM_ADDR_ATTR_FLAGS]) + entry->flags = nla_get_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]); + return 0; +} + +static struct pm_nl_pernet *genl_info_pm_nl(struct genl_info *info) +{ + return net_generic(genl_info_net(info), pm_nl_pernet_id); +} + +static int mptcp_nl_cmd_add_addr(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *attr = info->attrs[MPTCP_PM_ATTR_ADDR]; + struct pm_nl_pernet *pernet = genl_info_pm_nl(info); + struct mptcp_pm_addr_entry addr, *entry; + int ret; + + ret = mptcp_pm_parse_addr(attr, info, true, &addr); + if (ret) + return ret; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + NL_SET_ERR_MSG(info->extack, "can't allocate addr"); + return -ENOMEM; + } + + *entry = addr; + ret = mptcp_pm_nl_append_new_addr(pernet, entry); + if (ret < 0) { + NL_SET_ERR_MSG(info->extack, "too many addresses"); + kfree(entry); + return ret; + } + + return 0; +} + +static struct mptcp_pm_addr_entry * +__lookup_addr_by_id(struct pm_nl_pernet *pernet, unsigned int id) +{ + struct mptcp_pm_addr_entry *entry; + + list_for_each_entry(entry, &pernet->addr_list, list) { + if (entry->addr.id == id) + return entry; + } + return NULL; +} + +static int mptcp_nl_cmd_del_addr(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *attr = info->attrs[MPTCP_PM_ATTR_ADDR]; + struct pm_nl_pernet *pernet = genl_info_pm_nl(info); + struct mptcp_pm_addr_entry addr, *entry; + int ret; + + ret = mptcp_pm_parse_addr(attr, info, false, &addr); + if (ret) + return ret; + + spin_lock_bh(&pernet->lock); + entry = __lookup_addr_by_id(pernet, addr.addr.id); + if (!entry) { + NL_SET_ERR_MSG(info->extack, "address not found"); + ret = -EINVAL; + goto out; + } + if (entry->flags & MPTCP_PM_ADDR_FLAG_SIGNAL) + pernet->add_addr_signal_max--; + if (entry->flags & MPTCP_PM_ADDR_FLAG_SUBFLOW) + pernet->local_addr_max--; + + pernet->addrs--; + list_del_rcu(&entry->list); + kfree_rcu(entry, rcu); +out: + spin_unlock_bh(&pernet->lock); + return ret; +} + +static void __flush_addrs(struct pm_nl_pernet *pernet) +{ + while (!list_empty(&pernet->addr_list)) { + struct mptcp_pm_addr_entry *cur; + + cur = list_entry(pernet->addr_list.next, + struct mptcp_pm_addr_entry, list); + list_del_rcu(&cur->list); + kfree_rcu(cur, rcu); + } +} + +static void __reset_counters(struct pm_nl_pernet *pernet) +{ + pernet->add_addr_signal_max = 0; + pernet->add_addr_accept_max = 0; + pernet->local_addr_max = 0; + pernet->addrs = 0; +} + +static int mptcp_nl_cmd_flush_addrs(struct sk_buff *skb, struct genl_info *info) +{ + struct pm_nl_pernet *pernet = genl_info_pm_nl(info); + + spin_lock_bh(&pernet->lock); + __flush_addrs(pernet); + __reset_counters(pernet); + spin_unlock_bh(&pernet->lock); + return 0; +} + +static int mptcp_nl_fill_addr(struct sk_buff *skb, + struct mptcp_pm_addr_entry *entry) +{ + struct mptcp_addr_info *addr = &entry->addr; + struct nlattr *attr; + + attr = nla_nest_start(skb, MPTCP_PM_ATTR_ADDR); + if (!attr) + return -EMSGSIZE; + + if (nla_put_u16(skb, MPTCP_PM_ADDR_ATTR_FAMILY, addr->family)) + goto nla_put_failure; + if (nla_put_u8(skb, MPTCP_PM_ADDR_ATTR_ID, addr->id)) + goto nla_put_failure; + if (nla_put_u32(skb, MPTCP_PM_ADDR_ATTR_FLAGS, entry->flags)) + goto nla_put_failure; + + if (addr->family == AF_INET) + nla_put_in_addr(skb, MPTCP_PM_ADDR_ATTR_ADDR4, + addr->addr.s_addr); +#if IS_ENABLED(CONFIG_IPV6) + else if (addr->family == AF_INET6) + nla_put_in6_addr(skb, MPTCP_PM_ADDR_ATTR_ADDR6, &addr->addr6); +#endif + nla_nest_end(skb, attr); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, attr); + return -EMSGSIZE; +} + +static int mptcp_nl_cmd_get_addr(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *attr = info->attrs[MPTCP_PM_ATTR_ADDR]; + struct pm_nl_pernet *pernet = genl_info_pm_nl(info); + struct mptcp_pm_addr_entry addr, *entry; + struct sk_buff *msg; + void *reply; + int ret; + + ret = mptcp_pm_parse_addr(attr, info, false, &addr); + if (ret) + return ret; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + reply = genlmsg_put_reply(msg, info, &mptcp_genl_family, 0, + MPTCP_CMD_ADD_ADDR); + if (!reply) { + NL_SET_ERR_MSG(info->extack, "not enough space in Netlink message"); + ret = -EMSGSIZE; + goto fail; + } + + spin_lock_bh(&pernet->lock); + entry = __lookup_addr_by_id(pernet, addr.addr.id); + if (!entry) { + NL_SET_ERR_MSG(info->extack, "address not found"); + ret = -EINVAL; + goto unlock_fail; + } + + ret = mptcp_nl_fill_addr(msg, entry); + if (ret) + goto unlock_fail; + + genlmsg_end(msg, reply); + ret = genlmsg_reply(msg, info); + spin_unlock_bh(&pernet->lock); + return ret; + +unlock_fail: + spin_unlock_bh(&pernet->lock); + +fail: + nlmsg_free(msg); + return ret; +} + +static int mptcp_nl_cmd_dump_addrs(struct sk_buff *msg, + struct netlink_callback *cb) +{ + struct net *net = sock_net(msg->sk); + struct mptcp_pm_addr_entry *entry; + struct pm_nl_pernet *pernet; + int id = cb->args[0]; + void *hdr; + + pernet = net_generic(net, pm_nl_pernet_id); + + spin_lock_bh(&pernet->lock); + list_for_each_entry(entry, &pernet->addr_list, list) { + if (entry->addr.id <= id) + continue; + + hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, &mptcp_genl_family, + NLM_F_MULTI, MPTCP_CMD_ADD_ADDR); + if (!hdr) + break; + + if (mptcp_nl_fill_addr(msg, entry) < 0) { + genlmsg_cancel(msg, hdr); + break; + } + + id = entry->addr.id; + genlmsg_end(msg, hdr); + } + spin_unlock_bh(&pernet->lock); + + cb->args[0] = id; + return msg->len; +} + +static int +mptcp_nl_cmd_set_rcv_add_addrs(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *attr = info->attrs[MPTCP_PM_ATTR_RCV_ADD_ADDRS]; + struct pm_nl_pernet *pernet = genl_info_pm_nl(info); + int limit; + + if (!attr) { + NL_SET_ERR_MSG(info->extack, "missing announce accept limit"); + return -EINVAL; + } + + limit = nla_get_u16(attr); + if (limit > MPTCP_PM_ADDR_MAX) { + NL_SET_ERR_MSG(info->extack, "announce accept limit greater than maximum"); + return -EINVAL; + } + + WRITE_ONCE(pernet->add_addr_accept_max, limit); + return 0; +} + +static int +mptcp_nl_cmd_get_rcv_add_addrs(struct sk_buff *skb, struct genl_info *info) +{ + struct pm_nl_pernet *pernet = genl_info_pm_nl(info); + struct sk_buff *msg; + void *reply; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + reply = genlmsg_put_reply(msg, info, &mptcp_genl_family, 0, + MPTCP_CMD_GET_RCV_ADD_ADDRS); + if (!reply) + goto fail; + + if (nla_put_u32(msg, MPTCP_PM_ATTR_RCV_ADD_ADDRS, + READ_ONCE(pernet->add_addr_accept_max))) + goto fail; + + genlmsg_end(msg, reply); + return genlmsg_reply(msg, info); + +fail: + NL_SET_ERR_MSG(info->extack, "not enough space in Netlink message"); + nlmsg_free(msg); + return -EMSGSIZE; +} + +static struct genl_ops mptcp_pm_ops[] = { + { + .cmd = MPTCP_CMD_ADD_ADDR, + .doit = mptcp_nl_cmd_add_addr, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = MPTCP_CMD_DEL_ADDR, + .doit = mptcp_nl_cmd_del_addr, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = MPTCP_CMD_FLUSH_ADDRS, + .doit = mptcp_nl_cmd_flush_addrs, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = MPTCP_CMD_GET_ADDR, + .doit = mptcp_nl_cmd_get_addr, + .dumpit = mptcp_nl_cmd_dump_addrs, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = MPTCP_CMD_SET_RCV_ADD_ADDRS, + .doit = mptcp_nl_cmd_set_rcv_add_addrs, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = MPTCP_CMD_GET_RCV_ADD_ADDRS, + .doit = mptcp_nl_cmd_get_rcv_add_addrs, + .flags = GENL_ADMIN_PERM, + }, +}; + +static struct genl_family mptcp_genl_family __ro_after_init = { + .name = MPTCP_PM_NAME, + .version = MPTCP_PM_VER, + .maxattr = MPTCP_PM_ATTR_MAX, + .policy = mptcp_pm_policy, + .netnsok = true, + .module = THIS_MODULE, + .ops = mptcp_pm_ops, + .n_ops = ARRAY_SIZE(mptcp_pm_ops), + .mcgrps = mptcp_pm_mcgrps, + .n_mcgrps = ARRAY_SIZE(mptcp_pm_mcgrps), +}; + +static int __net_init pm_nl_init_net(struct net *net) +{ + struct pm_nl_pernet *pernet = net_generic(net, pm_nl_pernet_id); + + INIT_LIST_HEAD_RCU(&pernet->addr_list); + __reset_counters(pernet); + pernet->next_id = 1; + spin_lock_init(&pernet->lock); + return 0; +} + +static void __net_exit pm_nl_exit_net(struct list_head *net_list) +{ + struct net *net; + + list_for_each_entry(net, net_list, exit_list) { + /* net is removed from namespace list, can't race with + * other modifiers + */ + __flush_addrs(net_generic(net, pm_nl_pernet_id)); + } +} + +static struct pernet_operations mptcp_pm_pernet_ops = { + .init = pm_nl_init_net, + .exit_batch = pm_nl_exit_net, + .id = &pm_nl_pernet_id, + .size = sizeof(struct pm_nl_pernet), +}; + +void mptcp_pm_nl_init(void) +{ + if (register_pernet_subsys(&mptcp_pm_pernet_ops) < 0) + panic("Failed to register MPTCP PM pernet subsystem.\n"); + + if (genl_register_family(&mptcp_genl_family)) + panic("Failed to register MPTCP PM netlink family"); +} diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 9623f007f235..b8dff59cc8d1 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -386,6 +386,13 @@ int mptcp_pm_addr_signal(struct mptcp_sock *msk, unsigned int remaining, struct mptcp_addr_info *saddr); int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc); +void mptcp_pm_nl_init(void); +void mptcp_pm_nl_data_init(struct mptcp_sock *msk); +void mptcp_pm_nl_fully_established(struct mptcp_sock *msk); +void mptcp_pm_nl_subflow_established(struct mptcp_sock *msk); +void mptcp_pm_nl_add_addr(struct mptcp_sock *msk); +int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct sock_common *skc); + static inline struct mptcp_ext *mptcp_get_ext(struct sk_buff *skb) { return (struct mptcp_ext *)skb_ext_find(skb, SKB_EXT_MPTCP); From patchwork Mon Feb 17 18:28:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Abeni X-Patchwork-Id: 1239499 X-Patchwork-Delegate: pabeni@redhat.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.01.org (client-ip=2001:19d0:306:5::1; helo=ml01.01.org; envelope-from=mptcp-bounces@lists.01.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Xpi0lVz4; dkim-atps=neutral Received: from ml01.01.org (ml01.01.org [IPv6:2001:19d0:306:5::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48LsvK0MZ8z9s29 for ; Tue, 18 Feb 2020 05:29:00 +1100 (AEDT) Received: from ml01.vlan13.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 2FEA810FC35A1; Mon, 17 Feb 2020 10:32:16 -0800 (PST) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=207.211.31.81; helo=us-smtp-delivery-1.mimecast.com; envelope-from=pabeni@redhat.com; receiver= Received: from us-smtp-delivery-1.mimecast.com (us-smtp-2.mimecast.com [207.211.31.81]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id E6FB410FC3419 for ; Mon, 17 Feb 2020 10:32:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1581964131; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=iGiroc8zW5pYMgeADoYyoaeOmZ3Zux31gLipNQhCoSo=; b=Xpi0lVz4PPxyqiyD9Oe5TaWlyRAAa6eShY8weRflRrLlcRiU2OifBTQPAMj7Zfa4W7R4i5 sbT1Rqum4lC+TdgpWJlZ3IDtKHitGEyE1R9utluRFfriAPUzlUsr1Y4ed1KJTigFbHeVXh poZhxax/uqPtqARHU0jgSAMG4pL6IeM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-87-FZhgOKhgMkqTGqlgy1Lkyw-1; Mon, 17 Feb 2020 13:28:49 -0500 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 67323107ACCA for ; Mon, 17 Feb 2020 18:28:48 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-116-153.ams2.redhat.com [10.36.116.153]) by smtp.corp.redhat.com (Postfix) with ESMTP id C295788859 for ; Mon, 17 Feb 2020 18:28:47 +0000 (UTC) From: Paolo Abeni To: mptcp@lists.01.org Date: Mon, 17 Feb 2020 19:28:33 +0100 Message-Id: <6e6bf9803a683ffd84daee3ed8db3941831546a6.1581963739.git.pabeni@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-MC-Unique: FZhgOKhgMkqTGqlgy1Lkyw-1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Message-ID-Hash: EMEWO5UPG57SYE4C22UA2KA43KAEL23F X-Message-ID-Hash: EMEWO5UPG57SYE4C22UA2KA43KAEL23F X-MailFrom: pabeni@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.1.1 Precedence: list Subject: [MPTCP] [PATCH 7/7] selftests: add PM netlink functional tests List-Id: Discussions regarding MPTCP upstreaming Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/mptcp/Makefile | 4 +- .../testing/selftests/net/mptcp/pm_netlink.sh | 149 +++++ tools/testing/selftests/net/mptcp/pm_nl_ctl.c | 556 ++++++++++++++++++ 3 files changed, 707 insertions(+), 2 deletions(-) create mode 100755 tools/testing/selftests/net/mptcp/pm_netlink.sh create mode 100644 tools/testing/selftests/net/mptcp/pm_nl_ctl.c diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile index 93de52016dde..8bbb676e49b6 100644 --- a/tools/testing/selftests/net/mptcp/Makefile +++ b/tools/testing/selftests/net/mptcp/Makefile @@ -2,11 +2,11 @@ top_srcdir = ../../../../.. -CFLAGS = -Wall -Wl,--no-as-needed -O2 -g +CFLAGS = -Wall -Wl,--no-as-needed -O2 -g -iquote../../../../../include/uapi/ TEST_PROGS := mptcp_connect.sh -TEST_GEN_FILES = mptcp_connect +TEST_GEN_FILES = mptcp_connect pm_nl_ctl EXTRA_CLEAN := *.pcap diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testing/selftests/net/mptcp/pm_netlink.sh new file mode 100755 index 000000000000..817c4200e348 --- /dev/null +++ b/tools/testing/selftests/net/mptcp/pm_netlink.sh @@ -0,0 +1,149 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +capture=false +ksft_skip=4 +ret=0 + +usage() { + echo "Usage: $0 [ -a ]" + echo -e "\t-c: capture packets for each test using tcpdump (default: no capture)" +} + + +while getopts "$optstring" option;do + case "$option" in + "h") + usage $0 + exit 0 + ;; + "c") + capture=true + ;; + "?") + usage $0 + exit 1 + ;; + esac +done + +sec=$(date +%s) +rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) +ns1="ns1-$rndh" +ns2="ns2-$rndh" +err=$(mktemp) +ret=0 + +cleanup() +{ + rm -f $out + for netns in "$ns1" "$ns2";do + ip netns del $netns + done +} + +ip -Version > /dev/null 2>&1 +if [ $? -ne 0 ];then + echo "SKIP: Could not run test without ip tool" + exit $ksft_skip +fi + +trap cleanup EXIT + +for i in "$ns1" "$ns2";do + ip netns add $i || exit $ksft_skip + ip -net $i link set lo up + ip netns exec $i sysctl -q net.mptcp.enabled=1 +done + +# "$ns1" ns2 +# ns1eth1 ns2eth1 +# ns1eth2 ns2eth2 +# ns1eth3 ns2eth3 + +for i in `seq 1 3`; do + ip link add ns1eth$i netns "$ns1" type veth peer name ns2eth$i netns "$ns2" + ip -net "$ns1" addr add 10.0.$i.1/24 dev ns1eth$i + ip -net "$ns1" addr add dead:beef:$i::1/64 dev ns1eth$i nodad + ip -net "$ns1" link set ns1eth$i up + + ip -net "$ns2" addr add 10.0.$i.2/24 dev ns2eth$i + ip -net "$ns2" addr add dead:beef:$i::2/64 dev ns2eth$i nodad + ip -net "$ns2" link set ns2eth$i up +done + +check() +{ + local cmd="$1" + local expected="$2" + local msg="$3" + local out=`$cmd 2>$err` + local cmd_ret=$? + + printf "%-50s %s" "$msg" + if [ $cmd_ret -ne 0 ]; then + echo "[FAIL] command execution '$cmd' stderr " + cat $err + ret=1 + elif [ "$out" = "$expected" ]; then + echo "[ OK ]" + else + echo -n "[FAIL] " + echo "expected '$expected' got '$out'" + ret=1 + fi +} + +check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "defaults addr list" +check "ip netns exec $ns1 ./pm_nl_ctl accept" "accept 0" "defaults accept add addr" + +ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.1 +ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.2 flags subflow +ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.3 flags signal +check "ip netns exec $ns1 ./pm_nl_ctl get 1" "id 1 flags 10.0.1.1 " "simple add/get addr" + +check "ip netns exec $ns1 ./pm_nl_ctl dump" \ +"id 1 flags 10.0.1.1 +id 2 flags subflow 10.0.1.2 +id 3 flags signal 10.0.1.3 " "dump addrs" + +ip netns exec $ns1 ./pm_nl_ctl del 2 +check "ip netns exec $ns1 ./pm_nl_ctl get 2" "" "simple del addr" +check "ip netns exec $ns1 ./pm_nl_ctl dump" \ +"id 1 flags 10.0.1.1 +id 3 flags signal 10.0.1.3 " "dump addrs after del" + +ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.3 +check "ip netns exec $ns1 ./pm_nl_ctl get 4" "" "duplicate addr" + +ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.4 id 10 flags signal +check "ip netns exec $ns1 ./pm_nl_ctl get 4" "id 4 flags signal 10.0.1.4 " "id addr increment" + +for i in `seq 5 9`; do + ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.$i flags signal >/dev/null 2>&1 +done +check "ip netns exec $ns1 ./pm_nl_ctl get 9" "id 9 flags signal 10.0.1.9 " "hard addr limit" +check "ip netns exec $ns1 ./pm_nl_ctl get 10" "" "above hard addr limit" + +for i in `seq 9 256`; do + ip netns exec $ns1 ./pm_nl_ctl del $i + ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.9 +done +check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags 10.0.1.1 +id 3 flags signal 10.0.1.3 +id 4 flags signal 10.0.1.4 +id 5 flags signal 10.0.1.5 +id 6 flags signal 10.0.1.6 +id 7 flags signal 10.0.1.7 +id 8 flags signal 10.0.1.8 " "id limit" + +ip netns exec $ns1 ./pm_nl_ctl flush +check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "flush addrs" + +ip netns exec $ns1 ./pm_nl_ctl accept 9 +check "ip netns exec $ns1 ./pm_nl_ctl accept" "accept 0" "above accept add_addr hard limit" + +ip netns exec $ns1 ./pm_nl_ctl accept 8 +check "ip netns exec $ns1 ./pm_nl_ctl accept" "accept 8" "set accept add_addr" + +exit $ret diff --git a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c new file mode 100644 index 000000000000..a2512f7c3d70 --- /dev/null +++ b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c @@ -0,0 +1,556 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include "linux/mptcp.h" + +#ifndef MPTCP_PM_NAME +#define MPTCP_PM_NAME "mptcp_pm" +#endif + +static void syntax(char *argv[]) +{ + fprintf(stderr, "%s add|get|del|flush|dump|accept []\n", argv[0]); + fprintf(stderr, "\tadd [flags signal|subflow] \n"); + fprintf(stderr, "\tdel \n"); + fprintf(stderr, "\tget \n"); + fprintf(stderr, "\tflush\n"); + fprintf(stderr, "\tdump\n"); + fprintf(stderr, "\taccept []\n"); + exit(0); +} + +static int init_genl_req(char *data, int family, int cmd, int version) +{ + struct nlmsghdr *nh = (void *)data; + struct genlmsghdr *gh; + int off = 0; + + nh->nlmsg_type = family; + nh->nlmsg_flags = NLM_F_REQUEST; + nh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + off += NLMSG_ALIGN(sizeof(*nh)); + + gh = (void *)(data + off); + gh->cmd = cmd; + gh->version = version; + off += NLMSG_ALIGN(sizeof(*gh)); + return off; +} + +static void nl_error(struct nlmsghdr *nh) +{ + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nh); + int len = nh->nlmsg_len - sizeof(*nh); + uint32_t off; + + if (len < sizeof(struct nlmsgerr)) + error(1, 0, "netlink error message truncated %d min %ld", len, + sizeof(struct nlmsgerr)); + + if (!err->error) { + /* check messages from kernel */ + struct rtattr *attrs = (struct rtattr *)NLMSG_DATA(nh); + while (RTA_OK(attrs, len)) { + if (attrs->rta_type == NLMSGERR_ATTR_MSG) + fprintf(stderr, "netlink ext ack msg: %s\n", + (char *)RTA_DATA(attrs)); + if (attrs->rta_type == NLMSGERR_ATTR_OFFS) { + memcpy(&off, RTA_DATA(attrs), 4); + fprintf(stderr, "netlink err off %d\n", + (int)off); + } + attrs = RTA_NEXT(attrs, len); + } + } else { + fprintf(stderr, "netlink error %d", err->error); + } +} + +/* 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) +{ + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; + socklen_t addr_len; + void *data = nh; + int rem, ret; + int err = 0; + + nh->nlmsg_len = len; + ret = sendto(fd, data, len, 0, (void *)&nladdr, sizeof(nladdr)); + if (ret != len) + error(1, errno, "send netlink: %uB != %uB\n", ret, len); + if (max == 0) + return 0; + + addr_len = sizeof(nladdr); + rem = ret = recvfrom(fd, data, max, 0, (void *)&nladdr, &addr_len); + if (ret < 0) + error(1, errno, "recv netlink: %uB\n", ret); + + /* Beware: the NLMSG_NEXT macro updates the 'rem' argument */ + for (;NLMSG_OK(nh, rem); nh = NLMSG_NEXT(nh, rem)) { + if (nh->nlmsg_type == NLMSG_ERROR) { + nl_error(nh); + err = 1; + } + } + if (err) + error(1, 0, "bailing out due to netlink error[s]"); + return ret; +} + +static int genl_parse_getfamily(struct nlmsghdr *nlh) +{ + struct genlmsghdr *ghdr = NLMSG_DATA(nlh); + int len = nlh->nlmsg_len; + struct rtattr *attrs; + + if (nlh->nlmsg_type != GENL_ID_CTRL) + error(1, errno, "Not a controller message, nlmsg_len=%d " + "nlmsg_type=0x%x\n", nlh->nlmsg_len, nlh->nlmsg_type); + + len -= NLMSG_LENGTH(GENL_HDRLEN); + + if (len < 0) + error(1, errno, "wrong controller message len %d\n", len); + + if (ghdr->cmd != CTRL_CMD_NEWFAMILY) + error(1, errno, "Unknown controller command %d\n", ghdr->cmd); + + attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN); + while (RTA_OK(attrs, len)) { + if (attrs->rta_type == CTRL_ATTR_FAMILY_ID) + return *(__u16 *)RTA_DATA(attrs); + attrs = RTA_NEXT(attrs, len); + } + + error(1, errno, "can't find CTRL_ATTR_FAMILY_ID attr"); + return -1; +} + +static int resolve_mptcp_pm_netlink(int fd) +{ + char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct genlmsghdr)) + + 1024]; + struct nlmsghdr *nh; + struct rtattr *rta; + int namelen; + int off = 0; + + memset(data, 0, sizeof(data)); + nh = (void *)data; + off = init_genl_req(data, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 0); + + rta = (void *)(data + off); + namelen = strlen(MPTCP_PM_NAME) + 1; + rta->rta_type = CTRL_ATTR_FAMILY_NAME; + rta->rta_len = RTA_LENGTH(namelen); + memcpy(RTA_DATA(rta), MPTCP_PM_NAME, namelen); + off += NLMSG_ALIGN(rta->rta_len); + + do_nl_req(fd, nh, off, sizeof(data)); + return genl_parse_getfamily((void *)data); +} + +int add_addr(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, *nest; + struct nlmsghdr *nh; + u_int16_t family; + u_int32_t flags; + int nest_start; + 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_CMD_ADD_ADDR, MPTCP_PM_VER); + + if (argc < 3) + syntax(argv); + + nest_start = off; + nest = (void *)(data + off); + nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; + nest->rta_len = RTA_LENGTH(0); + off += NLMSG_ALIGN(nest->rta_len); + + /* addr data */ + 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); + + /* 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], "flags")) { + /* flags */ + flags = 0; + if (++arg >= argc) + error(1, 0, " missing flags value"); + + /* do not support flag list yet */ + if (!strcmp(argv[arg], "subflow")) + flags |= MPTCP_PM_ADDR_FLAG_SUBFLOW; + else if (!strcmp(argv[arg], "signal")) + flags |= MPTCP_PM_ADDR_FLAG_SIGNAL; + else + error(1, errno, "unknown flag %s", argv[arg]); + + 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); + } else 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_ADDR_ATTR_ID; + rta->rta_len = RTA_LENGTH(1); + memcpy(RTA_DATA(rta), &id, 1); + off += NLMSG_ALIGN(rta->rta_len); + } else + error(1, 0, "unknown keyword %s", argv[arg]); + } + nest->rta_len = off - nest_start; + + do_nl_req(fd, nh, off, 0); + return 0; +} + +int del_addr(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, *nest; + struct nlmsghdr *nh; + int nest_start; + u_int8_t id; + int off = 0; + + memset(data, 0, sizeof(data)); + nh = (void *)data; + off = init_genl_req(data, pm_family, MPTCP_CMD_DEL_ADDR, MPTCP_PM_VER); + + /* the only argument is the address id */ + if (argc != 3) + syntax(argv); + + id = atoi(argv[2]); + + nest_start = off; + nest = (void *)(data + off); + nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; + nest->rta_len = RTA_LENGTH(0); + off += NLMSG_ALIGN(nest->rta_len); + + /* build a dummy addr with only the ID set */ + 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); + nest->rta_len = off - nest_start; + + do_nl_req(fd, nh, off, 0); + return 0; +} + +static void print_addr(struct rtattr *attrs, int len) +{ + uint16_t family = 0; + char str[1024]; + uint32_t flags; + uint8_t id; + + while (RTA_OK(attrs, len)) { + if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_FAMILY) + memcpy(&family, RTA_DATA(attrs), 2); + if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ADDR4) { + if (family != AF_INET) + error(1, errno, "wrong IP (v4) for family %d", + family); + inet_ntop(AF_INET, RTA_DATA(attrs), str, sizeof(str)); + printf("%s ", str); + } + if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ADDR6) { + if (family != AF_INET6) + error(1, errno, "wrong IP (v6) for family %d", + family); + inet_ntop(AF_INET6, RTA_DATA(attrs), str, sizeof(str)); + printf("%s ", str); + } + if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ID) { + memcpy(&id, RTA_DATA(attrs), 1); + printf("id %d ", id); + } + if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_FLAGS) { + memcpy(&flags, RTA_DATA(attrs), 4); + + printf("flags "); + if (flags & MPTCP_PM_ADDR_FLAG_SIGNAL) { + printf("signal"); + flags &= ~MPTCP_PM_ADDR_FLAG_SIGNAL; + if (flags) + printf(","); + } + + if (flags & MPTCP_PM_ADDR_FLAG_SUBFLOW) { + printf("subflow"); + flags &= ~MPTCP_PM_ADDR_FLAG_SUBFLOW; + if (flags) + printf(","); + } + + /* bump unkown flags, if any */ + if (flags) + printf("0x%x", flags); + printf(" "); + } + + attrs = RTA_NEXT(attrs, len); + } + printf("\n"); +} + +static void print_addrs(struct nlmsghdr *nh, int pm_family, int total_len) +{ + struct rtattr *attrs; + + for (;NLMSG_OK(nh, total_len); nh = NLMSG_NEXT(nh, total_len)) { + int len = nh->nlmsg_len; + + if (nh->nlmsg_type == NLMSG_DONE) + break; + if (nh->nlmsg_type == NLMSG_ERROR) + nl_error(nh); + if (nh->nlmsg_type != pm_family) + continue; + + len -= NLMSG_LENGTH(GENL_HDRLEN); + attrs = (struct rtattr *) ((char *) NLMSG_DATA(nh) + + GENL_HDRLEN); + while (RTA_OK(attrs, len)) { + if (attrs->rta_type == + (MPTCP_PM_ATTR_ADDR | NLA_F_NESTED)) + print_addr((void *)RTA_DATA(attrs), + attrs->rta_len); + attrs = RTA_NEXT(attrs, len); + } + } +} + +int get_addr(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, *nest; + struct nlmsghdr *nh; + int nest_start; + u_int8_t id; + int off = 0; + + memset(data, 0, sizeof(data)); + nh = (void *)data; + off = init_genl_req(data, pm_family, MPTCP_CMD_GET_ADDR, MPTCP_PM_VER); + + /* the only argument is the address id */ + if (argc != 3) + syntax(argv); + + id = atoi(argv[2]); + + nest_start = off; + nest = (void *)(data + off); + nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; + nest->rta_len = RTA_LENGTH(0); + off += NLMSG_ALIGN(nest->rta_len); + + /* build a dummy addr with only the ID set */ + 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); + nest->rta_len = off - nest_start; + + print_addrs(nh, pm_family, do_nl_req(fd, nh, off, sizeof(data))); + return 0; +} + +int dump_addrs(int fd, int pm_family, int argc, char *argv[]) +{ + char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct genlmsghdr)) + + 1024]; + pid_t pid = getpid(); + struct nlmsghdr *nh; + int off = 0; + + memset(data, 0, sizeof(data)); + nh = (void *)data; + off = init_genl_req(data, pm_family, MPTCP_CMD_GET_ADDR, MPTCP_PM_VER); + nh->nlmsg_flags |= NLM_F_DUMP; + nh->nlmsg_seq = 1; + nh->nlmsg_pid = pid; + nh->nlmsg_len = off; + + print_addrs(nh, pm_family, do_nl_req(fd, nh, off, sizeof(data))); + return 0; +} + +int flush_addrs(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; + int off = 0; + + memset(data, 0, sizeof(data)); + nh = (void *)data; + off = init_genl_req(data, pm_family, MPTCP_CMD_FLUSH_ADDRS, MPTCP_PM_VER); + + do_nl_req(fd, nh, off, 0); + return 0; +} + +static void print_accept(struct nlmsghdr *nh, int pm_family, int total_len) +{ + struct rtattr *attrs; + uint32_t max; + + for (;NLMSG_OK(nh, total_len); nh = NLMSG_NEXT(nh, total_len)) { + int len = nh->nlmsg_len; + + if (nh->nlmsg_type == NLMSG_DONE) + break; + if (nh->nlmsg_type == NLMSG_ERROR) + nl_error(nh); + if (nh->nlmsg_type != pm_family) + continue; + + len -= NLMSG_LENGTH(GENL_HDRLEN); + attrs = (struct rtattr *) ((char *) NLMSG_DATA(nh) + + GENL_HDRLEN); + while (RTA_OK(attrs, len)) { + if (attrs->rta_type != MPTCP_PM_ATTR_RCV_ADD_ADDRS) + goto next; + + memcpy(&max, RTA_DATA(attrs), 4); + printf("accept %u\n", max); + +next: + attrs = RTA_NEXT(attrs, len); + } + } +} + +int get_set_accept(int fd, int pm_family, int argc, char *argv[]) +{ + char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct genlmsghdr)) + + 1024]; + int cmd, len = sizeof(data); + struct nlmsghdr *nh; + uint32_t max = 0; + int off = 0; + + /* limit */ + if (argc == 3) { + max = atoi(argv[2]); + cmd = MPTCP_CMD_SET_RCV_ADD_ADDRS; + } else { + cmd = MPTCP_CMD_GET_RCV_ADD_ADDRS; + } + + memset(data, 0, sizeof(data)); + nh = (void *)data; + off = init_genl_req(data, pm_family, cmd, MPTCP_PM_VER); + + /* limit */ + if (cmd == MPTCP_CMD_SET_RCV_ADD_ADDRS) { + struct rtattr *rta = (void *)(data + off); + + rta->rta_type = MPTCP_PM_ATTR_RCV_ADD_ADDRS; + rta->rta_len = RTA_LENGTH(4); + memcpy(RTA_DATA(rta), &max, 4); + off += NLMSG_ALIGN(rta->rta_len); + cmd = MPTCP_CMD_SET_RCV_ADD_ADDRS; + len = 0; + } + + len = do_nl_req(fd, nh, off, len); + if (cmd == MPTCP_CMD_GET_RCV_ADD_ADDRS) + print_accept(nh, pm_family, len); + return 0; +} + +int main(int argc, char *argv[]) +{ + int fd, pm_family; + + if (argc < 2) + syntax(argv); + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (fd == -1) + error(1, errno, "socket netlink"); + + pm_family = resolve_mptcp_pm_netlink(fd); + + if (!strcmp(argv[1], "add")) + return add_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")) + return flush_addrs(fd, pm_family, argc, argv); + else if (!strcmp(argv[1], "get")) + return get_addr(fd, pm_family, argc, argv); + else if (!strcmp(argv[1], "dump")) + return dump_addrs(fd, pm_family, argc, argv); + else if (!strcmp(argv[1], "accept")) + return get_set_accept(fd, pm_family, argc, argv); + + fprintf(stderr, "unknown sub-command: %s", argv[1]); + syntax(argv); + return 0; +}