]> git.ipfire.org Git - thirdparty/bird.git/blob - proto/bgp/attrs.c
5c1e70db6e08afb02ab027b973ca18e053d613b4
[thirdparty/bird.git] / proto / bgp / attrs.c
1 /*
2 * BIRD -- BGP Attributes
3 *
4 * (c) 2000 Martin Mares <mj@ucw.cz>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
9 #define LOCAL_DEBUG
10
11 #include "nest/bird.h"
12 #include "nest/iface.h"
13 #include "nest/protocol.h"
14 #include "nest/route.h"
15 #include "conf/conf.h"
16 #include "lib/resource.h"
17 #include "lib/string.h"
18 #include "lib/unaligned.h"
19
20 #include "bgp.h"
21
22 static int bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH, BA_NEXT_HOP };
23
24 struct bgp_bucket {
25 struct bgp_bucket *next;
26 unsigned hash;
27 ea_list eattrs[0];
28 };
29
30 struct attr_desc {
31 char *name; /* FIXME: Use the same names as in filters */
32 int expected_length;
33 int expected_flags;
34 int type;
35 int (*validate)(struct bgp_proto *p, byte *attr, int len);
36 void (*format)(eattr *ea, byte *buf);
37 };
38
39 extern struct attr_desc bgp_attr_table[];
40
41 static void
42 bgp_normalize_set(u32 *dest, u32 *src, unsigned cnt)
43 {
44 memcpy(dest, src, sizeof(u32) * cnt);
45 /* FIXME */
46 }
47
48 static void
49 bgp_rehash_buckets(struct bgp_proto *p)
50 {
51 struct bgp_bucket **old = p->bucket_table;
52 struct bgp_bucket **new;
53 unsigned oldn = p->hash_size;
54 unsigned i, e, mask;
55 struct bgp_bucket *b;
56
57 p->hash_size = p->hash_limit;
58 DBG("BGP: Rehashing bucket table from %d to %d\n", oldn, p->hash_size);
59 p->hash_limit *= 4;
60 if (p->hash_limit >= 65536)
61 p->hash_limit = ~0;
62 new = p->bucket_table = mb_allocz(p->p.pool, p->hash_size * sizeof(struct bgp_bucket *));
63 mask = p->hash_size - 1;
64 for (i=0; i<oldn; i++)
65 while (b = old[i])
66 {
67 old[i] = b->next;
68 e = b->hash & mask;
69 b->next = new[e];
70 new[e] = b;
71 }
72 mb_free(old);
73 }
74
75 static struct bgp_bucket *
76 bgp_new_bucket(struct bgp_proto *p, ea_list *new, unsigned hash)
77 {
78 struct bgp_bucket *b;
79 unsigned ea_size = sizeof(ea_list) + new->count * sizeof(eattr);
80 unsigned ea_size_aligned = ALIGN(ea_size, CPU_STRUCT_ALIGN);
81 unsigned size = sizeof(struct bgp_bucket) + ea_size;
82 unsigned i;
83 byte *dest;
84 unsigned index = hash & (p->hash_size - 1);
85
86 /* Gather total size of non-inline attributes */
87 for (i=0; i<new->count; i++)
88 {
89 eattr *a = &new->attrs[i];
90 if (!(a->type & EAF_EMBEDDED))
91 size += ALIGN(sizeof(struct adata) + a->u.ptr->length, CPU_STRUCT_ALIGN);
92 }
93
94 /* Create the bucket and hash it */
95 b = mb_alloc(p->p.pool, size);
96 b->next = p->bucket_table[index];
97 p->bucket_table[index] = b;
98 b->hash = hash;
99 memcpy(b->eattrs, new, ea_size);
100 dest = ((byte *)b->eattrs) + ea_size_aligned;
101
102 /* Copy values of non-inline attributes */
103 for (i=0; i<new->count; i++)
104 {
105 eattr *a = &new->attrs[i];
106 if (!(a->type & EAF_EMBEDDED))
107 {
108 struct adata *oa = a->u.ptr;
109 struct adata *na = (struct adata *) dest;
110 memcpy(na, oa, sizeof(struct adata) + oa->length);
111 a->u.ptr = na;
112 dest += ALIGN(na->length, CPU_STRUCT_ALIGN);
113 }
114 }
115
116 /* If needed, rehash */
117 p->hash_count++;
118 if (p->hash_count > p->hash_limit)
119 bgp_rehash_buckets(p);
120
121 return b;
122 }
123
124 static struct bgp_bucket *
125 bgp_get_bucket(struct bgp_proto *p, ea_list *old, ea_list *tmp)
126 {
127 ea_list *t, *new;
128 unsigned i, cnt;
129 eattr *a, *d;
130 u32 seen = 0;
131 unsigned hash;
132 struct bgp_bucket *b;
133
134 /* Merge the attribute lists */
135 for(t=tmp; t->next; t=t->next)
136 ;
137 t->next = old;
138 new = alloca(ea_scan(tmp));
139 ea_merge(tmp, new);
140 t->next = NULL;
141 ea_sort(tmp);
142
143 /* Normalize attributes */
144 d = new->attrs;
145 cnt = new->count;
146 new->count = 0;
147 for(i=0; i<cnt; i++)
148 {
149 a = &new->attrs[i];
150 #ifdef LOCAL_DEBUG
151 {
152 byte buf[256];
153 ea_format(a, buf);
154 DBG("\t%s\n", buf);
155 }
156 #endif
157 if (EA_PROTO(a->id) != EAP_BGP)
158 continue;
159 if (EA_ID(a->id) < 32)
160 seen |= 1 << EA_ID(a->id);
161 *d = *a;
162 switch (d->type & EAF_TYPE_MASK)
163 {
164 case EAF_TYPE_INT_SET:
165 {
166 struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length);
167 z->length = d->u.ptr->length;
168 bgp_normalize_set((u32 *) z->data, (u32 *) d->u.ptr->data, z->length / 4);
169 d->u.ptr = z;
170 break;
171 }
172 default:
173 }
174 d++;
175 new->count++;
176 }
177
178 /* Hash */
179 hash = ea_hash(new);
180 for(b=p->bucket_table[hash & (p->hash_size - 1)]; b; b=b->next)
181 if (b->hash == hash && ea_same(b->eattrs, new))
182 {
183 DBG("Found bucket.\n");
184 return b;
185 }
186
187 /* Ensure that there are all mandatory attributes */
188 /* FIXME: Introduce array size macro */
189 for(i=0; i<sizeof(bgp_mandatory_attrs)/sizeof(bgp_mandatory_attrs[0]); i++)
190 if (!(seen & (1 << bgp_mandatory_attrs[i])))
191 {
192 log(L_ERR "%s: Mandatory attribute %s missing", p->p.name, bgp_attr_table[bgp_mandatory_attrs[i]].name);
193 return NULL;
194 }
195
196 /* Create new bucket */
197 DBG("Creating bucket.\n");
198 return bgp_new_bucket(p, new, hash);
199 }
200
201 void
202 bgp_rt_notify(struct proto *P, net *n, rte *new, rte *old, ea_list *tmpa)
203 {
204 struct bgp_proto *p = (struct bgp_proto *) P;
205
206 DBG("BGP: Got route %I/%d\n", n->n.prefix, n->n.pxlen);
207
208 if (new)
209 {
210 struct bgp_bucket *buck = bgp_get_bucket(p, new->attrs->eattrs, tmpa);
211 if (!buck) /* Inconsistent attribute list */
212 return;
213 }
214
215 /* FIXME: Normalize attributes */
216 /* FIXME: Check next hop */
217 }
218
219 static int
220 bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *pool)
221 {
222 ea_list *ea = lp_alloc(pool, sizeof(ea_list) + 3*sizeof(eattr));
223 eattr *a = ea->attrs;
224 rta *rta = e->attrs;
225
226 ea->next = *attrs;
227 *attrs = ea;
228 ea->flags = EALF_SORTED;
229 ea->count = 3;
230
231 a->id = EA_CODE(EAP_BGP, BA_ORIGIN);
232 a->flags = BAF_TRANSITIVE;
233 a->type = EAF_TYPE_INT;
234 if (rta->source == RTS_RIP_EXT || rta->source == RTS_OSPF_EXT)
235 a->u.data = 2; /* Incomplete */
236 else
237 a->u.data = 0; /* IGP */
238 a++;
239
240 a->id = EA_CODE(EAP_BGP, BA_AS_PATH);
241 a->flags = BAF_TRANSITIVE;
242 a->type = EAF_TYPE_AS_PATH;
243 if (p->is_internal)
244 {
245 a->u.ptr = lp_alloc(pool, sizeof(struct adata));
246 a->u.ptr->length = 0;
247 }
248 else
249 {
250 byte *z;
251 a->u.ptr = lp_alloc(pool, sizeof(struct adata) + 4);
252 a->u.ptr->length = 4;
253 z = a->u.ptr->data;
254 z[0] = 2; /* AS_SEQUENCE */
255 z[1] = 1; /* 1 AS */
256 put_u16(z+2, p->local_as);
257 }
258 a++;
259
260 a->id = EA_CODE(EAP_BGP, BA_NEXT_HOP);
261 a->flags = BAF_TRANSITIVE;
262 a->type = EAF_TYPE_IP_ADDRESS;
263 a->u.ptr = lp_alloc(pool, sizeof(struct adata) + sizeof(ip_addr));
264 a->u.ptr->length = sizeof(ip_addr);
265 if (p->cf->next_hop_self ||
266 !p->is_internal ||
267 rta->dest != RTD_ROUTER)
268 *(ip_addr *)a->u.ptr->data = p->local_addr;
269 else
270 *(ip_addr *)a->u.ptr->data = e->attrs->gw;
271
272 return 0; /* Leave decision to the filters */
273 }
274
275 ea_list *
276 bgp_path_prepend(struct linpool *pool, eattr *a, ea_list *old, int as)
277 {
278 struct ea_list *e = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
279 struct adata *olda = a->u.ptr;
280 struct adata *newa;
281
282 e->next = old;
283 e->flags = EALF_SORTED;
284 e->count = 1;
285 e->attrs[0].id = EA_CODE(EAP_BGP, BA_AS_PATH);
286 e->attrs[0].flags = BAF_TRANSITIVE;
287 e->attrs[0].type = EAF_TYPE_AS_PATH;
288 if (olda->length && olda->data[0] == 2 && olda->data[1] < 255) /* Starting with sequence => just prepend the AS number */
289 {
290 newa = lp_alloc(pool, sizeof(struct adata) + olda->length + 2);
291 newa->length = olda->length + 2;
292 newa->data[0] = 2;
293 newa->data[1] = olda->data[1] + 1;
294 memcpy(newa->data+4, olda->data+2, olda->length-2);
295 }
296 else /* Create new path segment */
297 {
298 newa = lp_alloc(pool, sizeof(struct adata) + olda->length + 4);
299 newa->length = olda->length + 4;
300 newa->data[0] = 2;
301 newa->data[1] = 1;
302 memcpy(newa->data+4, olda->data, olda->length);
303 }
304 put_u16(newa->data+2, as);
305 e->attrs[0].u.ptr = newa;
306 return e;
307 }
308
309 static int
310 bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *pool)
311 {
312 eattr *a;
313
314 if (!p->is_internal)
315 *attrs = bgp_path_prepend(pool, ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)), *attrs, p->local_as);
316
317 a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
318 if (a && (p->is_internal || (!p->is_internal && e->attrs->iface == p->neigh->iface)))
319 {
320 /* Leave the original next hop attribute, will check later where does it point */
321 }
322 else
323 {
324 /* Need to create new one */
325 ea_list *ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
326 ea->next = *attrs;
327 *attrs = ea;
328 ea->flags = EALF_SORTED;
329 ea->count = 1;
330 a = ea->attrs;
331 a->id = EA_CODE(EAP_BGP, BA_NEXT_HOP);
332 a->flags = BAF_TRANSITIVE;
333 a->type = EAF_TYPE_IP_ADDRESS;
334 a->u.ptr = lp_alloc(pool, sizeof(struct adata) + sizeof(ip_addr));
335 a->u.ptr->length = sizeof(ip_addr);
336 *(ip_addr *)a->u.ptr->data = p->local_addr;
337 }
338
339 return 0; /* Leave decision to the filters */
340 }
341
342 int
343 bgp_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool)
344 {
345 rte *e = *new;
346 struct bgp_proto *p = (struct bgp_proto *) P;
347 struct bgp_proto *new_bgp = (e->attrs->proto->proto == &proto_bgp) ? (struct bgp_proto *) e->attrs->proto : NULL;
348
349 if (p == new_bgp) /* Poison reverse updates */
350 return -1;
351 if (new_bgp)
352 {
353 if (p->local_as == new_bgp->local_as && p->is_internal && new_bgp->is_internal)
354 return -1; /* Don't redistribute internal routes with IBGP */
355 return bgp_update_attrs(p, e, attrs, pool);
356 }
357 else
358 return bgp_create_attrs(p, e, attrs, pool);
359 }
360
361 int
362 bgp_rte_better(rte *new, rte *old)
363 {
364 struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->proto;
365 struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->proto;
366 eattr *new_lpref = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
367 eattr *old_lpref = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
368
369 /* Start with local preferences */
370 if (new_lpref && old_lpref) /* Somebody might have undefined them */
371 {
372 if (new_lpref->u.data > old_lpref->u.data)
373 return 1;
374 if (new_lpref->u.data < old_lpref->u.data)
375 return 0;
376 }
377
378 /* A tie breaking procedure according to RFC 1771, section 9.1.2.1 */
379 /* FIXME: Look at MULTI_EXIT_DISC, take the lowest */
380 /* We don't have interior distances */
381 /* We prefer external peers */
382 if (new_bgp->is_internal > old_bgp->is_internal)
383 return 0;
384 if (new_bgp->is_internal < old_bgp->is_internal)
385 return 1;
386 /* Finally we compare BGP identifiers */
387 return (new_bgp->remote_id < old_bgp->remote_id);
388 }
389
390 static int
391 bgp_local_pref(struct bgp_proto *p, rta *a)
392 {
393 return 0; /* FIXME (should be compatible with Cisco defaults?) */
394 }
395
396 static int
397 bgp_path_loopy(struct bgp_proto *p, eattr *a)
398 {
399 byte *path = a->u.ptr->data;
400 int len = a->u.ptr->length;
401 int i, n;
402
403 while (len > 0)
404 {
405 n = path[1];
406 len -= 2 - 2*n;
407 path += 2;
408 for(i=0; i<n; i++)
409 {
410 if (get_u16(path) == p->local_as)
411 return 1;
412 path += 2;
413 }
414 }
415 return 0;
416 }
417
418 static int
419 bgp_check_origin(struct bgp_proto *p, byte *a, int len)
420 {
421 if (len > 2)
422 return 6;
423 return 0;
424 }
425
426 static void
427 bgp_format_origin(eattr *a, byte *buf)
428 {
429 static char *bgp_origin_names[] = { "IGP", "EGP", "Incomplete" };
430
431 bsprintf(buf, bgp_origin_names[a->u.data]);
432 }
433
434 static int
435 bgp_check_path(struct bgp_proto *p, byte *a, int len)
436 {
437 while (len)
438 {
439 DBG("Path segment %02x %02x\n", a[0], a[1]);
440 if (len < 2 ||
441 a[0] != BGP_PATH_AS_SET && a[0] != BGP_PATH_AS_SEQUENCE ||
442 2*a[1] + 2 > len)
443 return 11;
444 len -= 2*a[1] + 2;
445 a += 2*a[1] + 2;
446 }
447 return 0;
448 }
449
450 static int
451 bgp_check_next_hop(struct bgp_proto *p, byte *a, int len)
452 {
453 ip_addr addr;
454
455 memcpy(&addr, a, len);
456 if (ipa_classify(ipa_ntoh(addr)) & IADDR_HOST)
457 return 0;
458 else
459 return 8;
460 }
461
462 static int
463 bgp_check_local_pref(struct bgp_proto *p, byte *a, int len)
464 {
465 if (!p->is_internal) /* Ignore local preference from EBGP connections */
466 return -1;
467 return 0;
468 }
469
470 static struct attr_desc bgp_attr_table[] = {
471 { NULL, -1, 0, 0, /* Undefined */
472 NULL, NULL },
473 { "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, /* BA_ORIGIN */
474 bgp_check_origin, bgp_format_origin },
475 { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, /* BA_AS_PATH */
476 bgp_check_path, NULL },
477 { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, /* BA_NEXT_HOP */
478 bgp_check_next_hop, NULL },
479 { "MED", 4, BAF_OPTIONAL, EAF_TYPE_INT, /* BA_MULTI_EXIT_DISC */
480 NULL, NULL },
481 { "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, /* BA_LOCAL_PREF */
482 bgp_check_local_pref, NULL },
483 { "atomic_aggr", 0, BAF_OPTIONAL, EAF_TYPE_OPAQUE, /* BA_ATOMIC_AGGR */
484 NULL, NULL },
485 { "aggregator", 6, BAF_OPTIONAL, EAF_TYPE_OPAQUE, /* BA_AGGREGATOR */
486 NULL, NULL },
487 #if 0
488 /* FIXME: Handle community lists and remember to convert their endianity and normalize them */
489 { 0, 0 }, /* BA_COMMUNITY */
490 { 0, 0 }, /* BA_ORIGINATOR_ID */
491 { 0, 0 }, /* BA_CLUSTER_LIST */
492 #endif
493 };
494
495 struct rta *
496 bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct linpool *pool)
497 {
498 struct bgp_proto *bgp = conn->bgp;
499 rta *a = lp_alloc(pool, sizeof(struct rta));
500 unsigned int flags, code, l, i, type;
501 int errcode;
502 byte *z, *attr_start;
503 byte seen[256/8];
504 eattr *e;
505 ea_list *ea;
506 struct adata *ad;
507 neighbor *neigh;
508 ip_addr nexthop;
509
510 a->proto = &bgp->p;
511 a->source = RTS_BGP;
512 a->scope = SCOPE_UNIVERSE;
513 a->cast = RTC_UNICAST;
514 a->dest = RTD_ROUTER;
515 a->flags = 0;
516 a->aflags = 0;
517 a->from = bgp->cf->remote_ip;
518 a->eattrs = NULL;
519
520 /* Parse the attributes */
521 bzero(seen, sizeof(seen));
522 DBG("BGP: Parsing attributes\n");
523 while (len)
524 {
525 if (len < 2)
526 goto malformed;
527 attr_start = attr;
528 flags = *attr++;
529 code = *attr++;
530 len -= 2;
531 if (flags & BAF_EXT_LEN)
532 {
533 if (len < 2)
534 goto malformed;
535 l = get_u16(attr);
536 attr += 2;
537 len -= 2;
538 }
539 else
540 {
541 if (len < 1)
542 goto malformed;
543 l = *attr++;
544 len--;
545 }
546 if (l > len)
547 goto malformed;
548 len -= l;
549 z = attr;
550 attr += l;
551 DBG("Attr %02x %02x %d\n", code, flags, l);
552 if (seen[code/8] & (1 << (code%8)))
553 goto malformed;
554 if (code && code < sizeof(bgp_attr_table)/sizeof(bgp_attr_table[0]))
555 {
556 struct attr_desc *desc = &bgp_attr_table[code];
557 if (desc->expected_length >= 0 && desc->expected_length != (int) l)
558 { errcode = 5; goto err; }
559 if ((desc->expected_flags ^ flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
560 { errcode = 4; goto err; }
561 if (desc->validate)
562 {
563 errcode = desc->validate(bgp, z, l);
564 if (errcode > 0)
565 goto err;
566 if (errcode < 0)
567 continue;
568 }
569 type = desc->type;
570 }
571 else /* Unknown attribute */
572 { /* FIXME: Send partial bit when forwarding */
573 if (!(flags & BAF_OPTIONAL))
574 { errcode = 2; goto err; }
575 type = EAF_TYPE_OPAQUE;
576 }
577 seen[code/8] |= (1 << (code%8));
578 ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
579 ea->next = a->eattrs;
580 a->eattrs = ea;
581 ea->flags = 0;
582 ea->count = 1;
583 ea->attrs[0].id = EA_CODE(EAP_BGP, code);
584 ea->attrs[0].flags = flags;
585 ea->attrs[0].type = type;
586 if (type & EAF_EMBEDDED)
587 ad = NULL;
588 else
589 {
590 ad = lp_alloc(pool, sizeof(struct adata) + l);
591 ea->attrs[0].u.ptr = ad;
592 ad->length = l;
593 memcpy(ad->data, z, l);
594 }
595 switch (type)
596 {
597 case EAF_TYPE_ROUTER_ID:
598 case EAF_TYPE_INT:
599 if (l == 1)
600 ea->attrs[0].u.data = *z;
601 else
602 ea->attrs[0].u.data = get_u32(z);
603 break;
604 case EAF_TYPE_IP_ADDRESS:
605 *(ip_addr *)ad->data = ipa_ntoh(*(ip_addr *)ad->data);
606 break;
607 }
608 }
609
610 /* Check if all mandatory attributes are present */
611 for(i=0; i < sizeof(bgp_mandatory_attrs)/sizeof(bgp_mandatory_attrs[0]); i++)
612 {
613 code = bgp_mandatory_attrs[i];
614 if (!(seen[code/8] & (1 << (code%8))))
615 {
616 bgp_error(conn, 3, 3, code, 1);
617 return NULL;
618 }
619 }
620
621 /* Assign local preference if none defined */
622 if (!(seen[BA_LOCAL_PREF/8] & (1 << (BA_LOCAL_PREF%8))))
623 {
624 ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
625 ea->next = a->eattrs;
626 a->eattrs = ea;
627 ea->flags = 0;
628 ea->count = 1;
629 ea->attrs[0].id = EA_CODE(EAP_BGP, BA_LOCAL_PREF);
630 ea->attrs[0].flags = BAF_OPTIONAL;
631 ea->attrs[0].type = EAF_TYPE_INT;
632 ea->attrs[0].u.data = bgp_local_pref(bgp, a);
633 }
634
635 /* If the AS path attribute contains our AS, reject the routes */
636 e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
637 ASSERT(e);
638 if (bgp_path_loopy(bgp, e))
639 return NULL;
640
641 /* Fill in the remaining rta fields */
642 e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
643 ASSERT(e);
644 nexthop = *(ip_addr *) e->u.ptr->data;
645 if (ipa_equal(nexthop, bgp->local_addr))
646 {
647 DBG("BGP: Loop!\n"); /* FIXME */
648 return NULL;
649 }
650 neigh = neigh_find(&bgp->p, &nexthop, 0) ? : bgp->neigh;
651 a->gw = neigh->addr;
652 a->iface = neigh->iface;
653 return rta_lookup(a);
654
655 malformed:
656 bgp_error(conn, 3, 1, len, 0);
657 return NULL;
658
659 err:
660 bgp_error(conn, 3, errcode, code, 0); /* FIXME: Return attribute data! */
661 return NULL;
662 }
663
664 int
665 bgp_get_attr(eattr *a, byte *buf)
666 {
667 unsigned int i = EA_ID(a->id);
668 struct attr_desc *d;
669
670 if (i && i < sizeof(bgp_attr_table)/sizeof(bgp_attr_table[0]))
671 {
672 d = &bgp_attr_table[i];
673 buf += bsprintf(buf, "%s", d->name);
674 if (d->format)
675 {
676 *buf++ = ':';
677 *buf++ = ' ';
678 d->format(a, buf);
679 return GA_FULL;
680 }
681 return GA_NAME;
682 }
683 bsprintf(buf, "%02x%s", i, (a->flags & BAF_TRANSITIVE) ? "[t]" : "");
684 return GA_NAME;
685 }
686
687 void
688 bgp_attr_init(struct bgp_proto *p)
689 {
690 p->hash_size = 256;
691 p->hash_limit = p->hash_size * 4;
692 p->bucket_table = mb_allocz(p->p.pool, p->hash_size * sizeof(struct bgp_bucket *));
693 }