xref: /openbsd/usr.sbin/bgpd/rde_aspa.c (revision c0c9c169)
1*c0c9c169Sclaudio /*	$OpenBSD: rde_aspa.c,v 1.5 2023/08/16 08:26:35 claudio Exp $ */
228d66047Sclaudio 
328d66047Sclaudio /*
428d66047Sclaudio  * Copyright (c) 2022 Claudio Jeker <claudio@openbsd.org>
528d66047Sclaudio  *
628d66047Sclaudio  * Permission to use, copy, modify, and distribute this software for any
728d66047Sclaudio  * purpose with or without fee is hereby granted, provided that the above
828d66047Sclaudio  * copyright notice and this permission notice appear in all copies.
928d66047Sclaudio  *
1028d66047Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1128d66047Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1228d66047Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1328d66047Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1428d66047Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1528d66047Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1628d66047Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1728d66047Sclaudio  */
1828d66047Sclaudio 
1928d66047Sclaudio #include <stdint.h>
2028d66047Sclaudio #include <stdlib.h>
2128d66047Sclaudio #include <string.h>
2228d66047Sclaudio 
2328d66047Sclaudio #include "bgpd.h"
2428d66047Sclaudio #include "rde.h"
2528d66047Sclaudio 
26*c0c9c169Sclaudio enum cp_state {
27*c0c9c169Sclaudio 	UNKNOWN,
28*c0c9c169Sclaudio 	NOT_PROVIDER,
29*c0c9c169Sclaudio 	PROVIDER,
30*c0c9c169Sclaudio };
3128d66047Sclaudio 
3228d66047Sclaudio struct rde_aspa_set {
3328d66047Sclaudio 	uint32_t		 as;
3428d66047Sclaudio 	uint32_t		 num;
3528d66047Sclaudio 	uint32_t		*pas;
3628d66047Sclaudio 	int			 next;
3728d66047Sclaudio };
3828d66047Sclaudio 
3928d66047Sclaudio /*
4028d66047Sclaudio  * Power of 2 hash table
4128d66047Sclaudio  * The nodes are stored in the sets array.
4228d66047Sclaudio  * Additonal data for the rde_aspa_set are stored in data.
4328d66047Sclaudio  * For lookups only table and mask need to be accessed.
4428d66047Sclaudio  */
4528d66047Sclaudio struct rde_aspa {
4628d66047Sclaudio 	struct rde_aspa_set	**table;
4728d66047Sclaudio 	uint32_t		  mask;
4828d66047Sclaudio 	uint32_t		  maxset;
4928d66047Sclaudio 	struct rde_aspa_set	 *sets;
5028d66047Sclaudio 	uint32_t		 *data;
5128d66047Sclaudio 	size_t			  maxdata;
5228d66047Sclaudio 	size_t			  curdata;
5328d66047Sclaudio 	uint32_t		  curset;
54d7e93531Sclaudio 	time_t			  lastchange;
5528d66047Sclaudio };
5628d66047Sclaudio 
5728d66047Sclaudio struct aspa_state {
5828d66047Sclaudio 	int	nhops;
5928d66047Sclaudio 	int	nup_p;
6028d66047Sclaudio 	int	nup_u;
6128d66047Sclaudio 	int	nup_np;
6228d66047Sclaudio 	int	ndown_p;
6328d66047Sclaudio 	int	ndown_u;
6428d66047Sclaudio 	int	ndown_np;
6528d66047Sclaudio };
6628d66047Sclaudio 
6728d66047Sclaudio /*
6828d66047Sclaudio  * Use fmix32() the finalization mix of MurmurHash3 as a 32bit hash function.
6928d66047Sclaudio  */
7028d66047Sclaudio static inline uint32_t
hash(uint32_t h)7128d66047Sclaudio hash(uint32_t h)
7228d66047Sclaudio {
7328d66047Sclaudio 	h ^= h >> 16;
7428d66047Sclaudio 	h *= 0x85ebca6b;
7528d66047Sclaudio 	h ^= h >> 13;
7628d66047Sclaudio 	h *= 0xc2b2ae35;
7728d66047Sclaudio 	h ^= h >> 16;
7828d66047Sclaudio 	return h;
7928d66047Sclaudio }
8028d66047Sclaudio 
8128d66047Sclaudio /*
8228d66047Sclaudio  * Lookup an asnum in the aspa hash table.
8328d66047Sclaudio  */
8428d66047Sclaudio static struct rde_aspa_set *
aspa_lookup(struct rde_aspa * ra,uint32_t asnum)8528d66047Sclaudio aspa_lookup(struct rde_aspa *ra, uint32_t asnum)
8628d66047Sclaudio {
8728d66047Sclaudio 	struct rde_aspa_set *aspa;
8828d66047Sclaudio 	uint32_t h;
8928d66047Sclaudio 
9028d66047Sclaudio 	h = hash(asnum) & ra->mask;
9128d66047Sclaudio 	aspa = ra->table[h];
9228d66047Sclaudio 	if (aspa == NULL)
9328d66047Sclaudio 		return NULL;
9428d66047Sclaudio 
9528d66047Sclaudio 	while (aspa->as < asnum) {
9628d66047Sclaudio 		if (!aspa->next)
9728d66047Sclaudio 			break;
9828d66047Sclaudio 		aspa++;
9928d66047Sclaudio 	}
10028d66047Sclaudio 
10128d66047Sclaudio 	if (aspa->as == asnum)
10228d66047Sclaudio 		return aspa;
10328d66047Sclaudio 	return NULL;
10428d66047Sclaudio }
10528d66047Sclaudio 
10628d66047Sclaudio /*
10728d66047Sclaudio  * Lookup if there is a customer - provider relation between cas and pas.
10828d66047Sclaudio  * Returns UNKNOWN if cas is not in the ra table or the aid is out of range.
10928d66047Sclaudio  * Returns PROVIDER if pas is registered for cas for the specified aid.
11028d66047Sclaudio  * Retruns NOT_PROVIDER otherwise.
11128d66047Sclaudio  * This function is called very frequently and needs to be fast.
11228d66047Sclaudio  */
113*c0c9c169Sclaudio static enum cp_state
aspa_cp_lookup(struct rde_aspa * ra,uint32_t cas,uint32_t pas)114f8fade75Sclaudio aspa_cp_lookup(struct rde_aspa *ra, uint32_t cas, uint32_t pas)
11528d66047Sclaudio {
11628d66047Sclaudio 	struct rde_aspa_set *aspa;
117f8fade75Sclaudio 	uint32_t i;
11828d66047Sclaudio 
11928d66047Sclaudio 	aspa = aspa_lookup(ra, cas);
12028d66047Sclaudio 	if (aspa == NULL)
121*c0c9c169Sclaudio 		return UNKNOWN;
12228d66047Sclaudio 
12328d66047Sclaudio 	if (aspa->num < 16) {
12428d66047Sclaudio 		for (i = 0; i < aspa->num; i++) {
12528d66047Sclaudio 			if (aspa->pas[i] == pas)
12628d66047Sclaudio 				break;
12728d66047Sclaudio 			if (aspa->pas[i] > pas)
128*c0c9c169Sclaudio 				return NOT_PROVIDER;
12928d66047Sclaudio 		}
13028d66047Sclaudio 		if (i == aspa->num)
131*c0c9c169Sclaudio 			return NOT_PROVIDER;
13228d66047Sclaudio 	} else {
13328d66047Sclaudio 		uint32_t lim, x;
13428d66047Sclaudio 		for (i = 0, lim = aspa->num; lim != 0; lim /= 2) {
13528d66047Sclaudio 			x = lim / 2;
13628d66047Sclaudio 			i += x;
13728d66047Sclaudio 			if (aspa->pas[i] == pas) {
13828d66047Sclaudio 				break;
13928d66047Sclaudio 			} else if (aspa->pas[i] < pas) {
14028d66047Sclaudio 				/* move right */
14128d66047Sclaudio 				i++;
14228d66047Sclaudio 				lim--;
14328d66047Sclaudio 			} else {
14428d66047Sclaudio 				/* move left */
14528d66047Sclaudio 				i -= x;
14628d66047Sclaudio 			}
14728d66047Sclaudio 		}
14828d66047Sclaudio 		if (lim == 0)
149*c0c9c169Sclaudio 			return NOT_PROVIDER;
15028d66047Sclaudio 	}
15128d66047Sclaudio 
152*c0c9c169Sclaudio 	return PROVIDER;
15328d66047Sclaudio }
15428d66047Sclaudio 
15528d66047Sclaudio /*
15628d66047Sclaudio  * Calculate the various indexes of an aspath.
15728d66047Sclaudio  * The up-ramp starts at the source-as which is the right-most / last element
15828d66047Sclaudio  * in the aspath. The down-ramp starts at index 1.
15928d66047Sclaudio  * nhops: number of unique hops in the path
16028d66047Sclaudio  * nup_p: smallest index after which all aspath hops are provider valid
16128d66047Sclaudio  * nup_u: The biggest index of an unknown aspath hop.
16228d66047Sclaudio  * nup_np: The biggest index of a not-provider aspath hop.
16328d66047Sclaudio  * ndown_p: biggest index before which all aspath hops are provider valid
16428d66047Sclaudio  * ndown_u: The smallest index of an unknown aspath hop.
16528d66047Sclaudio  * ndown_np: The smallest index of a not-provider aspath hop.
16628d66047Sclaudio  * Returns 0 on success and -1 if a AS_SET is encountered.
16728d66047Sclaudio  */
16828d66047Sclaudio static int
aspa_check_aspath(struct rde_aspa * ra,struct aspath * a,struct aspa_state * s)169f8fade75Sclaudio aspa_check_aspath(struct rde_aspa *ra, struct aspath *a, struct aspa_state *s)
17028d66047Sclaudio {
17128d66047Sclaudio 	uint8_t		*seg;
17228d66047Sclaudio 	uint32_t	 as, prevas = 0;
17328d66047Sclaudio 	uint16_t	 len, seg_size;
174*c0c9c169Sclaudio 	uint8_t		 i, seg_type, seg_len;
17528d66047Sclaudio 
17628d66047Sclaudio 	/* the neighbor-as itself is by definition valid */
177*c0c9c169Sclaudio 	s->ndown_p = 1;
17828d66047Sclaudio 
17928d66047Sclaudio 	/*
18028d66047Sclaudio 	 * Walk aspath and validate if necessary both up- and down-ramp.
181f8fade75Sclaudio 	 * If an AS_SET is found return -1 to indicate failure.
18228d66047Sclaudio 	 */
18328d66047Sclaudio 	seg = aspath_dump(a);
18428d66047Sclaudio 	len = aspath_length(a);
18528d66047Sclaudio 	for (; len > 0; len -= seg_size, seg += seg_size) {
18628d66047Sclaudio 		seg_type = seg[0];
18728d66047Sclaudio 		seg_len = seg[1];
18828d66047Sclaudio 		seg_size = 2 + sizeof(uint32_t) * seg_len;
18928d66047Sclaudio 
190f8fade75Sclaudio 		if (seg_type != AS_SEQUENCE)
19128d66047Sclaudio 			return -1;
19228d66047Sclaudio 
19328d66047Sclaudio 		for (i = 0; i < seg_len; i++) {
19428d66047Sclaudio 			as = aspath_extract(seg, i);
19528d66047Sclaudio 
19628d66047Sclaudio 			if (as == prevas)
19728d66047Sclaudio 				continue; /* skip prepends */
19828d66047Sclaudio 
199*c0c9c169Sclaudio 			s->nhops++;
200f8fade75Sclaudio 			if (prevas == 0) {
201f8fade75Sclaudio 				prevas = as; /* skip left-most AS */
202f8fade75Sclaudio 				continue;
203f8fade75Sclaudio 			}
204f8fade75Sclaudio 
20528d66047Sclaudio 			/*
20628d66047Sclaudio 			 * down-ramp check, remember the
20728d66047Sclaudio 			 * left-most unknown or not-provider
20828d66047Sclaudio 			 * node and the right-most provider node
20928d66047Sclaudio 			 * for which all nodes before are valid.
21028d66047Sclaudio 			 */
211*c0c9c169Sclaudio 			switch (aspa_cp_lookup(ra, prevas, as)) {
21228d66047Sclaudio 			case UNKNOWN:
213*c0c9c169Sclaudio 				if (s->ndown_u == 0)
214*c0c9c169Sclaudio 					s->ndown_u = s->nhops;
21528d66047Sclaudio 				break;
21628d66047Sclaudio 			case PROVIDER:
217*c0c9c169Sclaudio 				if (s->ndown_p + 1 == s->nhops)
218*c0c9c169Sclaudio 					s->ndown_p = s->nhops;
21928d66047Sclaudio 				break;
22028d66047Sclaudio 			case NOT_PROVIDER:
221*c0c9c169Sclaudio 				if (s->ndown_np == 0)
222*c0c9c169Sclaudio 					s->ndown_np = s->nhops;
22328d66047Sclaudio 				break;
22428d66047Sclaudio 			}
225f8fade75Sclaudio 
22628d66047Sclaudio 			/*
22728d66047Sclaudio 			 * up-ramp check, remember the right-most
22828d66047Sclaudio 			 * unknown and not-provider node and the
22928d66047Sclaudio 			 * left-most provider node for which all nodes
23028d66047Sclaudio 			 * after are valid.
23128d66047Sclaudio 			 * We recorde the nhops value of prevas,
23228d66047Sclaudio 			 * that's why the use of nhops - 1.
23328d66047Sclaudio 			 */
234*c0c9c169Sclaudio 			switch (aspa_cp_lookup(ra, as, prevas)) {
23528d66047Sclaudio 			case UNKNOWN:
236*c0c9c169Sclaudio 				s->nup_p = 0;
237*c0c9c169Sclaudio 				s->nup_u = s->nhops - 1;
23828d66047Sclaudio 				break;
23928d66047Sclaudio 			case PROVIDER:
240*c0c9c169Sclaudio 				if (s->nup_p == 0)
241*c0c9c169Sclaudio 					s->nup_p = s->nhops - 1;
24228d66047Sclaudio 				break;
24328d66047Sclaudio 			case NOT_PROVIDER:
244*c0c9c169Sclaudio 				s->nup_p = 0;
245*c0c9c169Sclaudio 				s->nup_np = s->nhops - 1;
24628d66047Sclaudio 				break;
24728d66047Sclaudio 			}
24828d66047Sclaudio 			prevas = as;
24928d66047Sclaudio 		}
25028d66047Sclaudio 	}
25128d66047Sclaudio 
25228d66047Sclaudio 	/* the source-as itself is by definition valid */
253*c0c9c169Sclaudio 	if (s->nup_p == 0)
254*c0c9c169Sclaudio 		s->nup_p = s->nhops;
25528d66047Sclaudio 	return 0;
25628d66047Sclaudio }
25728d66047Sclaudio 
25828d66047Sclaudio /*
259f8fade75Sclaudio  * Set the two possible aspa outcomes for up-ramp only and up/down ramp
260f8fade75Sclaudio  * in the vstate array.
26128d66047Sclaudio  */
262f8fade75Sclaudio static void
aspa_check_finalize(struct aspa_state * state,uint8_t * onlyup,uint8_t * downup)263f8fade75Sclaudio aspa_check_finalize(struct aspa_state *state, uint8_t *onlyup, uint8_t *downup)
26428d66047Sclaudio {
26528d66047Sclaudio 	/*
26628d66047Sclaudio 	 * Just an up-ramp:
26728d66047Sclaudio 	 * if a check returned NOT_PROVIDER then the result is invalid.
26828d66047Sclaudio 	 * if a check returned UNKNOWN then the result is unknown.
26928d66047Sclaudio 	 * else path is valid.
27028d66047Sclaudio 	 */
271f8fade75Sclaudio 	if (state->nup_np != 0)
272f8fade75Sclaudio 		*onlyup = ASPA_INVALID;
273f8fade75Sclaudio 	else if (state->nup_u != 0)
274f8fade75Sclaudio 		*onlyup = ASPA_UNKNOWN;
275f8fade75Sclaudio 	else
276f8fade75Sclaudio 		*onlyup = ASPA_VALID;
277f8fade75Sclaudio 
27828d66047Sclaudio 	/*
27928d66047Sclaudio 	 * Both up-ramp and down-ramp:
28028d66047Sclaudio 	 * if nhops <= 2 the result is valid.
28128d66047Sclaudio 	 * if there is less than one AS hop between up-ramp and
28228d66047Sclaudio 	 *   down-ramp then the result is valid.
28328d66047Sclaudio 	 * if not-provider nodes for both ramps exist and they
28428d66047Sclaudio 	 *   do not overlap the path is invalid.
28528d66047Sclaudio 	 * else the path is unknown.
28628d66047Sclaudio 	 */
287f8fade75Sclaudio 	if (state->nhops <= 2)
288f8fade75Sclaudio 		*downup = ASPA_VALID;
289f8fade75Sclaudio 	else if (state->nup_p - state->ndown_p <= 1)
290f8fade75Sclaudio 		*downup = ASPA_VALID;
291f8fade75Sclaudio 	else if (state->nup_np != 0 && state->ndown_np != 0 &&
292f8fade75Sclaudio 	    state->nup_np - state->ndown_np >= 0)
293f8fade75Sclaudio 		*downup = ASPA_INVALID;
294f8fade75Sclaudio 	else
295f8fade75Sclaudio 		*downup = ASPA_UNKNOWN;
29628d66047Sclaudio }
297f8fade75Sclaudio 
298f8fade75Sclaudio /*
299f8fade75Sclaudio  * Validate an aspath against the aspa_set *ra.
300f8fade75Sclaudio  * Returns ASPA_VALID if the aspath is valid, ASPA_UNKNOWN if the
301*c0c9c169Sclaudio  * aspath contains hops with unknown relation and ASPA_INVALID for
302f8fade75Sclaudio  * empty aspaths, aspath with AS_SET and aspaths that fail validation.
303f8fade75Sclaudio  */
304f8fade75Sclaudio void
aspa_validation(struct rde_aspa * ra,struct aspath * a,struct rde_aspa_state * vstate)305f8fade75Sclaudio aspa_validation(struct rde_aspa *ra, struct aspath *a,
306f8fade75Sclaudio     struct rde_aspa_state *vstate)
307f8fade75Sclaudio {
308*c0c9c169Sclaudio 	struct aspa_state state = { 0 };
309f8fade75Sclaudio 
310f8fade75Sclaudio 	/* no aspa table, evrything is unknown */
311f8fade75Sclaudio 	if (ra == NULL) {
312*c0c9c169Sclaudio 		memset(vstate, ASPA_UNKNOWN, sizeof(*vstate));
313f8fade75Sclaudio 		return;
314f8fade75Sclaudio 	}
315f8fade75Sclaudio 
316f8fade75Sclaudio 	/* empty ASPATHs are always invalid */
317f8fade75Sclaudio 	if (aspath_length(a) == 0) {
318*c0c9c169Sclaudio 		memset(vstate, ASPA_INVALID, sizeof(*vstate));
319f8fade75Sclaudio 		return;
320f8fade75Sclaudio 	}
321f8fade75Sclaudio 
322*c0c9c169Sclaudio 	if (aspa_check_aspath(ra, a, &state) == -1) {
323*c0c9c169Sclaudio 		memset(vstate, ASPA_INVALID, sizeof(*vstate));
324f8fade75Sclaudio 		return;
325f8fade75Sclaudio 	}
326f8fade75Sclaudio 
327*c0c9c169Sclaudio 	aspa_check_finalize(&state, &vstate->onlyup, &vstate->downup);
32828d66047Sclaudio }
32928d66047Sclaudio 
33028d66047Sclaudio /*
33128d66047Sclaudio  * Preallocate all data structures needed for the aspa table.
33228d66047Sclaudio  * There are entries number of rde_aspa_sets with data_size bytes of
333d7e93531Sclaudio  * extra data (used to store SPAS and optional AFI bitmasks).
33428d66047Sclaudio  */
33528d66047Sclaudio struct rde_aspa *
aspa_table_prep(uint32_t entries,size_t datasize)336d7e93531Sclaudio aspa_table_prep(uint32_t entries, size_t datasize)
33728d66047Sclaudio {
33828d66047Sclaudio 	struct rde_aspa *ra;
33928d66047Sclaudio 	uint32_t hsize = 1024;
34028d66047Sclaudio 
34128d66047Sclaudio 	if (entries == 0)
34228d66047Sclaudio 		return NULL;
34328d66047Sclaudio 	if (entries > UINT32_MAX / 2)
34428d66047Sclaudio 		fatalx("aspa_table_prep overflow");
34528d66047Sclaudio 
34628d66047Sclaudio 	while (hsize < entries)
34728d66047Sclaudio 		hsize *= 2;
34828d66047Sclaudio 
34928d66047Sclaudio 	if ((ra = calloc(1, sizeof(*ra))) == NULL)
35028d66047Sclaudio 		fatal("aspa table prep");
35128d66047Sclaudio 
35228d66047Sclaudio 	if ((ra->table = calloc(hsize, sizeof(ra->table[0]))) == NULL)
35328d66047Sclaudio 		fatal("aspa table prep");
35428d66047Sclaudio 
35528d66047Sclaudio 	if ((ra->sets = calloc(entries, sizeof(ra->sets[0]))) == NULL)
35628d66047Sclaudio 		fatal("aspa table prep");
35728d66047Sclaudio 
358d7e93531Sclaudio 	if ((ra->data = malloc(datasize)) == NULL)
35928d66047Sclaudio 		fatal("aspa table prep");
36028d66047Sclaudio 
36128d66047Sclaudio 	ra->mask = hsize - 1;
36228d66047Sclaudio 	ra->maxset = entries;
363d7e93531Sclaudio 	ra->maxdata = datasize / sizeof(ra->data[0]);
364d7e93531Sclaudio 	ra->lastchange = getmonotime();
36528d66047Sclaudio 
36628d66047Sclaudio 	return ra;
36728d66047Sclaudio }
36828d66047Sclaudio 
36928d66047Sclaudio /*
37028d66047Sclaudio  * Insert an aspa customer/provider set into the hash table.
37128d66047Sclaudio  * For hash conflict resulution insertion must happen in reverse order (biggest
37228d66047Sclaudio  * customer asnum first). On conflict objects in the sets array are moved
37328d66047Sclaudio  * around so that conflicting elements are direct neighbors.
37428d66047Sclaudio  * The per AID information is (if required) stored as 2bits per provider.
37528d66047Sclaudio  */
37628d66047Sclaudio void
aspa_add_set(struct rde_aspa * ra,uint32_t cas,const uint32_t * pas,uint32_t pascnt)37728d66047Sclaudio aspa_add_set(struct rde_aspa *ra, uint32_t cas, const uint32_t *pas,
378*c0c9c169Sclaudio     uint32_t pascnt)
37928d66047Sclaudio {
38028d66047Sclaudio 	struct rde_aspa_set *aspa;
38128d66047Sclaudio 	uint32_t h, i;
38228d66047Sclaudio 
38328d66047Sclaudio 	if (ra->curset >= ra->maxset)
38428d66047Sclaudio 		fatalx("aspa set overflow");
38528d66047Sclaudio 
38628d66047Sclaudio 	h = hash(cas) & ra->mask;
38728d66047Sclaudio 	aspa = ra->table[h];
38828d66047Sclaudio 	if (aspa == NULL) {
38928d66047Sclaudio 		aspa = &ra->sets[ra->curset++];
39028d66047Sclaudio 	} else {
39128d66047Sclaudio 		if (aspa->as <= cas)
39228d66047Sclaudio 			fatalx("%s: bad order of adds", __func__);
39328d66047Sclaudio 
39428d66047Sclaudio 		/* insert before aspa */
39528d66047Sclaudio 		memmove(aspa + 1, aspa,
39628d66047Sclaudio 		    (ra->sets + ra->curset - aspa) * sizeof(*aspa));
39728d66047Sclaudio 		ra->curset++;
39828d66047Sclaudio 		memset(aspa, 0, sizeof(*aspa));
39928d66047Sclaudio 		aspa->next = 1;
40028d66047Sclaudio 
40128d66047Sclaudio 		/* adjust hashtable after shift of elements */
40228d66047Sclaudio 		for (i = 0; i <= ra->mask; i++) {
40328d66047Sclaudio 			if (ra->table[i] > aspa)
40428d66047Sclaudio 				ra->table[i]++;
40528d66047Sclaudio 		}
40628d66047Sclaudio 	}
40728d66047Sclaudio 	aspa->as = cas;
40828d66047Sclaudio 	ra->table[h] = aspa;
40928d66047Sclaudio 
41028d66047Sclaudio 	if (ra->maxdata - ra->curdata < pascnt)
41128d66047Sclaudio 		fatalx("aspa set data overflow");
41228d66047Sclaudio 
41328d66047Sclaudio 	aspa->num = pascnt;
41428d66047Sclaudio 	aspa->pas = ra->data + ra->curdata;
41528d66047Sclaudio 	for (i = 0; i < pascnt; i++)
41628d66047Sclaudio 		ra->data[ra->curdata++] = pas[i];
41728d66047Sclaudio }
41828d66047Sclaudio 
41928d66047Sclaudio void
aspa_table_free(struct rde_aspa * ra)42028d66047Sclaudio aspa_table_free(struct rde_aspa *ra)
42128d66047Sclaudio {
42228d66047Sclaudio 	if (ra == NULL)
42328d66047Sclaudio 		return;
42428d66047Sclaudio 	free(ra->table);
42528d66047Sclaudio 	free(ra->sets);
42628d66047Sclaudio 	free(ra->data);
42728d66047Sclaudio 	free(ra);
42828d66047Sclaudio }
429d7e93531Sclaudio 
430d7e93531Sclaudio void
aspa_table_stats(const struct rde_aspa * ra,struct ctl_show_set * cset)431d7e93531Sclaudio aspa_table_stats(const struct rde_aspa *ra, struct ctl_show_set *cset)
432d7e93531Sclaudio {
433d7e93531Sclaudio 	if (ra == NULL)
434d7e93531Sclaudio 		return;
435d7e93531Sclaudio 	cset->lastchange = ra->lastchange;
436d7e93531Sclaudio 	cset->as_cnt = ra->maxset;
437d7e93531Sclaudio }
438d7e93531Sclaudio 
439d7e93531Sclaudio /*
440*c0c9c169Sclaudio  * Return true if the two rde_aspa tables contain the same data.
441d7e93531Sclaudio  */
442d7e93531Sclaudio int
aspa_table_equal(const struct rde_aspa * ra,const struct rde_aspa * rb)443d7e93531Sclaudio aspa_table_equal(const struct rde_aspa *ra, const struct rde_aspa *rb)
444d7e93531Sclaudio {
445d7e93531Sclaudio 	uint32_t i;
446d7e93531Sclaudio 
447d7e93531Sclaudio 	/* allow NULL pointers to be passed */
448d7e93531Sclaudio 	if (ra == NULL && rb == NULL)
449d7e93531Sclaudio 		return 1;
450d7e93531Sclaudio 	if (ra == NULL || rb == NULL)
451d7e93531Sclaudio 		return 0;
452d7e93531Sclaudio 
453d7e93531Sclaudio 	if (ra->maxset != rb->maxset ||
454d7e93531Sclaudio 	    ra->maxdata != rb->maxdata)
455d7e93531Sclaudio 		return 0;
456d7e93531Sclaudio 	for (i = 0; i < ra->maxset; i++)
457d7e93531Sclaudio 		if (ra->sets[i].as != rb->sets[i].as)
458d7e93531Sclaudio 			return 0;
459d7e93531Sclaudio 	if (memcmp(ra->data, rb->data, ra->maxdata * sizeof(ra->data[0])) != 0)
460d7e93531Sclaudio 		return 0;
461d7e93531Sclaudio 
462d7e93531Sclaudio 	return 1;
463d7e93531Sclaudio }
464d7e93531Sclaudio 
465d7e93531Sclaudio void
aspa_table_unchanged(struct rde_aspa * ra,const struct rde_aspa * old)466d7e93531Sclaudio aspa_table_unchanged(struct rde_aspa *ra, const struct rde_aspa *old)
467d7e93531Sclaudio {
468d7e93531Sclaudio 	if (ra == NULL || old == NULL)
469d7e93531Sclaudio 		return;
470d7e93531Sclaudio 	ra->lastchange = old->lastchange;
471d7e93531Sclaudio }
472