]>
Commit | Line | Data |
---|---|---|
c01e3741 MM |
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 | ||
c00d31be MM |
9 | #define LOCAL_DEBUG |
10 | ||
e3558ab1 MM |
11 | #include <stdlib.h> |
12 | ||
c01e3741 MM |
13 | #include "nest/bird.h" |
14 | #include "nest/iface.h" | |
15 | #include "nest/protocol.h" | |
16 | #include "nest/route.h" | |
c0668f36 | 17 | #include "nest/attrs.h" |
c01e3741 | 18 | #include "conf/conf.h" |
c00d31be MM |
19 | #include "lib/resource.h" |
20 | #include "lib/string.h" | |
21 | #include "lib/unaligned.h" | |
c01e3741 MM |
22 | |
23 | #include "bgp.h" | |
c00d31be | 24 | |
ae8f5584 MM |
25 | static int bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH, BA_NEXT_HOP }; |
26 | ||
ae8f5584 MM |
27 | struct attr_desc { |
28 | char *name; /* FIXME: Use the same names as in filters */ | |
29 | int expected_length; | |
30 | int expected_flags; | |
31 | int type; | |
56a2bed4 | 32 | int allow_in_ebgp; |
ae8f5584 MM |
33 | int (*validate)(struct bgp_proto *p, byte *attr, int len); |
34 | void (*format)(eattr *ea, byte *buf); | |
35 | }; | |
36 | ||
f421cfdd MM |
37 | static int |
38 | bgp_check_origin(struct bgp_proto *p, byte *a, int len) | |
39 | { | |
40 | if (len > 2) | |
41 | return 6; | |
42 | return 0; | |
43 | } | |
44 | ||
45 | static void | |
46 | bgp_format_origin(eattr *a, byte *buf) | |
47 | { | |
48 | static char *bgp_origin_names[] = { "IGP", "EGP", "Incomplete" }; | |
49 | ||
50 | bsprintf(buf, bgp_origin_names[a->u.data]); | |
51 | } | |
52 | ||
53 | static int | |
54 | bgp_check_path(struct bgp_proto *p, byte *a, int len) | |
55 | { | |
56 | while (len) | |
57 | { | |
58 | DBG("Path segment %02x %02x\n", a[0], a[1]); | |
59 | if (len < 2 || | |
1ed2fe96 | 60 | a[0] != AS_PATH_SET && a[0] != AS_PATH_SEQUENCE || |
f421cfdd MM |
61 | 2*a[1] + 2 > len) |
62 | return 11; | |
63 | len -= 2*a[1] + 2; | |
64 | a += 2*a[1] + 2; | |
65 | } | |
66 | return 0; | |
67 | } | |
68 | ||
69 | static int | |
70 | bgp_check_next_hop(struct bgp_proto *p, byte *a, int len) | |
71 | { | |
72 | ip_addr addr; | |
73 | ||
74 | memcpy(&addr, a, len); | |
75 | ipa_ntoh(addr); | |
76 | if (ipa_classify(addr) & IADDR_HOST) | |
77 | return 0; | |
78 | else | |
79 | return 8; | |
80 | } | |
81 | ||
f421cfdd | 82 | static struct attr_desc bgp_attr_table[] = { |
56a2bed4 | 83 | { NULL, -1, 0, 0, 0, /* Undefined */ |
f421cfdd | 84 | NULL, NULL }, |
56a2bed4 | 85 | { "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* BA_ORIGIN */ |
f421cfdd | 86 | bgp_check_origin, bgp_format_origin }, |
56a2bed4 | 87 | { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* BA_AS_PATH */ |
f421cfdd | 88 | bgp_check_path, NULL }, |
56a2bed4 | 89 | { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* BA_NEXT_HOP */ |
f421cfdd | 90 | bgp_check_next_hop, NULL }, |
56a2bed4 | 91 | { "MED", 4, BAF_OPTIONAL, EAF_TYPE_INT, 0, /* BA_MULTI_EXIT_DISC */ |
f421cfdd | 92 | NULL, NULL }, |
56a2bed4 | 93 | { "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, 0, /* BA_LOCAL_PREF */ |
f421cfdd | 94 | NULL, NULL }, |
56a2bed4 | 95 | { "atomic_aggr", 0, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */ |
f421cfdd | 96 | NULL, NULL }, |
56a2bed4 MM |
97 | { "aggregator", 6, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_AGGREGATOR */ |
98 | NULL, NULL }, | |
99 | { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */ | |
1ed2fe96 | 100 | NULL, NULL }, |
f421cfdd | 101 | #if 0 |
f421cfdd MM |
102 | { 0, 0 }, /* BA_ORIGINATOR_ID */ |
103 | { 0, 0 }, /* BA_CLUSTER_LIST */ | |
104 | #endif | |
105 | }; | |
106 | ||
107 | byte * | |
108 | bgp_encode_attrs(byte *w, struct bgp_bucket *buck) | |
109 | { | |
110 | int remains = 1024; | |
111 | unsigned int i, code, flags; | |
112 | byte *start = w; | |
113 | int len; | |
114 | ||
115 | w += 2; | |
116 | for(i=0; i<buck->eattrs->count; i++) | |
117 | { | |
118 | eattr *a = &buck->eattrs->attrs[i]; | |
119 | ASSERT(EA_PROTO(a->id) == EAP_BGP); | |
120 | code = EA_ID(a->id); | |
e3558ab1 | 121 | flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL); |
f421cfdd MM |
122 | if (code && code < ARRAY_SIZE(bgp_attr_table)) |
123 | { | |
124 | struct attr_desc *desc = &bgp_attr_table[code]; | |
125 | len = desc->expected_length; | |
126 | if (len < 0) | |
127 | { | |
128 | ASSERT(!(a->type & EAF_EMBEDDED)); | |
129 | len = a->u.ptr->length; | |
130 | } | |
131 | } | |
132 | else | |
133 | { | |
134 | ASSERT((a->type & EAF_TYPE_MASK) == EAF_TYPE_OPAQUE); | |
135 | len = a->u.ptr->length; | |
136 | } | |
e3558ab1 | 137 | DBG("\tAttribute %02x (type %02x, %d bytes, flags %02x)\n", code, a->type, len, flags); |
f421cfdd MM |
138 | if (remains < len + 4) |
139 | { | |
140 | log(L_ERR "BGP: attribute list too long, ignoring the remaining attributes"); | |
141 | break; | |
142 | } | |
f421cfdd MM |
143 | if (len < 256) |
144 | { | |
145 | *w++ = flags; | |
146 | *w++ = code; | |
147 | *w++ = len; | |
148 | remains -= 3; | |
149 | } | |
150 | else | |
151 | { | |
152 | *w++ = flags | BAF_EXT_LEN; | |
153 | *w++ = code; | |
154 | put_u16(w, len); | |
155 | w += 2; | |
156 | remains -= 4; | |
157 | } | |
158 | switch (a->type & EAF_TYPE_MASK) | |
159 | { | |
160 | case EAF_TYPE_INT: | |
161 | case EAF_TYPE_ROUTER_ID: | |
162 | if (len == 4) | |
163 | put_u32(w, a->u.data); | |
164 | else | |
165 | *w = a->u.data; | |
166 | break; | |
167 | case EAF_TYPE_IP_ADDRESS: | |
168 | { | |
169 | ip_addr ip = *(ip_addr *)a->u.ptr->data; | |
170 | ipa_hton(ip); | |
171 | memcpy(w, &ip, len); | |
172 | break; | |
173 | } | |
1ed2fe96 MM |
174 | case EAF_TYPE_INT_SET: |
175 | { | |
176 | u32 *z = (u32 *)a->u.ptr->data; | |
177 | int i; | |
178 | for(i=0; i<len; i+=4) | |
179 | put_u32(w+i, *z++); | |
180 | break; | |
181 | } | |
f421cfdd MM |
182 | case EAF_TYPE_OPAQUE: |
183 | case EAF_TYPE_AS_PATH: | |
f421cfdd MM |
184 | memcpy(w, a->u.ptr->data, len); |
185 | break; | |
186 | default: | |
187 | bug("bgp_encode_attrs: unknown attribute type %02x", a->type); | |
188 | } | |
189 | remains -= len; | |
190 | w += len; | |
191 | } | |
192 | put_u16(start, w-start-2); | |
193 | return w; | |
194 | } | |
ae8f5584 | 195 | |
c2b28c99 MM |
196 | static void |
197 | bgp_init_prefix(struct fib_node *N) | |
198 | { | |
199 | struct bgp_prefix *p = (struct bgp_prefix *) N; | |
f421cfdd | 200 | p->bucket_node.next = NULL; |
c2b28c99 MM |
201 | } |
202 | ||
e3558ab1 MM |
203 | static int |
204 | bgp_compare_u32(const u32 *x, const u32 *y) | |
205 | { | |
206 | return (*x < *y) ? -1 : (*x > *y) ? 1 : 0; | |
207 | } | |
208 | ||
ae8f5584 MM |
209 | static void |
210 | bgp_normalize_set(u32 *dest, u32 *src, unsigned cnt) | |
211 | { | |
212 | memcpy(dest, src, sizeof(u32) * cnt); | |
e3558ab1 | 213 | qsort(dest, cnt, sizeof(u32), (int(*)(const void *, const void *)) bgp_compare_u32); |
ae8f5584 MM |
214 | } |
215 | ||
216 | static void | |
217 | bgp_rehash_buckets(struct bgp_proto *p) | |
218 | { | |
c2b28c99 | 219 | struct bgp_bucket **old = p->bucket_hash; |
ae8f5584 MM |
220 | struct bgp_bucket **new; |
221 | unsigned oldn = p->hash_size; | |
222 | unsigned i, e, mask; | |
223 | struct bgp_bucket *b; | |
224 | ||
225 | p->hash_size = p->hash_limit; | |
226 | DBG("BGP: Rehashing bucket table from %d to %d\n", oldn, p->hash_size); | |
227 | p->hash_limit *= 4; | |
228 | if (p->hash_limit >= 65536) | |
229 | p->hash_limit = ~0; | |
c2b28c99 | 230 | new = p->bucket_hash = mb_allocz(p->p.pool, p->hash_size * sizeof(struct bgp_bucket *)); |
ae8f5584 MM |
231 | mask = p->hash_size - 1; |
232 | for (i=0; i<oldn; i++) | |
233 | while (b = old[i]) | |
234 | { | |
c2b28c99 | 235 | old[i] = b->hash_next; |
ae8f5584 | 236 | e = b->hash & mask; |
c2b28c99 MM |
237 | b->hash_next = new[e]; |
238 | if (b->hash_next) | |
239 | b->hash_next->hash_prev = b; | |
240 | b->hash_prev = NULL; | |
ae8f5584 MM |
241 | new[e] = b; |
242 | } | |
243 | mb_free(old); | |
244 | } | |
245 | ||
246 | static struct bgp_bucket * | |
247 | bgp_new_bucket(struct bgp_proto *p, ea_list *new, unsigned hash) | |
248 | { | |
249 | struct bgp_bucket *b; | |
250 | unsigned ea_size = sizeof(ea_list) + new->count * sizeof(eattr); | |
251 | unsigned ea_size_aligned = ALIGN(ea_size, CPU_STRUCT_ALIGN); | |
252 | unsigned size = sizeof(struct bgp_bucket) + ea_size; | |
253 | unsigned i; | |
254 | byte *dest; | |
255 | unsigned index = hash & (p->hash_size - 1); | |
256 | ||
257 | /* Gather total size of non-inline attributes */ | |
258 | for (i=0; i<new->count; i++) | |
259 | { | |
260 | eattr *a = &new->attrs[i]; | |
261 | if (!(a->type & EAF_EMBEDDED)) | |
262 | size += ALIGN(sizeof(struct adata) + a->u.ptr->length, CPU_STRUCT_ALIGN); | |
263 | } | |
264 | ||
265 | /* Create the bucket and hash it */ | |
266 | b = mb_alloc(p->p.pool, size); | |
c2b28c99 MM |
267 | b->hash_next = p->bucket_hash[index]; |
268 | if (b->hash_next) | |
269 | b->hash_next->hash_prev = b; | |
270 | p->bucket_hash[index] = b; | |
271 | b->hash_prev = NULL; | |
ae8f5584 | 272 | b->hash = hash; |
f421cfdd MM |
273 | add_tail(&p->bucket_queue, &b->send_node); |
274 | init_list(&b->prefixes); | |
ae8f5584 MM |
275 | memcpy(b->eattrs, new, ea_size); |
276 | dest = ((byte *)b->eattrs) + ea_size_aligned; | |
277 | ||
278 | /* Copy values of non-inline attributes */ | |
279 | for (i=0; i<new->count; i++) | |
280 | { | |
281 | eattr *a = &new->attrs[i]; | |
282 | if (!(a->type & EAF_EMBEDDED)) | |
283 | { | |
284 | struct adata *oa = a->u.ptr; | |
285 | struct adata *na = (struct adata *) dest; | |
286 | memcpy(na, oa, sizeof(struct adata) + oa->length); | |
287 | a->u.ptr = na; | |
288 | dest += ALIGN(na->length, CPU_STRUCT_ALIGN); | |
289 | } | |
290 | } | |
291 | ||
292 | /* If needed, rehash */ | |
293 | p->hash_count++; | |
294 | if (p->hash_count > p->hash_limit) | |
295 | bgp_rehash_buckets(p); | |
296 | ||
297 | return b; | |
298 | } | |
299 | ||
bd2d8190 MM |
300 | static int |
301 | bgp_export_check(struct bgp_proto *p, ea_list *new) | |
302 | { | |
303 | eattr *a; | |
304 | struct adata *d; | |
305 | ||
306 | /* Check if next hop is valid */ | |
307 | a = ea_find(new, EA_CODE(EAP_BGP, BA_NEXT_HOP)); | |
308 | if (!a || ipa_equal(p->next_hop, *(ip_addr *)a->u.ptr)) | |
309 | { | |
310 | DBG("\tInvalid NEXT_HOP\n"); | |
311 | return 0; | |
312 | } | |
313 | ||
314 | /* Check if we aren't forbidden to export the route by communities */ | |
315 | a = ea_find(new, EA_CODE(EAP_BGP, BA_COMMUNITY)); | |
316 | if (a) | |
317 | { | |
318 | d = a->u.ptr; | |
319 | if (int_set_contains(d, BGP_COMM_NO_ADVERTISE)) | |
320 | { | |
321 | DBG("\tNO_ADVERTISE\n"); | |
322 | return 0; | |
323 | } | |
324 | if (!p->is_internal && | |
325 | (int_set_contains(d, BGP_COMM_NO_EXPORT) || | |
326 | int_set_contains(d, BGP_COMM_NO_EXPORT_SUBCONFED))) | |
327 | { | |
328 | DBG("\tNO_EXPORT\n"); | |
329 | return 0; | |
330 | } | |
331 | } | |
332 | ||
333 | return 1; | |
334 | } | |
335 | ||
ae8f5584 | 336 | static struct bgp_bucket * |
e3558ab1 | 337 | bgp_get_bucket(struct bgp_proto *p, ea_list *old, ea_list *tmp, int originate) |
ae8f5584 MM |
338 | { |
339 | ea_list *t, *new; | |
56a2bed4 | 340 | unsigned i, cnt, hash, code; |
ae8f5584 MM |
341 | eattr *a, *d; |
342 | u32 seen = 0; | |
ae8f5584 MM |
343 | struct bgp_bucket *b; |
344 | ||
345 | /* Merge the attribute lists */ | |
346 | for(t=tmp; t->next; t=t->next) | |
347 | ; | |
348 | t->next = old; | |
349 | new = alloca(ea_scan(tmp)); | |
350 | ea_merge(tmp, new); | |
351 | t->next = NULL; | |
352 | ea_sort(tmp); | |
353 | ||
354 | /* Normalize attributes */ | |
355 | d = new->attrs; | |
356 | cnt = new->count; | |
357 | new->count = 0; | |
358 | for(i=0; i<cnt; i++) | |
359 | { | |
360 | a = &new->attrs[i]; | |
361 | #ifdef LOCAL_DEBUG | |
362 | { | |
1ed2fe96 | 363 | byte buf[EA_FORMAT_BUF_SIZE]; |
ae8f5584 MM |
364 | ea_format(a, buf); |
365 | DBG("\t%s\n", buf); | |
366 | } | |
367 | #endif | |
368 | if (EA_PROTO(a->id) != EAP_BGP) | |
369 | continue; | |
56a2bed4 MM |
370 | code = EA_ID(a->id); |
371 | if (code < ARRAY_SIZE(bgp_attr_table)) | |
372 | { | |
373 | if (!bgp_attr_table[code].allow_in_ebgp && !p->is_internal) | |
374 | continue; | |
375 | } | |
376 | if (code < 32) | |
377 | seen |= 1 << code; | |
ae8f5584 | 378 | *d = *a; |
e3558ab1 MM |
379 | if ((d->type & EAF_ORIGINATED) && !originate && (d->flags & BAF_TRANSITIVE) && (d->flags & BAF_OPTIONAL)) |
380 | d->flags |= BAF_PARTIAL; | |
ae8f5584 MM |
381 | switch (d->type & EAF_TYPE_MASK) |
382 | { | |
e3558ab1 | 383 | case EAF_TYPE_INT_SET: |
ae8f5584 MM |
384 | { |
385 | struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length); | |
386 | z->length = d->u.ptr->length; | |
387 | bgp_normalize_set((u32 *) z->data, (u32 *) d->u.ptr->data, z->length / 4); | |
388 | d->u.ptr = z; | |
389 | break; | |
390 | } | |
391 | default: | |
392 | } | |
393 | d++; | |
394 | new->count++; | |
395 | } | |
396 | ||
397 | /* Hash */ | |
398 | hash = ea_hash(new); | |
c2b28c99 | 399 | for(b=p->bucket_hash[hash & (p->hash_size - 1)]; b; b=b->hash_next) |
ae8f5584 MM |
400 | if (b->hash == hash && ea_same(b->eattrs, new)) |
401 | { | |
402 | DBG("Found bucket.\n"); | |
403 | return b; | |
404 | } | |
405 | ||
406 | /* Ensure that there are all mandatory attributes */ | |
77506349 | 407 | for(i=0; i<ARRAY_SIZE(bgp_mandatory_attrs); i++) |
ae8f5584 MM |
408 | if (!(seen & (1 << bgp_mandatory_attrs[i]))) |
409 | { | |
410 | log(L_ERR "%s: Mandatory attribute %s missing", p->p.name, bgp_attr_table[bgp_mandatory_attrs[i]].name); | |
411 | return NULL; | |
412 | } | |
413 | ||
bd2d8190 | 414 | if (!bgp_export_check(p, new)) |
f421cfdd MM |
415 | return NULL; |
416 | ||
ae8f5584 MM |
417 | /* Create new bucket */ |
418 | DBG("Creating bucket.\n"); | |
419 | return bgp_new_bucket(p, new, hash); | |
420 | } | |
421 | ||
f421cfdd MM |
422 | void |
423 | bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck) | |
424 | { | |
425 | if (buck->hash_next) | |
426 | buck->hash_next->hash_prev = buck->hash_prev; | |
427 | if (buck->hash_prev) | |
428 | buck->hash_prev->hash_next = buck->hash_next; | |
429 | else | |
430 | p->bucket_hash[buck->hash & (p->hash_size-1)] = buck->hash_next; | |
431 | mb_free(buck); | |
432 | } | |
433 | ||
ef2c708d MM |
434 | void |
435 | bgp_rt_notify(struct proto *P, net *n, rte *new, rte *old, ea_list *tmpa) | |
436 | { | |
ae8f5584 | 437 | struct bgp_proto *p = (struct bgp_proto *) P; |
f421cfdd MM |
438 | struct bgp_bucket *buck; |
439 | struct bgp_prefix *px; | |
ae8f5584 | 440 | |
f421cfdd | 441 | DBG("BGP: Got route %I/%d %s\n", n->n.prefix, n->n.pxlen, new ? "up" : "down"); |
ae8f5584 MM |
442 | |
443 | if (new) | |
444 | { | |
e3558ab1 | 445 | buck = bgp_get_bucket(p, new->attrs->eattrs, tmpa, new->attrs->source != RTS_BGP); |
ae8f5584 MM |
446 | if (!buck) /* Inconsistent attribute list */ |
447 | return; | |
448 | } | |
f421cfdd MM |
449 | else |
450 | { | |
451 | if (!(buck = p->withdraw_bucket)) | |
452 | { | |
453 | buck = p->withdraw_bucket = mb_alloc(P->pool, sizeof(struct bgp_bucket)); | |
454 | init_list(&buck->prefixes); | |
455 | } | |
456 | } | |
457 | px = fib_get(&p->prefix_fib, &n->n.prefix, n->n.pxlen); | |
458 | if (px->bucket_node.next) | |
459 | { | |
460 | DBG("\tRemoving old entry.\n"); | |
461 | rem_node(&px->bucket_node); | |
462 | } | |
463 | add_tail(&buck->prefixes, &px->bucket_node); | |
464 | bgp_schedule_packet(p->conn, PKT_UPDATE); | |
ef2c708d MM |
465 | } |
466 | ||
48e842cc MM |
467 | static int |
468 | bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *pool) | |
ef2c708d MM |
469 | { |
470 | ea_list *ea = lp_alloc(pool, sizeof(ea_list) + 3*sizeof(eattr)); | |
471 | eattr *a = ea->attrs; | |
472 | rta *rta = e->attrs; | |
473 | ||
48e842cc MM |
474 | ea->next = *attrs; |
475 | *attrs = ea; | |
ef2c708d MM |
476 | ea->flags = EALF_SORTED; |
477 | ea->count = 3; | |
478 | ||
479 | a->id = EA_CODE(EAP_BGP, BA_ORIGIN); | |
480 | a->flags = BAF_TRANSITIVE; | |
481 | a->type = EAF_TYPE_INT; | |
482 | if (rta->source == RTS_RIP_EXT || rta->source == RTS_OSPF_EXT) | |
483 | a->u.data = 2; /* Incomplete */ | |
484 | else | |
485 | a->u.data = 0; /* IGP */ | |
ae8f5584 | 486 | a++; |
ef2c708d MM |
487 | |
488 | a->id = EA_CODE(EAP_BGP, BA_AS_PATH); | |
489 | a->flags = BAF_TRANSITIVE; | |
490 | a->type = EAF_TYPE_AS_PATH; | |
491 | if (p->is_internal) | |
492 | { | |
493 | a->u.ptr = lp_alloc(pool, sizeof(struct adata)); | |
494 | a->u.ptr->length = 0; | |
495 | } | |
496 | else | |
497 | { | |
498 | byte *z; | |
499 | a->u.ptr = lp_alloc(pool, sizeof(struct adata) + 4); | |
500 | a->u.ptr->length = 4; | |
501 | z = a->u.ptr->data; | |
502 | z[0] = 2; /* AS_SEQUENCE */ | |
503 | z[1] = 1; /* 1 AS */ | |
504 | put_u16(z+2, p->local_as); | |
505 | } | |
ae8f5584 | 506 | a++; |
ef2c708d MM |
507 | |
508 | a->id = EA_CODE(EAP_BGP, BA_NEXT_HOP); | |
509 | a->flags = BAF_TRANSITIVE; | |
510 | a->type = EAF_TYPE_IP_ADDRESS; | |
511 | a->u.ptr = lp_alloc(pool, sizeof(struct adata) + sizeof(ip_addr)); | |
512 | a->u.ptr->length = sizeof(ip_addr); | |
48e842cc MM |
513 | if (p->cf->next_hop_self || |
514 | !p->is_internal || | |
515 | rta->dest != RTD_ROUTER) | |
516 | *(ip_addr *)a->u.ptr->data = p->local_addr; | |
ef2c708d | 517 | else |
48e842cc | 518 | *(ip_addr *)a->u.ptr->data = e->attrs->gw; |
ef2c708d | 519 | |
48e842cc | 520 | return 0; /* Leave decision to the filters */ |
ef2c708d MM |
521 | } |
522 | ||
0a40e973 PM |
523 | static ea_list * |
524 | bgp_path_prepend(struct linpool *pool, eattr *a, ea_list *old, int as) | |
525 | { | |
526 | struct ea_list *e = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr)); | |
527 | struct adata *olda = a->u.ptr; | |
528 | ||
529 | e->next = old; | |
530 | e->flags = EALF_SORTED; | |
531 | e->count = 1; | |
532 | e->attrs[0].id = EA_CODE(EAP_BGP, BA_AS_PATH); | |
533 | e->attrs[0].flags = BAF_TRANSITIVE; | |
534 | e->attrs[0].type = EAF_TYPE_AS_PATH; | |
c8f685cb | 535 | e->attrs[0].u.ptr = as_path_prepend(pool, olda, as); |
ef2c708d MM |
536 | return e; |
537 | } | |
538 | ||
48e842cc MM |
539 | static int |
540 | bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *pool) | |
ef2c708d | 541 | { |
48e842cc MM |
542 | eattr *a; |
543 | ||
ef2c708d | 544 | if (!p->is_internal) |
48e842cc | 545 | *attrs = bgp_path_prepend(pool, ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)), *attrs, p->local_as); |
ef2c708d | 546 | |
48e842cc MM |
547 | a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); |
548 | if (a && (p->is_internal || (!p->is_internal && e->attrs->iface == p->neigh->iface))) | |
549 | { | |
550 | /* Leave the original next hop attribute, will check later where does it point */ | |
551 | } | |
552 | else | |
553 | { | |
554 | /* Need to create new one */ | |
555 | ea_list *ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr)); | |
556 | ea->next = *attrs; | |
557 | *attrs = ea; | |
558 | ea->flags = EALF_SORTED; | |
559 | ea->count = 1; | |
560 | a = ea->attrs; | |
561 | a->id = EA_CODE(EAP_BGP, BA_NEXT_HOP); | |
562 | a->flags = BAF_TRANSITIVE; | |
563 | a->type = EAF_TYPE_IP_ADDRESS; | |
564 | a->u.ptr = lp_alloc(pool, sizeof(struct adata) + sizeof(ip_addr)); | |
565 | a->u.ptr->length = sizeof(ip_addr); | |
566 | *(ip_addr *)a->u.ptr->data = p->local_addr; | |
567 | } | |
ef2c708d | 568 | |
48e842cc | 569 | return 0; /* Leave decision to the filters */ |
ef2c708d MM |
570 | } |
571 | ||
572 | int | |
573 | bgp_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool) | |
574 | { | |
575 | rte *e = *new; | |
576 | struct bgp_proto *p = (struct bgp_proto *) P; | |
577 | struct bgp_proto *new_bgp = (e->attrs->proto->proto == &proto_bgp) ? (struct bgp_proto *) e->attrs->proto : NULL; | |
578 | ||
48e842cc | 579 | if (p == new_bgp) /* Poison reverse updates */ |
ef2c708d MM |
580 | return -1; |
581 | if (new_bgp) | |
582 | { | |
583 | if (p->local_as == new_bgp->local_as && p->is_internal && new_bgp->is_internal) | |
584 | return -1; /* Don't redistribute internal routes with IBGP */ | |
48e842cc | 585 | return bgp_update_attrs(p, e, attrs, pool); |
ef2c708d MM |
586 | } |
587 | else | |
48e842cc | 588 | return bgp_create_attrs(p, e, attrs, pool); |
ef2c708d MM |
589 | } |
590 | ||
591 | int | |
592 | bgp_rte_better(rte *new, rte *old) | |
593 | { | |
594 | struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->proto; | |
595 | struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->proto; | |
56a2bed4 MM |
596 | eattr *x, *y; |
597 | u32 n, o; | |
ef2c708d MM |
598 | |
599 | /* Start with local preferences */ | |
56a2bed4 MM |
600 | x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF)); |
601 | y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF)); | |
602 | n = x ? x->u.data : new_bgp->cf->default_local_pref; | |
603 | o = y ? y->u.data : old_bgp->cf->default_local_pref; | |
604 | if (n > o) | |
605 | return 1; | |
606 | if (n < o) | |
607 | return 0; | |
608 | ||
609 | /* Use AS path lengths */ | |
610 | if (new_bgp->cf->compare_path_lengths || old_bgp->cf->compare_path_lengths) | |
ef2c708d | 611 | { |
56a2bed4 MM |
612 | x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)); |
613 | y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)); | |
614 | n = x ? as_path_getlen(x->u.ptr) : 100000; | |
615 | o = y ? as_path_getlen(y->u.ptr) : 100000; | |
616 | if (n < o) | |
ef2c708d | 617 | return 1; |
56a2bed4 | 618 | if (n > o) |
ef2c708d MM |
619 | return 0; |
620 | } | |
621 | ||
56a2bed4 MM |
622 | /* Use origins */ |
623 | x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN)); | |
624 | y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN)); | |
625 | n = x ? x->u.data : 2; | |
626 | o = y ? y->u.data : 2; | |
627 | if (n < o) | |
628 | return 1; | |
629 | if (n > o) | |
630 | return 0; | |
631 | ||
632 | /* Compare MED's */ | |
633 | x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); | |
634 | y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); | |
635 | n = x ? x->u.data : new_bgp->cf->default_med; | |
636 | o = y ? y->u.data : old_bgp->cf->default_med; | |
637 | if (n < o) | |
638 | return 1; | |
639 | if (n > o) | |
640 | return 0; | |
641 | ||
ef2c708d | 642 | /* A tie breaking procedure according to RFC 1771, section 9.1.2.1 */ |
ef2c708d MM |
643 | /* We don't have interior distances */ |
644 | /* We prefer external peers */ | |
645 | if (new_bgp->is_internal > old_bgp->is_internal) | |
646 | return 0; | |
647 | if (new_bgp->is_internal < old_bgp->is_internal) | |
648 | return 1; | |
649 | /* Finally we compare BGP identifiers */ | |
650 | return (new_bgp->remote_id < old_bgp->remote_id); | |
651 | } | |
652 | ||
ef2c708d MM |
653 | static int |
654 | bgp_path_loopy(struct bgp_proto *p, eattr *a) | |
655 | { | |
656 | byte *path = a->u.ptr->data; | |
657 | int len = a->u.ptr->length; | |
658 | int i, n; | |
659 | ||
660 | while (len > 0) | |
661 | { | |
662 | n = path[1]; | |
663 | len -= 2 - 2*n; | |
664 | path += 2; | |
665 | for(i=0; i<n; i++) | |
666 | { | |
667 | if (get_u16(path) == p->local_as) | |
668 | return 1; | |
669 | path += 2; | |
670 | } | |
671 | } | |
672 | return 0; | |
673 | } | |
674 | ||
c00d31be MM |
675 | struct rta * |
676 | bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct linpool *pool) | |
677 | { | |
678 | struct bgp_proto *bgp = conn->bgp; | |
679 | rta *a = lp_alloc(pool, sizeof(struct rta)); | |
ef2c708d MM |
680 | unsigned int flags, code, l, i, type; |
681 | int errcode; | |
c00d31be MM |
682 | byte *z, *attr_start; |
683 | byte seen[256/8]; | |
684 | eattr *e; | |
685 | ea_list *ea; | |
686 | struct adata *ad; | |
687 | neighbor *neigh; | |
688 | ip_addr nexthop; | |
689 | ||
690 | a->proto = &bgp->p; | |
691 | a->source = RTS_BGP; | |
692 | a->scope = SCOPE_UNIVERSE; | |
693 | a->cast = RTC_UNICAST; | |
694 | a->dest = RTD_ROUTER; | |
695 | a->flags = 0; | |
696 | a->aflags = 0; | |
697 | a->from = bgp->cf->remote_ip; | |
698 | a->eattrs = NULL; | |
699 | ||
700 | /* Parse the attributes */ | |
701 | bzero(seen, sizeof(seen)); | |
702 | DBG("BGP: Parsing attributes\n"); | |
703 | while (len) | |
704 | { | |
705 | if (len < 2) | |
706 | goto malformed; | |
707 | attr_start = attr; | |
708 | flags = *attr++; | |
709 | code = *attr++; | |
710 | len -= 2; | |
711 | if (flags & BAF_EXT_LEN) | |
712 | { | |
713 | if (len < 2) | |
714 | goto malformed; | |
715 | l = get_u16(attr); | |
716 | attr += 2; | |
717 | len -= 2; | |
718 | } | |
719 | else | |
720 | { | |
721 | if (len < 1) | |
722 | goto malformed; | |
723 | l = *attr++; | |
724 | len--; | |
725 | } | |
726 | if (l > len) | |
727 | goto malformed; | |
728 | len -= l; | |
729 | z = attr; | |
730 | attr += l; | |
731 | DBG("Attr %02x %02x %d\n", code, flags, l); | |
732 | if (seen[code/8] & (1 << (code%8))) | |
733 | goto malformed; | |
f421cfdd | 734 | if (code && code < ARRAY_SIZE(bgp_attr_table)) |
c00d31be MM |
735 | { |
736 | struct attr_desc *desc = &bgp_attr_table[code]; | |
737 | if (desc->expected_length >= 0 && desc->expected_length != (int) l) | |
738 | { errcode = 5; goto err; } | |
739 | if ((desc->expected_flags ^ flags) & (BAF_OPTIONAL | BAF_TRANSITIVE)) | |
740 | { errcode = 4; goto err; } | |
56a2bed4 MM |
741 | if (!desc->allow_in_ebgp && !bgp->is_internal) |
742 | continue; | |
ef2c708d MM |
743 | if (desc->validate) |
744 | { | |
745 | errcode = desc->validate(bgp, z, l); | |
746 | if (errcode > 0) | |
747 | goto err; | |
748 | if (errcode < 0) | |
749 | continue; | |
750 | } | |
c00d31be MM |
751 | type = desc->type; |
752 | } | |
753 | else /* Unknown attribute */ | |
e3558ab1 | 754 | { |
c00d31be MM |
755 | if (!(flags & BAF_OPTIONAL)) |
756 | { errcode = 2; goto err; } | |
757 | type = EAF_TYPE_OPAQUE; | |
758 | } | |
ef2c708d MM |
759 | seen[code/8] |= (1 << (code%8)); |
760 | ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr)); | |
c00d31be MM |
761 | ea->next = a->eattrs; |
762 | a->eattrs = ea; | |
763 | ea->flags = 0; | |
764 | ea->count = 1; | |
765 | ea->attrs[0].id = EA_CODE(EAP_BGP, code); | |
766 | ea->attrs[0].flags = flags; | |
767 | ea->attrs[0].type = type; | |
768 | if (type & EAF_EMBEDDED) | |
769 | ad = NULL; | |
770 | else | |
771 | { | |
772 | ad = lp_alloc(pool, sizeof(struct adata) + l); | |
773 | ea->attrs[0].u.ptr = ad; | |
774 | ad->length = l; | |
775 | memcpy(ad->data, z, l); | |
776 | } | |
777 | switch (type) | |
778 | { | |
779 | case EAF_TYPE_ROUTER_ID: | |
780 | case EAF_TYPE_INT: | |
10be74da MM |
781 | if (l == 1) |
782 | ea->attrs[0].u.data = *z; | |
783 | else | |
784 | ea->attrs[0].u.data = get_u32(z); | |
c00d31be MM |
785 | break; |
786 | case EAF_TYPE_IP_ADDRESS: | |
f421cfdd | 787 | ipa_ntoh(*(ip_addr *)ad->data); |
c00d31be | 788 | break; |
1ed2fe96 MM |
789 | case EAF_TYPE_INT_SET: |
790 | { | |
791 | u32 *z = (u32 *) ad->data; | |
792 | for(i=0; i<ad->length/4; i++) | |
793 | z[i] = ntohl(z[i]); | |
794 | break; | |
795 | } | |
c00d31be MM |
796 | } |
797 | } | |
798 | ||
799 | /* Check if all mandatory attributes are present */ | |
800 | for(i=0; i < sizeof(bgp_mandatory_attrs)/sizeof(bgp_mandatory_attrs[0]); i++) | |
801 | { | |
802 | code = bgp_mandatory_attrs[i]; | |
803 | if (!(seen[code/8] & (1 << (code%8)))) | |
804 | { | |
805 | bgp_error(conn, 3, 3, code, 1); | |
806 | return NULL; | |
807 | } | |
808 | } | |
809 | ||
ef2c708d MM |
810 | /* If the AS path attribute contains our AS, reject the routes */ |
811 | e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)); | |
812 | ASSERT(e); | |
813 | if (bgp_path_loopy(bgp, e)) | |
e3558ab1 MM |
814 | { |
815 | DBG("BGP: Path loop!\n"); | |
816 | return NULL; | |
817 | } | |
ef2c708d | 818 | |
c00d31be MM |
819 | /* Fill in the remaining rta fields */ |
820 | e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); | |
821 | ASSERT(e); | |
822 | nexthop = *(ip_addr *) e->u.ptr->data; | |
48e842cc | 823 | if (ipa_equal(nexthop, bgp->local_addr)) |
c00d31be | 824 | { |
e3558ab1 | 825 | DBG("BGP: Loop!\n"); |
c00d31be MM |
826 | return NULL; |
827 | } | |
48e842cc | 828 | neigh = neigh_find(&bgp->p, &nexthop, 0) ? : bgp->neigh; |
c00d31be MM |
829 | a->gw = neigh->addr; |
830 | a->iface = neigh->iface; | |
831 | return rta_lookup(a); | |
832 | ||
833 | malformed: | |
834 | bgp_error(conn, 3, 1, len, 0); | |
835 | return NULL; | |
836 | ||
837 | err: | |
838 | bgp_error(conn, 3, errcode, code, 0); /* FIXME: Return attribute data! */ | |
839 | return NULL; | |
840 | } | |
10be74da MM |
841 | |
842 | int | |
843 | bgp_get_attr(eattr *a, byte *buf) | |
844 | { | |
845 | unsigned int i = EA_ID(a->id); | |
846 | struct attr_desc *d; | |
847 | ||
848 | if (i && i < sizeof(bgp_attr_table)/sizeof(bgp_attr_table[0])) | |
849 | { | |
850 | d = &bgp_attr_table[i]; | |
851 | buf += bsprintf(buf, "%s", d->name); | |
852 | if (d->format) | |
853 | { | |
854 | *buf++ = ':'; | |
855 | *buf++ = ' '; | |
856 | d->format(a, buf); | |
857 | return GA_FULL; | |
858 | } | |
859 | return GA_NAME; | |
860 | } | |
861 | bsprintf(buf, "%02x%s", i, (a->flags & BAF_TRANSITIVE) ? "[t]" : ""); | |
862 | return GA_NAME; | |
863 | } | |
ae8f5584 MM |
864 | |
865 | void | |
866 | bgp_attr_init(struct bgp_proto *p) | |
867 | { | |
868 | p->hash_size = 256; | |
869 | p->hash_limit = p->hash_size * 4; | |
c2b28c99 MM |
870 | p->bucket_hash = mb_allocz(p->p.pool, p->hash_size * sizeof(struct bgp_bucket *)); |
871 | init_list(&p->bucket_queue); | |
872 | p->withdraw_bucket = NULL; | |
873 | fib_init(&p->prefix_fib, p->p.pool, sizeof(struct bgp_prefix), 0, bgp_init_prefix); | |
ae8f5584 | 874 | } |