1/*	$NetBSD: portset.c,v 1.6 2022/09/23 12:15:33 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16/*! \file */
17
18#include <inttypes.h>
19#include <stdbool.h>
20
21#include <isc/mem.h>
22#include <isc/portset.h>
23#include <isc/string.h>
24#include <isc/types.h>
25#include <isc/util.h>
26
27#define ISC_PORTSET_BUFSIZE (65536 / (sizeof(uint32_t) * 8))
28
29/*%
30 * Internal representation of portset.  It's an array of 32-bit integers, each
31 * bit corresponding to a single port in the ascending order.  For example,
32 * the second most significant bit of buf[0] corresponds to port 1.
33 */
34struct isc_portset {
35	unsigned int nports; /*%< number of ports in the set */
36	uint32_t buf[ISC_PORTSET_BUFSIZE];
37};
38
39static bool
40portset_isset(isc_portset_t *portset, in_port_t port) {
41	return ((portset->buf[port >> 5] & ((uint32_t)1 << (port & 31))) != 0);
42}
43
44static void
45portset_add(isc_portset_t *portset, in_port_t port) {
46	if (!portset_isset(portset, port)) {
47		portset->nports++;
48		portset->buf[port >> 5] |= ((uint32_t)1 << (port & 31));
49	}
50}
51
52static void
53portset_remove(isc_portset_t *portset, in_port_t port) {
54	if (portset_isset(portset, port)) {
55		portset->nports--;
56		portset->buf[port >> 5] &= ~((uint32_t)1 << (port & 31));
57	}
58}
59
60isc_result_t
61isc_portset_create(isc_mem_t *mctx, isc_portset_t **portsetp) {
62	isc_portset_t *portset;
63
64	REQUIRE(portsetp != NULL && *portsetp == NULL);
65
66	portset = isc_mem_get(mctx, sizeof(*portset));
67
68	/* Make the set 'empty' by default */
69	memset(portset, 0, sizeof(*portset));
70	*portsetp = portset;
71
72	return (ISC_R_SUCCESS);
73}
74
75void
76isc_portset_destroy(isc_mem_t *mctx, isc_portset_t **portsetp) {
77	isc_portset_t *portset;
78
79	REQUIRE(portsetp != NULL);
80	portset = *portsetp;
81
82	isc_mem_put(mctx, portset, sizeof(*portset));
83}
84
85bool
86isc_portset_isset(isc_portset_t *portset, in_port_t port) {
87	REQUIRE(portset != NULL);
88
89	return (portset_isset(portset, port));
90}
91
92unsigned int
93isc_portset_nports(isc_portset_t *portset) {
94	REQUIRE(portset != NULL);
95
96	return (portset->nports);
97}
98
99void
100isc_portset_add(isc_portset_t *portset, in_port_t port) {
101	REQUIRE(portset != NULL);
102
103	portset_add(portset, port);
104}
105
106void
107isc_portset_remove(isc_portset_t *portset, in_port_t port) {
108	portset_remove(portset, port);
109}
110
111void
112isc_portset_addrange(isc_portset_t *portset, in_port_t port_lo,
113		     in_port_t port_hi) {
114	in_port_t p;
115
116	REQUIRE(portset != NULL);
117	REQUIRE(port_lo <= port_hi);
118
119	p = port_lo;
120	do {
121		portset_add(portset, p);
122	} while (p++ < port_hi);
123}
124
125void
126isc_portset_removerange(isc_portset_t *portset, in_port_t port_lo,
127			in_port_t port_hi) {
128	in_port_t p;
129
130	REQUIRE(portset != NULL);
131	REQUIRE(port_lo <= port_hi);
132
133	p = port_lo;
134	do {
135		portset_remove(portset, p);
136	} while (p++ < port_hi);
137}
138