TRIP Routing Daemon
TRIP (RFC 3219) Location Server Implementation
Loading...
Searching...
No Matches
trib.c
Go to the documentation of this file.
1/*
2
3 trip: Modern TRIP LS implementation
4 Copyright (C) 2025 arf20 (Ángel Ruiz Fernandez)
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19 trib.c: Telephony Routing Information Bases
20
21*/
22
27
28#include "trib.h"
29#include "db/pib.h"
30#include "protocol/protocol.h"
31
32#define _COMPONENT_ "db"
33
34#include <logging/logging.h>
35
36#include <stdlib.h>
37#include <string.h>
38
39#define INIT_TABLE_CAPACITY 256
40#define INIT_ADJ_TRIBS_CAPACITY 32
41
42static trib_t g_trib = { 0 };
43
44
45entry_t *
46entry_clone(const entry_t *entry)
47{
48 entry_t *ne = malloc(sizeof(entry_t));
49 /* shallow copy */
50 *ne = *entry;
51 /* deep copy */
52 ne->attrs = entry->attrs;
53 ne->prefix = strdup(entry->prefix);
54 ne->attrs.nexthop = strdup(entry->attrs.nexthop);
55 if (ATTR_IS_USED_ADVERTPATH(entry->attrs.use)) {
56 ne->attrs.advertpath = malloc(sizeof(uint32_t)
57 * entry->attrs.advertpath_size);
58 memcpy(ne->attrs.advertpath, entry->attrs.advertpath, sizeof(uint32_t)
59 * entry->attrs.advertpath_size);
60 } else ne->attrs.advertpath = NULL;
61 if (ATTR_IS_USED_ROUTEDPATH(entry->attrs.use)) {
62 ne->attrs.routedpath = malloc(sizeof(uint32_t)
63 * entry->attrs.routedpath_size);
64 memcpy(ne->attrs.routedpath, entry->attrs.routedpath, sizeof(uint32_t)
65 * entry->attrs.routedpath_size);
66 } else ne->attrs.routedpath = NULL;
67 if (ATTR_IS_USED_COMMUNITIES(entry->attrs.use)) {
68 ne->attrs.communities = malloc(sizeof(uint32_t)
69 * entry->attrs.communities_size);
70 memcpy(ne->attrs.communities, entry->attrs.communities,
71 sizeof(community_t) * entry->attrs.communities_size);
72 } else ne->attrs.communities = NULL;
73 return ne;
74}
75
76void
78{
79 free(entry->prefix);
80 free(entry->attrs.nexthop);
81 if (ATTR_IS_USED_ADVERTPATH(entry->attrs.use))
82 free(entry->attrs.advertpath);
83 if (ATTR_IS_USED_ROUTEDPATH(entry->attrs.use))
84 free(entry->attrs.routedpath);
85 if (ATTR_IS_USED_COMMUNITIES(entry->attrs.use))
86 free(entry->attrs.communities);
87 free(entry);
88}
89
90static void
91table_init(table_t *t)
92{
93 t->capacity = INIT_TABLE_CAPACITY;
94 t->size = 0;
95 t->table = malloc(t->capacity * sizeof(entry_t*));
96 t->routemap = NULL;
97}
98
99static void
100table_copy(table_t *dst, table_t *src)
101{
102 for (size_t i = 0; i < src->size; i++)
103 trib_table_insert(dst, entry_clone(src->table[i]));
104}
105
106void
108{
109 for (size_t i = 0; i < t->size; i++)
110 entry_destroy(t->table[i]);
111 free(t->table);
112}
113
114trib_t *
115trib_new(uint32_t local_itad)
116{
117 if (g_trib.local_routes.table)
118 return NULL;
119
120 trib_t *t = &g_trib;
121
122 t->local_itad = local_itad;
123
124 table_init(&t->local_routes);
125 table_init(&t->ext_trib);
126 table_init(&t->loc_trib);
127 table_init(&t->optimized_loc_trib);
128
129 t->adj_tribs_capacity = INIT_ADJ_TRIBS_CAPACITY;
130 t->adj_tribs_size = 0;
131 t->adj_tribs_in = malloc(t->adj_tribs_capacity * sizeof(table_t));
132 t->adj_tribs_out = malloc(t->adj_tribs_capacity * sizeof(table_t));
133
134 return t;
135}
136
137void
139{
140 if (trib->adj_tribs_capacity < trib->adj_tribs_size + 1) {
141 trib->adj_tribs_capacity *= 2;
142 trib->adj_tribs_in = realloc(trib->adj_tribs_in,
143 trib->adj_tribs_capacity * sizeof(table_t));
144 trib->adj_tribs_out = realloc(trib->adj_tribs_out,
145 trib->adj_tribs_capacity * sizeof(table_t));
146 }
147
148 trib->adj_tribs_in[trib->adj_tribs_size] = in;
149 trib->adj_tribs_out[trib->adj_tribs_size] = out;
150 trib->adj_tribs_size++;
151
152 table_init(in);
153 table_init(out);
154}
155
156void
157trib_adj_pair_remove(trib_t *trib, table_t *in, table_t *out)
158{
159 for (size_t i = 0; i < trib->adj_tribs_size; i++) {
160 if (trib->adj_tribs_in[i] == in && trib->adj_tribs_out[i] == out) {
163 memmove(&trib->adj_tribs_in[i], &trib->adj_tribs_in[i+1],
164 sizeof(table_t*) * (trib->adj_tribs_size - i));
165 memmove(&trib->adj_tribs_out[i], &trib->adj_tribs_out[i+1],
166 sizeof(table_t*) * (trib->adj_tribs_size - i));
167 trib->adj_tribs_size--;
168 return;
169 }
170 }
171}
172
173void
175{
176 trib_table_deinit(&trib->local_routes);
177 trib_table_deinit(&trib->ext_trib);
178 trib_table_deinit(&trib->loc_trib);
179 trib_table_deinit(&trib->optimized_loc_trib);
180 free(trib->adj_tribs_in);
181 free(trib->adj_tribs_out);
182}
183
184
185void
187{
188 if (table->capacity < table->size + 1) {
189 table->capacity *= 2;
190 table->table = realloc(table->table,
191 table->capacity * sizeof(entry_t*));
192 }
193
194 table->table[table->size++] = entry;
195}
196
197void
199{
200 for (size_t i = 0; i < table->size; i++)
201 entry_destroy(table->table[i]);
202 table->size = 0;
203}
204
205
218static int
219entry_compare(const entry_t *e1, const entry_t *e2, uint32_t itad)
220{
221 if (e1->attrs.local_pref != e2->attrs.local_pref)
222 return e1->attrs.local_pref < e2->attrs.local_pref;
223 if (e1->type != e2->type)
224 return e1->type < e2->type;
225 if (e1->attrs.routedpath_size != e2->attrs.routedpath_size)
226 return e1->attrs.routedpath_size < e2->attrs.routedpath_size;
227 if (e1->attrs.metric != e2->attrs.metric)
228 return e1->attrs.metric < e2->attrs.metric;
229 if ((e1->learn_itad == itad) != (e2->learn_itad == itad))
230 return e2->learn_itad == itad;
231 if (e1->time != e2->time)
232 return e1->time > e2->time;
233 if (e1->learn_lsid != e2->learn_lsid)
234 return e1->learn_lsid < e2->learn_lsid;
235 INFO("identical route preference for %s", e1->prefix);
236 return 0;
237}
238
239static entry_t **
240table_find(table_t *t, uint16_t af, const char *prefix)
241{
242 for (size_t i = 0; i < t->size; i++)
243 if (t->table[i]->af == af && strcmp(t->table[i]->prefix, prefix) == 0)
244 return &t->table[i];
245 return NULL;
246}
247
257static void
258table_select_into(table_t *t1, table_t *t2, uint32_t itad)
259{
260 for (size_t i = 0; i < t2->size; i++) {
261 entry_t **match = table_find(t1, t2->table[i]->af, t2->table[i]->prefix);
262 if (!match) {
263 trib_table_insert(t1, entry_clone(t2->table[i]));
264 continue;
265 }
266
267 /* replace entry */
268 if (entry_compare(*match, t2->table[i], itad)) {
269 entry_destroy(*match);
270 *match = entry_clone(t2->table[i]);
271 }
272 }
273}
274
276static void
277optimize_table(table_t *dst, table_t *src)
278{
279 for (size_t i = 0; i < src->size; i++) {
280 /* TODO: */
281 trib_table_insert(dst, entry_clone(src->table[i]));
282 }
283}
284
286static void
287apply_policy(table_t *dst, const table_t *src, uint32_t local_itad,
288 const routemap_t *policy)
289{
290 for (size_t i = 0; i < src->size; i++) {
291 const routemap_statement_t *s =
292 routemap_match(policy, src->table[i]->prefix);
293 if (!s)
294 continue; /* default deny */
295
296 entry_t *e = entry_clone(src->table[i]);
297
298 for (size_t j = 0; j < s->actions_size; j++) {
299 switch (s->actions[j].attribute) {
302 e->attrs.local_pref = s->actions[j].value;
303 break;
305 free(e->attrs.nexthop);
306 e->attrs.nexthop = strdup(s->actions[j].valstr2);
307 break;
309 e->attrs.routedpath_size = s->actions[j].value
310 + e->attrs.routedpath_size;
311 e->attrs.routedpath = realloc(e->attrs.routedpath,
312 e->attrs.routedpath_size * sizeof(uint32_t));
313 for (size_t k = 0; k < s->actions[j].value; k++)
314 e->attrs.routedpath[k] = local_itad;
315 break;
316 }
317 }
318
319 trib_table_insert(dst, e);
320 }
321}
322
323
324void
326{
327 for (size_t i = 0; i < trib->local_routes.size; i++) {
328 entry_attrs_t *a = &trib->local_routes.table[i]->attrs;
329 if (ATTR_IS_USED_NEXTHOP(a->use))
330 a->nextitad = trib->local_itad;
331 if (ATTR_IS_USED_ADVERTPATH(a->use))
332 a->advertpath[0] = trib->local_itad;
333 if (ATTR_IS_USED_ROUTEDPATH(a->use))
334 a->routedpath[0] = trib->local_itad;
335 }
336}
337
338void
339trib_update_adj_out(trib_t *trib, table_t *adj_trib_out)
340{
341 trib_table_clear(adj_trib_out);
342 /* apply output policy if applicable */
343 if (adj_trib_out->routemap)
344 apply_policy(adj_trib_out, &trib->optimized_loc_trib, trib->local_itad,
345 adj_trib_out->routemap);
346 else
347 table_copy(adj_trib_out, &trib->optimized_loc_trib);
348}
349
350void
352{
353 trib_table_clear(&trib->ext_trib);
354 trib_table_clear(&trib->loc_trib);
355 trib_table_clear(&trib->optimized_loc_trib);
356
357 /* Phase 2a: local routes and external Ext-TRIBs-in to Ext-TRIB */
358 table_t scratch; /* temporary working table */
359 table_init(&scratch);
360
361 table_select_into(&trib->ext_trib, &trib->local_routes, trib->local_itad);
362 for (size_t i = 0; i < trib->adj_tribs_size; i++) {
363 if (trib->adj_tribs_in[i]->peer_itad != trib->local_itad) {
364 /* apply input policy if applicable */
365 if (trib->adj_tribs_in[i]->routemap) {
366 trib_table_clear(&scratch);
367 apply_policy(&scratch, trib->adj_tribs_in[i], trib->local_itad,
368 trib->adj_tribs_in[i]->routemap);
369 table_select_into(&trib->ext_trib, &scratch, trib->local_itad);
370 } else
371 table_select_into(&trib->ext_trib, trib->adj_tribs_in[i],
372 trib->local_itad);
373 }
374 }
375
376 /* Phase 2b: Ext-TRIB and internal Ext-TRIBs-in to Loc-TRIB */
377 table_select_into(&trib->loc_trib, &trib->ext_trib, trib->local_itad);
378 for (size_t i = 0; i < trib->adj_tribs_size; i++)
379 if (trib->adj_tribs_in[i]->peer_itad == trib->local_itad)
380 table_select_into(&trib->loc_trib, trib->adj_tribs_in[i],
381 trib->local_itad);
382
383 /* Phase 3: Loc-TRIB to Ext-TRIBs-Out */
384 /* optimize table */
385 optimize_table(&trib->optimized_loc_trib, &trib->loc_trib);
386
387 for (size_t i = 0; i < trib->adj_tribs_size; i++) {
389 trib_update_adj_out(trib, trib->adj_tribs_out[i]);
390 }
391
392 trib_table_deinit(&scratch);
393}
394
395
396size_t
397get_new_entries(const table_t *table, entry_t ***new_ents_out)
398{
399 size_t new_ents_size = 0, new_ents_capacity = INIT_TABLE_CAPACITY;
400 entry_t **new_ents = malloc(sizeof(entry_t*) * new_ents_capacity);
401
402 for (size_t i = 0; i < table->size; i++) {
403 if (table->table[i]->sent)
404 continue;
405
406 if (new_ents_size + 1 > new_ents_capacity) {
407 new_ents_capacity *= 2;
408 new_ents = realloc(new_ents, sizeof(entry_t*) * new_ents_capacity);
409 }
410
411 new_ents[new_ents_size++] = table->table[i];
412 }
413
414 *new_ents_out = new_ents;
415 return new_ents_size;
416}
417
419static int
420attrs_equals(const entry_attrs_t *a1, const entry_attrs_t *a2)
421{
422 /* group all withdrawn together */
423 if (a1->withdrawn && a2->withdrawn)
424 return 1;
425 if (a1->withdrawn || a2->withdrawn)
426 return 0;
427
428 if (!ATTR_IS_USED_NEXTHOP(a1->use) || !ATTR_IS_USED_NEXTHOP(a2->use)) {
429 ERROR("reachable route compared without nexthop");
430 return 0;
431 }
432
433 return
434 (strcmp(a1->nexthop, a2->nexthop) == 0) &&
435 ((!ATTR_IS_USED_ADVERTPATH(a1->use) || !ATTR_IS_USED_ADVERTPATH(a2->use)) ||
436 (a1->advertpath_size == a2->advertpath_size &&
437 memcmp(a1->advertpath, a2->advertpath,
438 sizeof(uint32_t) * a1->advertpath_size))) &&
439 ((!ATTR_IS_USED_ROUTEDPATH(a1->use) || !ATTR_IS_USED_ROUTEDPATH(a2->use)) ||
440 (a1->routedpath_size == a2->routedpath_size &&
441 memcmp(a1->routedpath, a2->routedpath,
442 sizeof(uint32_t) * a1->routedpath_size))) &&
443 ((!ATTR_IS_USED_LOCALPREF(a1->use) || !ATTR_IS_USED_LOCALPREF(a2->use)) ||
444 (a1->local_pref == a2->local_pref)) &&
445 ((!ATTR_IS_USED_METRIC(a1->use) || !ATTR_IS_USED_METRIC(a2->use)) ||
446 (a1->metric == a2->metric)) &&
447 ((!ATTR_IS_USED_COMMUNITIES(a1->use) || !ATTR_IS_USED_COMMUNITIES(a2->use)) ||
448 (a1->communities_size == a2->communities_size &&
449 memcmp(a1->communities, a2->communities,
450 sizeof(uint32_t) * a1->communities_size)));
451}
452
453static entry_group_t *
454is_entry_in_group(const entry_t *e, entry_group_t *groups,
455 size_t groups_size)
456{
457 for (size_t i = 0; i < groups_size; i++)
458 if (attrs_equals(&e->attrs, &groups[i].attrs))
459 return &groups[i];
460 return NULL;
461}
462
463static void
464group_init(entry_group_t *g, const entry_attrs_t *attrs)
465{
466 g->capacity = INIT_TABLE_CAPACITY;
467 g->size = 0;
468 g->entries = malloc(sizeof(entry_t*) * g->capacity);
469 g->attrs = *attrs;
470}
471
472static void
473group_insert(entry_group_t *g, entry_t *e)
474{
475 if (g->size + 1 > g->capacity) {
476 g->capacity *= 2;
477 g->entries = realloc(g->entries,
478 sizeof(entry_t*) * g->capacity);
479 }
480
481 g->entries[g->size++] = e;
482}
483
484size_t
485group_entries_by_attrs(entry_t **entries, size_t entry_size,
486 entry_group_t **groups_out)
487{
488
489 size_t groups_size = 0, groups_capacity = entry_size;
490 entry_group_t *groups = malloc(sizeof(entry_group_t) * groups_capacity);
491
492 for (size_t i = 0; i < entry_size; i++) {
493 entry_group_t *ingroup = is_entry_in_group(entries[i], groups,
494 groups_size);
495
496 if (!ingroup) {
497 if (groups_size + 1 > groups_capacity) {
498 groups_capacity *= 2;
499 groups = realloc(groups, sizeof(entry_group_t) * groups_capacity);
500 }
501
502 ingroup = &groups[groups_size++];
503 group_init(ingroup, &entries[i]->attrs);
504 }
505
506 group_insert(ingroup, entries[i]);
507 }
508
509
510 *groups_out = groups;
511 return groups_size;
512}
513
logging utilities
#define ERROR(format,...)
log an error
Definition logging.h:56
#define INFO(format,...)
log informational event
Definition logging.h:62
const routemap_statement_t * routemap_match(const routemap_t *routemap, const char *route)
Match route against route map matchers.
Definition pib.c:367
Policy Information Base.
@ ROUTEMAP_SET_METRIC
Definition pib.h:49
@ ROUTEMAP_SET_ITADPATH_PREPEND
Definition pib.h:51
@ ROUTEMAP_SET_NEXTHOP
Definition pib.h:50
@ ROUTEMAP_SET_LOCALPREF
Definition pib.h:48
Protocol definition header.
af
Address families.
Definition protocol.h:206
Groupable attributes that are related to a route.
Definition trib.h:51
char * nexthop
Definition trib.h:55
uint32_t local_pref
Definition trib.h:61
uint32_t nextitad
Definition trib.h:54
community_t * communities
Definition trib.h:63
uint32_t use
Definition trib.h:52
uint32_t * advertpath
Definition trib.h:56
int withdrawn
Definition trib.h:53
uint32_t metric
Definition trib.h:62
uint32_t * routedpath
Definition trib.h:58
Definition trib.h:107
entry_t ** entries
Definition trib.h:109
entry_attrs_t attrs
Definition trib.h:108
Route Entry.
Definition trib.h:75
int sent
Definition trib.h:95
uint16_t af
Definition trib.h:77
entry_type_t type
Definition trib.h:81
uint32_t learn_lsid
Definition trib.h:88
uint32_t learn_itad
Definition trib.h:86
entry_attrs_t attrs
Definition trib.h:93
time_t time
Definition trib.h:91
char * prefix
Definition trib.h:79
Route map statement.
Definition pib.h:69
Route map.
Definition pib.h:79
Route Table.
Definition trib.h:99
uint32_t peer_itad
Definition trib.h:100
routemap_t * routemap
Definition trib.h:103
Telephony Routing Information Base.
Definition trib.h:136
uint32_t local_itad
Definition trib.h:137
table_t ** adj_tribs_out
Definition trib.h:141
void trib_update_local(trib_t *trib)
Update local routes when ITAD is defined.
Definition trib.c:325
size_t get_new_entries(const table_t *table, entry_t ***new_ents_out)
Return array of new entry references to new.
Definition trib.c:397
size_t group_entries_by_attrs(entry_t **entries, size_t entry_size, entry_group_t **groups_out)
Group array of entry references by attributes.
Definition trib.c:485
void trib_destroy(trib_t *trib)
Deinit TRIB structure.
Definition trib.c:174
void trib_adj_pair_add(trib_t *trib, table_t *in, table_t *out)
Add and init pair of tables owned by caller.
Definition trib.c:138
void trib_table_insert(table_t *table, entry_t *entry)
Add route to table.
Definition trib.c:186
void trib_table_deinit(table_t *t)
Deinitialize table.
Definition trib.c:107
void entry_destroy(entry_t *entry)
Destroy entry.
Definition trib.c:77
void trib_update_adj_out(trib_t *trib, table_t *adj_trib_out)
Update an Adj-TRIB-Out.
Definition trib.c:339
trib_t * trib_new(uint32_t local_itad)
Initialize TRIB structure.
Definition trib.c:115
void trib_update_full(trib_t *trib)
Execute route selection.
Definition trib.c:351
void trib_table_clear(table_t *table)
Destroy all entries and clear table.
Definition trib.c:198
Telephony Routing Information Base.