Received: by 2002:a6b:fb09:0:0:0:0:0 with SMTP id h9csp1507044iog; Tue, 14 Jun 2022 07:26:19 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzWzTLmeCbST6cMiToIa44GYpxqUKzw+BtNHFPFWiM5wAXPJtIufc8ibjspgzYPRPUE/a5Z X-Received: by 2002:a17:906:9c82:b0:6e1:1d6c:914c with SMTP id fj2-20020a1709069c8200b006e11d6c914cmr4392172ejc.769.1655216779283; Tue, 14 Jun 2022 07:26:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1655216779; cv=none; d=google.com; s=arc-20160816; b=Xqbt35u5Co/HxcKC72O8YMWls1kXyDqstJ9ooEIUJJiEpCvkdagq6dQvOE4FcP86wS bzl/kbr8WttzNQBJTn2hK9xaeWMa5O8aojOZ4XzhOYKnjlIM/AuggeVt5f8+d7ENTZne XvMh4S1p+HSpi/891PMHvV6Tvoy1FHi/VnFqEXPJkcYGGKOZVZEUsyfMQRw9Uyw1jcon YikL6wpeMmw9fUr4EjugrcgfqsUWsuFKb8t53Ah3Z7IHnVj0pvZTQ0GXcz6mqJdNnSsc he2dX80+fLqcI4J+Y2pHO0+xemOs21YV6E038lcnE/mEBph4G9RAMMpuOpceUwgVf2F/ C0aw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:organization:date:cc:to:from:subject:message-id :dkim-signature:dkim-signature; bh=bDd90XKiMgODuOQ3PSH0JGyFEVry39DF/53FITgmXG8=; b=XhBflVMP+YTgWtym7EuV7sauEhTpb3il9HAJmc7Sb6KF6RbCv2AmGTa9DFkW9f9OaF DrofORLKLr3SWoBXccZD+xmGDD2LyUIsvEionmnka2WPQ8lwQKtw5MWHXDf+YM/4jIz3 lgU6mwj2cmdeYaqqt+DRBy8aCe8ppHeDaxz0DgoATIqPxz7qPuewMSCkTbiIhfeYJkNk L4vTnp8gxcq71lnNzd14+z/Qv3ZqLhF+E00SDi3/rTToXNBJwyhm2ue3wE6pC61mXUxV XFZRisAXVl3OLhROqf8r5qyPCBKe3VkV0Nlrph2IFulRgf7Alx14CmqM3izehDkw6/uS laHw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@suse.cz header.s=susede2_rsa header.b=u9j6uBzH; dkim=neutral (no key) header.i=@suse.cz header.s=susede2_ed25519 header.b=qNh9XhTZ; spf=pass (google.com: domain of linux-nfs-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-nfs-owner@vger.kernel.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id sh16-20020a1709076e9000b006fee2aa8759si12510747ejc.797.2022.06.14.07.25.45; Tue, 14 Jun 2022 07:26:19 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-nfs-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@suse.cz header.s=susede2_rsa header.b=u9j6uBzH; dkim=neutral (no key) header.i=@suse.cz header.s=susede2_ed25519 header.b=qNh9XhTZ; spf=pass (google.com: domain of linux-nfs-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-nfs-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237995AbiFNOZm (ORCPT + 99 others); Tue, 14 Jun 2022 10:25:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43794 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236169AbiFNOZl (ORCPT ); Tue, 14 Jun 2022 10:25:41 -0400 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.220.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4E2B52CE27 for ; Tue, 14 Jun 2022 07:25:40 -0700 (PDT) Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 076861F8F6; Tue, 14 Jun 2022 14:25:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.cz; s=susede2_rsa; t=1655216739; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=bDd90XKiMgODuOQ3PSH0JGyFEVry39DF/53FITgmXG8=; b=u9j6uBzHXc2J88c1KW61Tg3zEyzXTrnP5EBaZR/1YpX6v3f8JfizjaxIecX+ji9W0J2z+a x95UZ8+4NGx5H5nW+GTvy9cl7UkPRWizQlZ9GtJJCVLE+WXfqiRfj7rHXNntHzHkNWaZsm eU6DNP0qnhMsUH6R8rFWGa+Ds6J7kBE= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.cz; s=susede2_ed25519; t=1655216739; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=bDd90XKiMgODuOQ3PSH0JGyFEVry39DF/53FITgmXG8=; b=qNh9XhTZsRSFHHKAJQmJu0xJbz+mQEgJ9itLYtVLwr/yhkGvY2l3fxQly57EFmK3m2q6mE VTb2PMn6q2I9LTBA== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id DF16A139EC; Tue, 14 Jun 2022 14:25:38 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id XP1yNWKaqGKEQwAAMHmgww (envelope-from ); Tue, 14 Jun 2022 14:25:38 +0000 Message-ID: Subject: [PATCH] binddynport.c honor ip_local_reserved_ports From: Otto Hollmann To: libtirpc-devel@lists.sourceforge.net Cc: linux-nfs@vger.kernel.org Date: Tue, 14 Jun 2022 16:25:38 +0200 Organization: SUSE Content-Type: text/plain; charset="UTF-8" User-Agent: Evolution 3.34.4 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_MED,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org Read reserved ports from /proc/sys/net/ipv4/ip_local_reserved_ports, store them into bit-wise array and before binding to random port check if port is not reserved. Currently, there is no way how to reserve ports so then will not be used by rpcbind. Random ports are opened by rpcbind because of rmtcalls. There is compile-time flag for disabling them, but in some cases we can not simply disable them. One solution would be run time option --enable-rmtcalls as already discussed, but it was rejected. So if we want to keep rmtcalls enabled and also be able to reserve some ports, there is no other way than filtering available ports. The easiest and clearest way seems to be just respect kernel list of ip_reserved_ports. Unfortunately there is one known disadvantage/side effect - it affects probability of ports which are right after reserved ones. The bigger reserved block is, the higher is probability of selecting following unreserved port. But if there is no reserved port, impact of this patch is minimal/none. Signed-off-by: Otto Hollmann --- src/binddynport.c | 107 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 99 insertions(+), 8 deletions(-) diff --git a/src/binddynport.c b/src/binddynport.c index 062629a..6f78ebe 100644 --- a/src/binddynport.c +++ b/src/binddynport.c @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -56,6 +57,84 @@ enum { NPORTS = ENDPORT - LOWPORT + 1, }; +/* + * This function decodes information about given port from provided array and + * return if port is reserved or not. + * + * @reserved_ports an array of size at least "NPORTS / (8*sizeof(char)) + 1". + * @port port number within range LOWPORT and ENDPORT + * + * Returns 0 if port is not reserved, non-negative if port is reserved. + */ +int is_reserved(char *reserved_ports, int port) { + port -= LOWPORT; + if (port < 0 || port >= NPORTS) + return 0; + return reserved_ports[port/(8*sizeof(char))] & 1<<(port%(8*sizeof(char))); +} + +/* + * This function encodes information about given *reserved* port into provided + * array. Don't call this function for ports which are not reserved. + * + * @reserved_ports an array of size at least "NPORTS / (8*sizeof(char)) + 1". + * @port port number within range LOWPORT and ENDPORT + * + */ +void set_reserved(char *reserved_ports, int port) { + port -= LOWPORT; + if (port < 0 || port >= NPORTS) + return; + reserved_ports[port/(8*sizeof(char))] |= 1<<(port%(8*sizeof(char))); +} + +/* + * Parse local reserved ports obtained from + * /proc/sys/net/ipv4/ip_local_reserved_ports into bit array. + * + * @reserved_ports a zeroed array of size at least + * "NPORTS / (8*sizeof(char)) + 1". Will be used for bit-wise encoding of + * reserved ports. + * + * On each call, reserved ports are read from /proc and bit-wise stored into + * provided array + * + * Returns 0 on success, -1 on failure. + */ + +int parse_reserved_ports(char *reserved_ports) { + int from, to; + char delimiter = ','; + int res; + FILE * file_ptr = fopen("/proc/sys/net/ipv4/ip_local_reserved_ports","r"); + if (file_ptr == NULL) { + (void) syslog(LOG_ERR, + "Unable to open open /proc/sys/net/ipv4/ip_local_reserved_ports."); + return -1; + } + do { + if ((res = fscanf(file_ptr, "%d", &to)) != 1) { + if (res == EOF) break; + goto err; + } + if (delimiter != '-') { + from = to; + } + for (int i = from; i <= to; ++i) { + set_reserved(reserved_ports, i); + } + } while ((res = fscanf(file_ptr, "%c", &delimiter)) == 1); + if (res != EOF) + goto err; + fclose(file_ptr); + return 0; +err: + (void) syslog(LOG_ERR, + "An error occurred while parsing ip_local_reserved_ports."); + fclose(file_ptr); + return -1; +} + /* * Bind a socket to a dynamically-assigned IP port. * @@ -81,7 +160,8 @@ int __binddynport(int fd) in_port_t port, *portp; struct sockaddr *sap; socklen_t salen; - int i, res; + int i, res, array_size; + char *reserved_ports; if (__rpc_sockisbound(fd)) return 0; @@ -119,21 +199,32 @@ int __binddynport(int fd) gettimeofday(&tv, NULL); seed = tv.tv_usec * getpid(); } + array_size = NPORTS / (8*sizeof(char)) + 1; + reserved_ports = malloc(array_size); + if (!reserved_ports) { + goto out; + } + memset(reserved_ports, 0, array_size); + parse_reserved_ports(reserved_ports); + port = (rand_r(&seed) % NPORTS) + LOWPORT; for (i = 0; i < NPORTS; ++i) { - *portp = htons(port++); - res = bind(fd, sap, salen); - if (res >= 0) { - res = 0; - break; + *portp = htons(port); + if (!is_reserved(reserved_ports, port++)) { + res = bind(fd, sap, salen); + if (res >= 0) { + res = 0; + break; + } + if (errno != EADDRINUSE) + break; } - if (errno != EADDRINUSE) - break; if (port > ENDPORT) port = LOWPORT; } out: + free(reserved_ports); mutex_unlock(&port_lock); return res; } -- 2.26.2