xref: /openbsd/usr.sbin/bgpd/rde_sets.c (revision e386eeb1)
1 /*	$OpenBSD: rde_sets.c,v 1.13 2024/09/10 09:38:45 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2018 Claudio Jeker <claudio@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #include <sys/types.h>
19 #include <sys/queue.h>
20 
21 #include <assert.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 
28 #include "rde.h"
29 
30 struct set_table {
31 	void			*set;
32 	size_t			 nmemb;
33 	size_t			 size;
34 	size_t			 max;
35 };
36 
37 struct as_set *
as_sets_new(struct as_set_head * as_sets,const char * name,size_t nmemb,size_t size)38 as_sets_new(struct as_set_head *as_sets, const char *name, size_t nmemb,
39     size_t size)
40 {
41 	struct as_set *aset;
42 	size_t len;
43 
44 	aset = calloc(1, sizeof(*aset));
45 	if (aset == NULL)
46 		return NULL;
47 
48 	len = strlcpy(aset->name, name, sizeof(aset->name));
49 	assert(len < sizeof(aset->name));
50 
51 	aset->set = set_new(nmemb, size);
52 	if (aset->set == NULL) {
53 		free(aset);
54 		return NULL;
55 	}
56 
57 	SIMPLEQ_INSERT_TAIL(as_sets, aset, entry);
58 	return aset;
59 }
60 
61 struct as_set *
as_sets_lookup(struct as_set_head * as_sets,const char * name)62 as_sets_lookup(struct as_set_head *as_sets, const char *name)
63 {
64 	struct as_set *aset;
65 
66 	SIMPLEQ_FOREACH(aset, as_sets, entry) {
67 		if (strcmp(aset->name, name) == 0)
68 			return aset;
69 	}
70 	return NULL;
71 }
72 
73 
74 void
as_sets_free(struct as_set_head * as_sets)75 as_sets_free(struct as_set_head *as_sets)
76 {
77 	struct as_set *aset;
78 
79 	if (as_sets == NULL)
80 		return;
81 	while (!SIMPLEQ_EMPTY(as_sets)) {
82 		aset = SIMPLEQ_FIRST(as_sets);
83 		SIMPLEQ_REMOVE_HEAD(as_sets, entry);
84 		set_free(aset->set);
85 		free(aset);
86 	}
87 }
88 
89 void
as_sets_mark_dirty(struct as_set_head * old,struct as_set_head * new)90 as_sets_mark_dirty(struct as_set_head *old, struct as_set_head *new)
91 {
92 	struct as_set	*n, *o;
93 
94 	SIMPLEQ_FOREACH(n, new, entry) {
95 		if (old == NULL || (o = as_sets_lookup(old, n->name)) == NULL ||
96 		    !set_equal(n->set, o->set)) {
97 			n->dirty = 1;
98 			n->lastchange = getmonotime();
99 		} else
100 			n->lastchange = o->lastchange;
101 	}
102 }
103 
104 int
as_set_match(const struct as_set * aset,uint32_t asnum)105 as_set_match(const struct as_set *aset, uint32_t asnum)
106 {
107 	return set_match(aset->set, asnum) != NULL;
108 }
109 
110 struct set_table *
set_new(size_t nmemb,size_t size)111 set_new(size_t nmemb, size_t size)
112 {
113 	struct set_table *set;
114 
115 	set = calloc(1, sizeof(*set));
116 	if (set == NULL)
117 		return NULL;
118 
119 	if (nmemb == 0)
120 		nmemb = 4;
121 
122 	set->size = size;
123 	set->max = nmemb;
124 	set->set = calloc(nmemb, set->size);
125 	if (set->set == NULL) {
126 		free(set);
127 		return NULL;
128 	}
129 
130 	rdemem.aset_cnt++;
131 	rdemem.aset_size += sizeof(*set);
132 	rdemem.aset_size += set->size * set->max;
133 	return set;
134 }
135 
136 void
set_free(struct set_table * set)137 set_free(struct set_table *set)
138 {
139 	if (set == NULL)
140 		return;
141 	rdemem.aset_cnt--;
142 	rdemem.aset_size -= sizeof(*set);
143 	rdemem.aset_size -= set->size * set->max;
144 	rdemem.aset_nmemb -= set->nmemb;
145 	free(set->set);
146 	free(set);
147 }
148 
149 int
set_add(struct set_table * set,void * elms,size_t nelms)150 set_add(struct set_table *set, void *elms, size_t nelms)
151 {
152 	if (nelms == 0)		/* nothing todo */
153 		return 0;
154 
155 	if (set->max < nelms || set->max - nelms < set->nmemb) {
156 		uint32_t *s;
157 		size_t new_size;
158 
159 		if (set->nmemb >= SIZE_MAX - 4096 - nelms) {
160 			errno = ENOMEM;
161 			return -1;
162 		}
163 		for (new_size = set->max; new_size < set->nmemb + nelms; )
164 			new_size += (new_size < 4096 ? new_size : 4096);
165 
166 		s = reallocarray(set->set, new_size, set->size);
167 		if (s == NULL)
168 			return -1;
169 		rdemem.aset_size += set->size * (new_size - set->max);
170 		set->set = s;
171 		set->max = new_size;
172 	}
173 
174 	memcpy((uint8_t *)set->set + set->nmemb * set->size, elms,
175 	    nelms * set->size);
176 	set->nmemb += nelms;
177 	rdemem.aset_nmemb += nelms;
178 
179 	return 0;
180 }
181 
182 void *
set_get(struct set_table * set,size_t * nelms)183 set_get(struct set_table *set, size_t *nelms)
184 {
185 	*nelms = set->nmemb;
186 	return set->set;
187 }
188 
189 static int
set_cmp(const void * ap,const void * bp)190 set_cmp(const void *ap, const void *bp)
191 {
192 	const uint32_t *a = ap;
193 	const uint32_t *b = bp;
194 
195 	if (*a > *b)
196 		return 1;
197 	else if (*a < *b)
198 		return -1;
199 	return 0;
200 }
201 
202 void
set_prep(struct set_table * set)203 set_prep(struct set_table *set)
204 {
205 	if (set == NULL)
206 		return;
207 	qsort(set->set, set->nmemb, set->size, set_cmp);
208 }
209 
210 void *
set_match(const struct set_table * a,uint32_t asnum)211 set_match(const struct set_table *a, uint32_t asnum)
212 {
213 	if (a == NULL)
214 		return NULL;
215 	return bsearch(&asnum, a->set, a->nmemb, a->size, set_cmp);
216 }
217 
218 int
set_equal(const struct set_table * a,const struct set_table * b)219 set_equal(const struct set_table *a, const struct set_table *b)
220 {
221 	/* allow NULL pointers to be passed */
222 	if (a == NULL && b == NULL)
223 		return 1;
224 	if (a == NULL || b == NULL)
225 		return 0;
226 
227 	if (a->nmemb != b->nmemb)
228 		return 0;
229 	if (memcmp(a->set, b->set, a->nmemb * a->size) != 0)
230 		return 0;
231 	return 1;
232 }
233 
234 size_t
set_nmemb(const struct set_table * set)235 set_nmemb(const struct set_table *set)
236 {
237 	return set->nmemb;
238 }
239