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