]> git.ipfire.org Git - thirdparty/bird.git/blame - nest/a-path.c
Nicer log output
[thirdparty/bird.git] / nest / a-path.c
CommitLineData
c0668f36
MM
1/*
2 * BIRD -- Path Operations
3 *
4 * (c) 2000 Martin Mares <mj@ucw.cz>
5 * (c) 2000 Pavel Machek <pavel@ucw.cz>
6 *
7 * Can be freely distributed and used under the terms of the GNU GPL.
8 */
9
10#include "nest/bird.h"
11#include "nest/route.h"
c6add07f 12#include "nest/attrs.h"
c0668f36
MM
13#include "lib/resource.h"
14#include "lib/unaligned.h"
c6add07f 15#include "lib/string.h"
05198c12 16#include "filter/filter.h"
c0668f36 17
43c1cecc
OZ
18// static inline void put_as(byte *data, u32 as) { put_u32(data, as); }
19// static inline u32 get_as(byte *data) { return get_u32(data); }
11cb6202 20
43c1cecc
OZ
21#define put_as put_u32
22#define get_as get_u32
23#define BS 4
11cb6202 24
c0668f36 25struct adata *
11cb6202 26as_path_prepend(struct linpool *pool, struct adata *olda, u32 as)
c0668f36
MM
27{
28 struct adata *newa;
29
11cb6202
OZ
30 if (olda->length && olda->data[0] == AS_PATH_SEQUENCE && olda->data[1] < 255)
31 /* Starting with sequence => just prepend the AS number */
c0668f36 32 {
43c1cecc 33 int nl = olda->length + BS;
11cb6202
OZ
34 newa = lp_alloc(pool, sizeof(struct adata) + nl);
35 newa->length = nl;
36 newa->data[0] = AS_PATH_SEQUENCE;
c0668f36 37 newa->data[1] = olda->data[1] + 1;
43c1cecc 38 memcpy(newa->data + BS + 2, olda->data + 2, olda->length - 2);
c0668f36 39 }
11cb6202 40 else /* Create new path segment */
c0668f36 41 {
43c1cecc 42 int nl = olda->length + BS + 2;
11cb6202
OZ
43 newa = lp_alloc(pool, sizeof(struct adata) + nl);
44 newa->length = nl;
45 newa->data[0] = AS_PATH_SEQUENCE;
c0668f36 46 newa->data[1] = 1;
43c1cecc 47 memcpy(newa->data + BS + 2, olda->data, olda->length);
c0668f36 48 }
11cb6202 49 put_as(newa->data + 2, as);
c0668f36
MM
50 return newa;
51}
c6add07f 52
11cb6202
OZ
53int
54as_path_convert_to_old(struct adata *path, byte *dst, int *new_used)
55{
56 byte *src = path->data;
57 byte *src_end = src + path->length;
58 byte *dst_start = dst;
59 u32 as;
60 int i, n;
61 *new_used = 0;
62
63 while (src < src_end)
64 {
65 n = src[1];
66 *dst++ = *src++;
67 *dst++ = *src++;
68
69 for(i=0; i<n; i++)
70 {
71 as = get_u32(src);
72 if (as > 0xFFFF)
73 {
74 as = AS_TRANS;
75 *new_used = 1;
76 }
77 put_u16(dst, as);
78 src += 4;
79 dst += 2;
80 }
81 }
82
83 return dst - dst_start;
84}
85
86int
87as_path_convert_to_new(struct adata *path, byte *dst, int req_as)
88{
89 byte *src = path->data;
90 byte *src_end = src + path->length;
91 byte *dst_start = dst;
92 u32 as;
93 int i, t, n;
94
95
96 while ((src < src_end) && (req_as > 0))
97 {
98 t = *src++;
99 n = *src++;
100
101 if (t == AS_PATH_SEQUENCE)
102 {
103 if (n > req_as)
104 n = req_as;
105
106 req_as -= n;
107 }
108 else // t == AS_PATH_SET
109 req_as--;
110
111 *dst++ = t;
112 *dst++ = n;
113
114 for(i=0; i<n; i++)
115 {
116 as = get_u16(src);
117 put_u32(dst, as);
118 src += 2;
119 dst += 4;
120 }
121 }
122
123 return dst - dst_start;
124}
125
c6add07f 126void
ae80a2de 127as_path_format(struct adata *path, byte *buf, uint size)
c6add07f
MM
128{
129 byte *p = path->data;
987de545 130 byte *e = p + path->length;
11cb6202 131 byte *end = buf + size - 16;
c6add07f 132 int sp = 1;
93a786cb 133 int l, isset;
c6add07f
MM
134
135 while (p < e)
136 {
137 if (buf > end)
138 {
139 strcpy(buf, " ...");
140 return;
141 }
142 isset = (*p++ == AS_PATH_SET);
143 l = *p++;
144 if (isset)
145 {
146 if (!sp)
147 *buf++ = ' ';
148 *buf++ = '{';
149 sp = 0;
150 }
151 while (l-- && buf <= end)
152 {
153 if (!sp)
154 *buf++ = ' ';
11cb6202 155 buf += bsprintf(buf, "%u", get_as(p));
43c1cecc 156 p += BS;
c6add07f
MM
157 sp = 0;
158 }
159 if (isset)
160 {
161 *buf++ = ' ';
162 *buf++ = '}';
163 sp = 0;
164 }
165 }
166 *buf = 0;
167}
684c6f5a
PM
168
169int
170as_path_getlen(struct adata *path)
171{
43c1cecc 172 return as_path_getlen_int(path, BS);
949bd34e
OZ
173}
174
175int
176as_path_getlen_int(struct adata *path, int bs)
177{
684c6f5a
PM
178 int res = 0;
179 u8 *p = path->data;
180 u8 *q = p+path->length;
181 int len;
182
4b03f64b
MM
183 while (p<q)
184 {
185 switch (*p++)
186 {
11cb6202
OZ
187 case AS_PATH_SET: len = *p++; res++; p += bs * len; break;
188 case AS_PATH_SEQUENCE: len = *p++; res += len; p += bs * len; break;
4b03f64b
MM
189 default: bug("as_path_getlen: Invalid path segment");
190 }
684c6f5a 191 }
684c6f5a
PM
192 return res;
193}
2a40efa5 194
f49528a3 195int
52b9b2a1 196as_path_get_last(struct adata *path, u32 *orig_as)
f49528a3 197{
11cb6202
OZ
198 int found = 0;
199 u32 res = 0;
f49528a3
MM
200 u8 *p = path->data;
201 u8 *q = p+path->length;
202 int len;
203
204 while (p<q)
205 {
206 switch (*p++)
207 {
208 case AS_PATH_SET:
209 if (len = *p++)
11cb6202 210 {
52b9b2a1 211 found = 0;
43c1cecc 212 p += BS * len;
11cb6202 213 }
f49528a3
MM
214 break;
215 case AS_PATH_SEQUENCE:
216 if (len = *p++)
11cb6202
OZ
217 {
218 found = 1;
43c1cecc
OZ
219 res = get_as(p + BS * (len - 1));
220 p += BS * len;
11cb6202 221 }
f49528a3 222 break;
9c9cc35c 223 default: bug("Invalid path segment");
f49528a3
MM
224 }
225 }
11cb6202 226
52b9b2a1
OZ
227 if (found)
228 *orig_as = res;
11cb6202 229 return found;
f49528a3
MM
230}
231
9c9cc35c
OZ
232u32
233as_path_get_last_nonaggregated(struct adata *path)
234{
235 u8 *p = path->data;
236 u8 *q = p+path->length;
237 u32 res = 0;
238 int len;
239
240 while (p<q)
241 {
242 switch (*p++)
243 {
244 case AS_PATH_SET:
245 return res;
246
247 case AS_PATH_SEQUENCE:
248 if (len = *p++)
249 res = get_as(p + BS * (len - 1));
250 p += BS * len;
251 break;
252
253 default: bug("Invalid path segment");
254 }
255 }
256
257 return res;
258}
259
260
b6bf284a 261int
52b9b2a1 262as_path_get_first(struct adata *path, u32 *last_as)
b6bf284a
OZ
263{
264 u8 *p = path->data;
265
266 if ((path->length == 0) || (p[0] != AS_PATH_SEQUENCE) || (p[1] == 0))
267 return 0;
268 else
269 {
270 *last_as = get_as(p+2);
271 return 1;
272 }
273}
274
11cb6202 275int
a15dab76 276as_path_contains(struct adata *path, u32 as, int min)
11cb6202 277{
11cb6202
OZ
278 u8 *p = path->data;
279 u8 *q = p+path->length;
a15dab76 280 int num = 0;
11cb6202
OZ
281 int i, n;
282
283 while (p<q)
284 {
285 n = p[1];
286 p += 2;
287 for(i=0; i<n; i++)
288 {
289 if (get_as(p) == as)
a15dab76
OZ
290 if (++num == min)
291 return 1;
43c1cecc 292 p += BS;
11cb6202
OZ
293 }
294 }
295 return 0;
296}
297
cc31b75a
OZ
298int
299as_path_match_set(struct adata *path, struct f_tree *set)
300{
301 u8 *p = path->data;
302 u8 *q = p+path->length;
303 int i, n;
304
305 while (p<q)
306 {
307 n = p[1];
308 p += 2;
309 for (i=0; i<n; i++)
310 {
311 struct f_val v = {T_INT, .val.i = get_as(p)};
312 if (find_tree(set, v))
313 return 1;
314 p += BS;
315 }
316 }
317
318 return 0;
319}
320
bff9ce51
OZ
321struct adata *
322as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos)
323{
324 if (!path)
325 return NULL;
326
327 int len = path->length;
328 u8 *p = path->data;
329 u8 *q = path->data + len;
330 u8 *d, *d2;
331 int i, bt, sn, dn;
332 u8 buf[len];
333
334 d = buf;
335 while (p<q)
336 {
337 /* Read block header (type and length) */
338 bt = p[0];
339 sn = p[1];
340 dn = 0;
341 p += 2;
342 d2 = d + 2;
343
344 for (i = 0; i < sn; i++)
345 {
346 u32 as = get_as(p);
347 int match;
348
349 if (set)
350 match = !!find_tree(set, (struct f_val){T_INT, .val.i = as});
351 else
352 match = (as == key);
353
354 if (match == pos)
355 {
356 put_as(d2, as);
357 d2 += BS;
358 dn++;
359 }
360
361 p += BS;
362 }
363
364 if (dn > 0)
365 {
366 /* Nonempty block, set block header and advance */
367 d[0] = bt;
368 d[1] = dn;
369 d = d2;
370 }
371 }
372
3e236955 373 uint nl = d - buf;
bff9ce51
OZ
374 if (nl == path->length)
375 return path;
376
377 struct adata *res = lp_alloc(pool, sizeof(struct adata) + nl);
378 res->length = nl;
379 memcpy(res->data, buf, nl);
380
381 return res;
382}
383
11cb6202 384
c8a6b9a3
OZ
385struct pm_pos
386{
387 u8 set;
388 u8 mark;
389 union
390 {
391 char *sp;
392 u32 asn;
393 } val;
394};
395
396static int
397parse_path(struct adata *path, struct pm_pos *pos)
2a40efa5 398{
2a40efa5 399 u8 *p = path->data;
c8a6b9a3
OZ
400 u8 *q = p + path->length;
401 struct pm_pos *opos = pos;
05198c12 402 int i, len;
2a40efa5 403
25cb9f1d 404
c8a6b9a3
OZ
405 while (p < q)
406 switch (*p++)
2a40efa5 407 {
c8a6b9a3
OZ
408 case AS_PATH_SET:
409 pos->set = 1;
410 pos->mark = 0;
411 pos->val.sp = p;
412 len = *p;
43c1cecc 413 p += 1 + BS * len;
c8a6b9a3
OZ
414 pos++;
415 break;
416
417 case AS_PATH_SEQUENCE:
418 len = *p++;
419 for (i = 0; i < len; i++)
420 {
421 pos->set = 0;
422 pos->mark = 0;
423 pos->val.asn = get_as(p);
43c1cecc 424 p += BS;
c8a6b9a3 425 pos++;
2a40efa5 426 }
c8a6b9a3
OZ
427 break;
428
429 default:
430 bug("as_path_match: Invalid path component");
2a40efa5 431 }
c8a6b9a3
OZ
432
433 return pos - opos;
434}
435
436
437static int
a0fe1944 438pm_match(struct pm_pos *pos, u32 asn, u32 asn2)
c8a6b9a3 439{
a0fe1944 440 u32 gas;
c8a6b9a3 441 if (! pos->set)
a0fe1944 442 return ((pos->val.asn >= asn) && (pos->val.asn <= asn2));
c8a6b9a3 443
c8a6b9a3
OZ
444 u8 *p = pos->val.sp;
445 int len = *p++;
446 int i;
447
448 for (i = 0; i < len; i++)
a0fe1944
OF
449 {
450 gas = get_as(p + i * BS);
451
452 if ((gas >= asn) && (gas <= asn2))
c8a6b9a3 453 return 1;
a0fe1944 454 }
c8a6b9a3
OZ
455
456 return 0;
457}
458
459static void
460pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh)
461{
462 int j;
463
464 if (pos[i].set)
465 pos[i].mark = 1;
466
467 for (j = i + 1; (j < plen) && pos[j].set && (! pos[j].mark); j++)
468 pos[j].mark = 1;
469 pos[j].mark = 1;
470
471 /* We are going downwards, therefore every mark is
472 new low and just the first mark is new high */
473
474 *nl = i + (pos[i].set ? 0 : 1);
475
476 if (*nh < 0)
477 *nh = j;
478}
479
480/* AS path matching is nontrivial. Because AS path can
481 * contain sets, it is not a plain wildcard matching. A set
482 * in an AS path is interpreted as it might represent any
483 * sequence of AS numbers from that set (possibly with
484 * repetitions). So it is also a kind of a pattern,
485 * more complicated than a path mask.
486 *
487 * The algorithm for AS path matching is a variant
488 * of nondeterministic finite state machine, where
489 * positions in AS path are states, and items in
490 * path mask are input for that finite state machine.
491 * During execution of the algorithm we maintain a set
492 * of marked states - a state is marked if it can be
493 * reached by any walk through NFSM with regard to
494 * currently processed part of input. When we process
495 * next part of mask, we advance each marked state.
496 * We start with marked first position, when we
497 * run out of marked positions, we reject. When
a0fe1944 498 * we process the whole mask, we accept if final position
c8a6b9a3
OZ
499 * (auxiliary position after last real position in AS path)
500 * is marked.
501 */
502
503int
504as_path_match(struct adata *path, struct f_path_mask *mask)
505{
506 struct pm_pos pos[2048 + 1];
507 int plen = parse_path(path, pos);
508 int l, h, i, nh, nl;
e81b440f 509 u32 val = 0;
a0fe1944 510 u32 val2 = 0;
c8a6b9a3
OZ
511
512 /* l and h are bound of interval of positions where
513 are marked states */
514
515 pos[plen].set = 0;
516 pos[plen].mark = 0;
517
518 l = h = 0;
519 pos[0].mark = 1;
520
521 while (mask)
522 {
523 /* We remove this mark to not step after pos[plen] */
524 pos[plen].mark = 0;
525
526 switch (mask->kind)
527 {
528 case PM_ASTERISK:
529 for (i = l; i <= plen; i++)
530 pos[i].mark = 1;
531 h = plen;
532 break;
533
a0fe1944
OF
534 case PM_ASN: /* Define single ASN as ASN..ASN - very narrow interval */
535 val2 = val = mask->val;
92a72a4c
OZ
536 goto step;
537 case PM_ASN_EXPR:
a0fe1944 538 val2 = val = f_eval_asn((struct f_inst *) mask->val);
92a72a4c 539 goto step;
a0fe1944
OF
540 case PM_ASN_RANGE:
541 val = mask->val;
542 val2 = mask->val2;
543 goto step;
92a72a4c
OZ
544 case PM_QUESTION:
545 step:
e81b440f 546 nh = nl = -1;
c8a6b9a3
OZ
547 for (i = h; i >= l; i--)
548 if (pos[i].mark)
549 {
550 pos[i].mark = 0;
a0fe1944 551 if ((mask->kind == PM_QUESTION) || pm_match(pos + i, val, val2))
c8a6b9a3
OZ
552 pm_mark(pos, i, plen, &nl, &nh);
553 }
554
555 if (nh < 0)
2a40efa5 556 return 0;
c8a6b9a3
OZ
557
558 h = nh;
559 l = nl;
560 break;
2a40efa5 561 }
2a40efa5 562
c8a6b9a3 563 mask = mask->next;
2a40efa5 564 }
98347659 565
c8a6b9a3
OZ
566 return pos[plen].mark;
567}