iof-bird-daemon / nest / rt-roa.c @ 8e433d6a
History | View | Annotate | Download (9.88 KB)
1 | af582c48 | Ondrej Zajicek | /*
|
---|---|---|---|
2 | * BIRD -- Route Origin Authorization
|
||
3 | *
|
||
4 | *
|
||
5 | * Can be freely distributed and used under the terms of the GNU GPL.
|
||
6 | */
|
||
7 | |||
8 | #undef LOCAL_DEBUG
|
||
9 | |||
10 | #include "nest/bird.h" |
||
11 | #include "nest/route.h" |
||
12 | #include "nest/cli.h" |
||
13 | #include "lib/lists.h" |
||
14 | #include "lib/resource.h" |
||
15 | #include "lib/event.h" |
||
16 | #include "lib/string.h" |
||
17 | #include "conf/conf.h" |
||
18 | |||
19 | |||
20 | pool *roa_pool; |
||
21 | static slab *roa_slab; /* Slab of struct roa_item */ |
||
22 | static list roa_table_list; /* List of struct roa_table */ |
||
23 | struct roa_table *roa_table_default; /* The first ROA table in the config */ |
||
24 | |||
25 | static inline int |
||
26 | src_match(struct roa_item *it, byte src)
|
||
27 | { return !src || it->src == src; }
|
||
28 | |||
29 | /**
|
||
30 | * roa_add_item - add a ROA entry
|
||
31 | * @t: ROA table
|
||
32 | * @prefix: prefix of the ROA entry
|
||
33 | * @pxlen: prefix length of the ROA entry
|
||
34 | * @maxlen: max length field of the ROA entry
|
||
35 | * @asn: AS number field of the ROA entry
|
||
36 | * @src: source of the ROA entry (ROA_SRC_*)
|
||
37 | *
|
||
38 | * The function adds a new ROA entry to the ROA table. If the same ROA
|
||
39 | * is already in the table, nothing is added. @src field is used to
|
||
40 | * distinguish different sources of ROAs.
|
||
41 | */
|
||
42 | void
|
||
43 | roa_add_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen, u32 asn, byte src)
|
||
44 | { |
||
45 | struct roa_node *n = fib_get(&t->fib, &prefix, pxlen);
|
||
46 | |||
47 | // if ((n->items == NULL) && (n->n.x0 != ROA_INVALID))
|
||
48 | // t->cached_items--;
|
||
49 | |||
50 | struct roa_item *it;
|
||
51 | for (it = n->items; it; it = it->next)
|
||
52 | if ((it->maxlen == maxlen) && (it->asn == asn) && src_match(it, src))
|
||
53 | return;
|
||
54 | |||
55 | it = sl_alloc(roa_slab); |
||
56 | it->asn = asn; |
||
57 | it->maxlen = maxlen; |
||
58 | it->src = src; |
||
59 | it->next = n->items; |
||
60 | n->items = it; |
||
61 | } |
||
62 | |||
63 | /**
|
||
64 | * roa_delete_item - delete a ROA entry
|
||
65 | * @t: ROA table
|
||
66 | * @prefix: prefix of the ROA entry
|
||
67 | * @pxlen: prefix length of the ROA entry
|
||
68 | * @maxlen: max length field of the ROA entry
|
||
69 | * @asn: AS number field of the ROA entry
|
||
70 | * @src: source of the ROA entry (ROA_SRC_*)
|
||
71 | *
|
||
72 | * The function removes a specified ROA entry from the ROA table and
|
||
73 | * frees it. If @src field is not ROA_SRC_ANY, only entries from
|
||
74 | * that source are considered.
|
||
75 | */
|
||
76 | void
|
||
77 | roa_delete_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen, u32 asn, byte src)
|
||
78 | { |
||
79 | struct roa_node *n = fib_find(&t->fib, &prefix, pxlen);
|
||
80 | |||
81 | if (!n)
|
||
82 | return;
|
||
83 | |||
84 | struct roa_item *it, **itp;
|
||
85 | for (itp = &n->items; it = *itp; itp = &it->next)
|
||
86 | if ((it->maxlen == maxlen) && (it->asn == asn) && src_match(it, src))
|
||
87 | break;
|
||
88 | |||
89 | if (!it)
|
||
90 | return;
|
||
91 | |||
92 | *itp = it->next; |
||
93 | sl_free(roa_slab, it); |
||
94 | |||
95 | // if ((n->items == NULL) && (n->n.x0 != ROA_INVALID))
|
||
96 | // t->cached_items++;
|
||
97 | } |
||
98 | |||
99 | |||
100 | /**
|
||
101 | * roa_flush - flush a ROA table
|
||
102 | * @t: ROA table
|
||
103 | * @src: source of ROA entries (ROA_SRC_*)
|
||
104 | *
|
||
105 | * The function removes and frees ROA entries from the ROA table. If
|
||
106 | * @src is ROA_SRC_ANY, all entries in the table are removed,
|
||
107 | * otherwise only all entries from that source are removed.
|
||
108 | */
|
||
109 | void
|
||
110 | roa_flush(struct roa_table *t, byte src)
|
||
111 | { |
||
112 | struct roa_item *it, **itp;
|
||
113 | struct roa_node *n;
|
||
114 | |||
115 | FIB_WALK(&t->fib, fn) |
||
116 | { |
||
117 | n = (struct roa_node *) fn;
|
||
118 | |||
119 | itp = &n->items; |
||
120 | while (it = *itp)
|
||
121 | if (src_match(it, src))
|
||
122 | { |
||
123 | *itp = it->next; |
||
124 | sl_free(roa_slab, it); |
||
125 | } |
||
126 | else
|
||
127 | itp = &it->next; |
||
128 | } |
||
129 | FIB_WALK_END; |
||
130 | |||
131 | // TODO add cleanup of roa_nodes
|
||
132 | } |
||
133 | |||
134 | |||
135 | |||
136 | /*
|
||
137 | byte
|
||
138 | roa_check(struct roa_table *t, ip_addr prefix, byte pxlen, u32 asn)
|
||
139 | {
|
||
140 | struct roa_node *n = fib_find(&t->fib, &prefix, pxlen);
|
||
141 | |||
142 | if (n && n->n.x0 == ROA_UNKNOWN)
|
||
143 | return ROA_UNKNOWN;
|
||
144 | |||
145 | if (n && n->n.x0 == ROA_VALID && asn == n->cached_asn)
|
||
146 | return ROA_VALID;
|
||
147 | |||
148 | byte rv = roa_match(t, n, prefix, pxlen, asn);
|
||
149 | |||
150 | if (rv != ROA_INVALID)
|
||
151 | {
|
||
152 | if (!n)
|
||
153 | {
|
||
154 | if (t->cached_items >= t->cached_items_max)
|
||
155 | n = fib_get(&t->fib, &prefix, pxlen);
|
||
156 | t->cached_items++;
|
||
157 | }
|
||
158 | |||
159 | n->cached_asn = asn;
|
||
160 | n->n.x0 = rv;
|
||
161 | }
|
||
162 | |||
163 | return rv;
|
||
164 | }
|
||
165 | */
|
||
166 | |||
167 | /**
|
||
168 | * roa_check - check validity of route origination in a ROA table
|
||
169 | * @t: ROA table
|
||
170 | * @prefix: network prefix to check
|
||
171 | * @pxlen: length of network prefix
|
||
172 | * @asn: AS number of network prefix
|
||
173 | *
|
||
174 | * Implements RFC 6483 route validation for the given network
|
||
175 | * prefix. The procedure is to find all candidate ROAs - ROAs whose
|
||
176 | * prefixes cover the give network prefix. If there is no candidate
|
||
177 | * ROA, return ROA_UNKNOWN. If there is a candidate ROA with matching
|
||
178 | * ASN and maxlen field greater than or equal to the given prefix
|
||
179 | * length, return ROA_VALID. Otherwise return ROA_INVALID. If caller
|
||
180 | * cannot determine origin AS, 0 could be used (in that case ROA_VALID
|
||
181 | * cannot happen).
|
||
182 | */
|
||
183 | byte |
||
184 | roa_check(struct roa_table *t, ip_addr prefix, byte pxlen, u32 asn)
|
||
185 | { |
||
186 | struct roa_node *n;
|
||
187 | ip_addr px; |
||
188 | byte anything = 0;
|
||
189 | |||
190 | int len;
|
||
191 | for (len = pxlen; len >= 0; len--) |
||
192 | { |
||
193 | px = ipa_and(prefix, ipa_mkmask(len)); |
||
194 | n = fib_find(&t->fib, &px, len); |
||
195 | |||
196 | if (!n)
|
||
197 | continue;
|
||
198 | |||
199 | struct roa_item *it;
|
||
200 | for (it = n->items; it; it = it->next)
|
||
201 | { |
||
202 | anything = 1;
|
||
203 | if ((it->maxlen >= pxlen) && (it->asn == asn) && asn)
|
||
204 | return ROA_VALID;
|
||
205 | } |
||
206 | } |
||
207 | |||
208 | return anything ? ROA_INVALID : ROA_UNKNOWN;
|
||
209 | } |
||
210 | |||
211 | static void |
||
212 | roa_node_init(struct fib_node *fn)
|
||
213 | { |
||
214 | struct roa_node *n = (struct roa_node *) fn; |
||
215 | n->items = NULL;
|
||
216 | } |
||
217 | |||
218 | static inline void |
||
219 | roa_populate(struct roa_table *t)
|
||
220 | { |
||
221 | struct roa_item_config *ric;
|
||
222 | for (ric = t->cf->roa_items; ric; ric = ric->next)
|
||
223 | roa_add_item(t, ric->prefix, ric->pxlen, ric->maxlen, ric->asn, ROA_SRC_CONFIG); |
||
224 | } |
||
225 | |||
226 | static void |
||
227 | roa_new_table(struct roa_table_config *cf)
|
||
228 | { |
||
229 | struct roa_table *t;
|
||
230 | |||
231 | t = mb_allocz(roa_pool, sizeof(struct roa_table)); |
||
232 | fib_init(&t->fib, roa_pool, sizeof(struct roa_node), 0, roa_node_init); |
||
233 | t->name = cf->name; |
||
234 | t->cf = cf; |
||
235 | |||
236 | cf->table = t; |
||
237 | add_tail(&roa_table_list, &t->n); |
||
238 | |||
239 | roa_populate(t); |
||
240 | } |
||
241 | |||
242 | struct roa_table_config *
|
||
243 | roa_new_table_config(struct symbol *s)
|
||
244 | { |
||
245 | struct roa_table_config *rtc = cfg_allocz(sizeof(struct roa_table_config)); |
||
246 | |||
247 | cf_define_symbol(s, SYM_ROA, rtc); |
||
248 | rtc->name = s->name; |
||
249 | add_tail(&new_config->roa_tables, &rtc->n); |
||
250 | return rtc;
|
||
251 | } |
||
252 | |||
253 | /**
|
||
254 | * roa_add_item_config - add a static ROA entry to a ROA table configuration
|
||
255 | *
|
||
256 | * Arguments are self-explanatory. The first is the ROA table config, rest
|
||
257 | * are specifying the ROA entry.
|
||
258 | */
|
||
259 | void
|
||
260 | roa_add_item_config(struct roa_table_config *rtc, ip_addr prefix, byte pxlen, byte maxlen, u32 asn)
|
||
261 | { |
||
262 | struct roa_item_config *ric = cfg_allocz(sizeof(struct roa_item_config)); |
||
263 | |||
264 | ric->prefix = prefix; |
||
265 | ric->pxlen = pxlen; |
||
266 | ric->maxlen = maxlen; |
||
267 | ric->asn = asn; |
||
268 | ric->next = rtc->roa_items; |
||
269 | rtc->roa_items = ric; |
||
270 | } |
||
271 | |||
272 | /**
|
||
273 | * roa_init - initialize ROA tables
|
||
274 | *
|
||
275 | * This function is called during BIRD startup. It initializes
|
||
276 | * the ROA table module.
|
||
277 | */
|
||
278 | void
|
||
279 | roa_init(void)
|
||
280 | { |
||
281 | roa_pool = rp_new(&root_pool, "ROA tables");
|
||
282 | roa_slab = sl_new(roa_pool, sizeof(struct roa_item)); |
||
283 | init_list(&roa_table_list); |
||
284 | } |
||
285 | |||
286 | void
|
||
287 | roa_preconfig(struct config *c)
|
||
288 | { |
||
289 | init_list(&c->roa_tables); |
||
290 | } |
||
291 | |||
292 | |||
293 | /**
|
||
294 | * roa_commit - commit new ROA table configuration
|
||
295 | * @new: new configuration
|
||
296 | * @old: original configuration or %NULL if it's boot time config
|
||
297 | *
|
||
298 | * Scan differences between @old and @new configuration and modify the
|
||
299 | * ROA tables according to these changes. If @new defines a previously
|
||
300 | * unknown table, create it, if it omits a table existing in @old,
|
||
301 | * delete it (there are no references, only indirect through struct
|
||
302 | * roa_table_config). If it exists in both configurations, update the
|
||
303 | * configured ROA entries.
|
||
304 | */
|
||
305 | void
|
||
306 | roa_commit(struct config *new, struct config *old) |
||
307 | { |
||
308 | struct roa_table_config *cf;
|
||
309 | struct roa_table *t;
|
||
310 | |||
311 | if (old)
|
||
312 | WALK_LIST(t, roa_table_list) |
||
313 | { |
||
314 | 9b9a7143 | Ondrej Zajicek (work) | struct symbol *sym = cf_find_symbol(new, t->name);
|
315 | af582c48 | Ondrej Zajicek | if (sym && sym->class == SYM_ROA)
|
316 | { |
||
317 | /* Found old table in new config */
|
||
318 | cf = sym->def; |
||
319 | cf->table = t; |
||
320 | t->name = cf->name; |
||
321 | t->cf = cf; |
||
322 | |||
323 | /* Reconfigure it */
|
||
324 | roa_flush(t, ROA_SRC_CONFIG); |
||
325 | roa_populate(t); |
||
326 | } |
||
327 | else
|
||
328 | { |
||
329 | t->cf->table = NULL;
|
||
330 | |||
331 | /* Free it now */
|
||
332 | roa_flush(t, ROA_SRC_ANY); |
||
333 | rem_node(&t->n); |
||
334 | fib_free(&t->fib); |
||
335 | mb_free(t); |
||
336 | } |
||
337 | } |
||
338 | |||
339 | /* Add new tables */
|
||
340 | WALK_LIST(cf, new->roa_tables) |
||
341 | if (! cf->table)
|
||
342 | roa_new_table(cf); |
||
343 | |||
344 | roa_table_default = EMPTY_LIST(new->roa_tables) ? NULL :
|
||
345 | ((struct roa_table_config *) HEAD(new->roa_tables))->table;
|
||
346 | } |
||
347 | |||
348 | |||
349 | |||
350 | static void |
||
351 | roa_show_node(struct cli *c, struct roa_node *rn, int len, u32 asn) |
||
352 | { |
||
353 | struct roa_item *ri;
|
||
354 | |||
355 | for (ri = rn->items; ri; ri = ri->next)
|
||
356 | if ((ri->maxlen >= len) && (!asn || (ri->asn == asn)))
|
||
357 | c47d037e | Ondrej Zajicek | cli_printf(c, -1019, "%I/%d max %d as %u", rn->n.prefix, rn->n.pxlen, ri->maxlen, ri->asn); |
358 | af582c48 | Ondrej Zajicek | } |
359 | |||
360 | static void |
||
361 | roa_show_cont(struct cli *c)
|
||
362 | { |
||
363 | struct roa_show_data *d = c->rover;
|
||
364 | struct fib *fib = &d->table->fib;
|
||
365 | struct fib_iterator *it = &d->fit;
|
||
366 | struct roa_node *rn;
|
||
367 | unsigned max = 32; |
||
368 | |||
369 | FIB_ITERATE_START(fib, it, f) |
||
370 | { |
||
371 | rn = (struct roa_node *) f;
|
||
372 | |||
373 | if (!max--)
|
||
374 | { |
||
375 | FIB_ITERATE_PUT(it, f); |
||
376 | return;
|
||
377 | } |
||
378 | |||
379 | if ((d->mode == ROA_SHOW_ALL) ||
|
||
380 | net_in_net(rn->n.prefix, rn->n.pxlen, d->prefix, d->pxlen)) |
||
381 | roa_show_node(c, rn, 0, d->asn);
|
||
382 | } |
||
383 | FIB_ITERATE_END(f); |
||
384 | |||
385 | cli_printf(c, 0, ""); |
||
386 | c->cont = c->cleanup = NULL;
|
||
387 | } |
||
388 | |||
389 | static void |
||
390 | roa_show_cleanup(struct cli *c)
|
||
391 | { |
||
392 | struct roa_show_data *d = c->rover;
|
||
393 | |||
394 | /* Unlink the iterator */
|
||
395 | fit_get(&d->table->fib, &d->fit); |
||
396 | } |
||
397 | |||
398 | void
|
||
399 | roa_show(struct roa_show_data *d)
|
||
400 | { |
||
401 | struct roa_node *rn;
|
||
402 | ip_addr px; |
||
403 | int len;
|
||
404 | |||
405 | switch (d->mode)
|
||
406 | { |
||
407 | case ROA_SHOW_ALL:
|
||
408 | case ROA_SHOW_IN:
|
||
409 | FIB_ITERATE_INIT(&d->fit, &d->table->fib); |
||
410 | this_cli->cont = roa_show_cont; |
||
411 | this_cli->cleanup = roa_show_cleanup; |
||
412 | this_cli->rover = d; |
||
413 | break;
|
||
414 | |||
415 | case ROA_SHOW_PX:
|
||
416 | rn = fib_find(&d->table->fib, &d->prefix, d->pxlen); |
||
417 | if (rn)
|
||
418 | { |
||
419 | roa_show_node(this_cli, rn, 0, d->asn);
|
||
420 | cli_msg(0, ""); |
||
421 | } |
||
422 | else
|
||
423 | cli_msg(-8001, "Network not in table"); |
||
424 | break;
|
||
425 | |||
426 | case ROA_SHOW_FOR:
|
||
427 | for (len = d->pxlen; len >= 0; len--) |
||
428 | { |
||
429 | px = ipa_and(d->prefix, ipa_mkmask(len)); |
||
430 | rn = fib_find(&d->table->fib, &px, len); |
||
431 | |||
432 | if (!rn)
|
||
433 | continue;
|
||
434 | |||
435 | roa_show_node(this_cli, rn, 0, d->asn);
|
||
436 | } |
||
437 | cli_msg(0, ""); |
||
438 | break;
|
||
439 | } |
||
440 | } |