]>
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 | ||
85368cd4 | 9 | #undef LOCAL_DEBUG |
c00d31be | 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 | |
1c1da87b MM |
25 | static byte bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH |
26 | #ifndef IPV6 | |
27 | ,BA_NEXT_HOP | |
28 | #endif | |
29 | }; | |
ae8f5584 | 30 | |
ae8f5584 | 31 | struct attr_desc { |
99f70c78 | 32 | char *name; |
ae8f5584 MM |
33 | int expected_length; |
34 | int expected_flags; | |
35 | int type; | |
56a2bed4 | 36 | int allow_in_ebgp; |
ae8f5584 MM |
37 | int (*validate)(struct bgp_proto *p, byte *attr, int len); |
38 | void (*format)(eattr *ea, byte *buf); | |
39 | }; | |
40 | ||
f421cfdd | 41 | static int |
e21423ba | 42 | bgp_check_origin(struct bgp_proto *p UNUSED, byte *a UNUSED, int len) |
f421cfdd MM |
43 | { |
44 | if (len > 2) | |
45 | return 6; | |
46 | return 0; | |
47 | } | |
48 | ||
49 | static void | |
50 | bgp_format_origin(eattr *a, byte *buf) | |
51 | { | |
52 | static char *bgp_origin_names[] = { "IGP", "EGP", "Incomplete" }; | |
53 | ||
54 | bsprintf(buf, bgp_origin_names[a->u.data]); | |
55 | } | |
56 | ||
57 | static int | |
11cb6202 | 58 | bgp_check_path(byte *a, int len, int bs, int errcode) |
f421cfdd MM |
59 | { |
60 | while (len) | |
61 | { | |
62 | DBG("Path segment %02x %02x\n", a[0], a[1]); | |
63 | if (len < 2 || | |
11cb6202 OZ |
64 | (a[0] != AS_PATH_SET && a[0] != AS_PATH_SEQUENCE) || |
65 | bs * a[1] + 2 > len) | |
66 | return errcode; | |
67 | len -= bs * a[1] + 2; | |
68 | a += bs * a[1] + 2; | |
f421cfdd MM |
69 | } |
70 | return 0; | |
71 | } | |
72 | ||
11cb6202 OZ |
73 | static int |
74 | bgp_check_as_path(struct bgp_proto *p, byte *a, int len) | |
75 | { | |
ba5ed6f3 | 76 | return bgp_check_path(a, len, p->as4_session ? 4 : 2, 11); |
11cb6202 OZ |
77 | } |
78 | ||
79 | static int | |
80 | bgp_check_as4_path(struct bgp_proto *p, byte *a, int len) | |
81 | { | |
ba5ed6f3 | 82 | if (bgp_as4_support && (! p->as4_session)) |
11cb6202 OZ |
83 | return bgp_check_path(a, len, 4, 9); |
84 | else | |
85 | return 0; | |
86 | } | |
87 | ||
88 | ||
f421cfdd | 89 | static int |
e21423ba | 90 | bgp_check_next_hop(struct bgp_proto *p UNUSED, byte *a, int len) |
f421cfdd | 91 | { |
1c1da87b MM |
92 | #ifdef IPV6 |
93 | return -1; | |
94 | #else | |
f421cfdd MM |
95 | ip_addr addr; |
96 | ||
97 | memcpy(&addr, a, len); | |
98 | ipa_ntoh(addr); | |
99 | if (ipa_classify(addr) & IADDR_HOST) | |
100 | return 0; | |
101 | else | |
102 | return 8; | |
1c1da87b MM |
103 | #endif |
104 | } | |
105 | ||
11cb6202 | 106 | static int |
4847a894 | 107 | bgp_check_aggregator(struct bgp_proto *p, UNUSED byte *a, int len) |
11cb6202 | 108 | { |
ba5ed6f3 | 109 | int exp_len = p->as4_session ? 8 : 6; |
11cb6202 OZ |
110 | |
111 | return (len == exp_len) ? 0 : 5; | |
112 | } | |
113 | ||
4847a894 OZ |
114 | static int |
115 | bgp_check_cluster_list(struct bgp_proto *p UNUSED, UNUSED byte *a, int len) | |
116 | { | |
117 | return ((len % 4) == 0) ? 0 : 5; | |
118 | } | |
119 | ||
1c1da87b | 120 | static int |
e21423ba | 121 | bgp_check_reach_nlri(struct bgp_proto *p UNUSED, byte *a UNUSED, int len UNUSED) |
1c1da87b MM |
122 | { |
123 | #ifdef IPV6 | |
124 | p->mp_reach_start = a; | |
125 | p->mp_reach_len = len; | |
1c1da87b | 126 | #endif |
cf3d6470 | 127 | return -1; |
1c1da87b MM |
128 | } |
129 | ||
130 | static int | |
e21423ba | 131 | bgp_check_unreach_nlri(struct bgp_proto *p UNUSED, byte *a UNUSED, int len UNUSED) |
1c1da87b MM |
132 | { |
133 | #ifdef IPV6 | |
134 | p->mp_unreach_start = a; | |
135 | p->mp_unreach_len = len; | |
1c1da87b | 136 | #endif |
cf3d6470 | 137 | return -1; |
f421cfdd MM |
138 | } |
139 | ||
f421cfdd | 140 | static struct attr_desc bgp_attr_table[] = { |
1c1da87b | 141 | { NULL, -1, 0, 0, 0, /* Undefined */ |
f421cfdd | 142 | NULL, NULL }, |
1c1da87b | 143 | { "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* BA_ORIGIN */ |
f421cfdd | 144 | bgp_check_origin, bgp_format_origin }, |
1c1da87b | 145 | { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* BA_AS_PATH */ |
11cb6202 | 146 | bgp_check_as_path, NULL }, |
1c1da87b | 147 | { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* BA_NEXT_HOP */ |
f421cfdd | 148 | bgp_check_next_hop, NULL }, |
b6bf284a | 149 | { "med", 4, BAF_OPTIONAL, EAF_TYPE_INT, 1, /* BA_MULTI_EXIT_DISC */ |
f421cfdd | 150 | NULL, NULL }, |
2138d3b4 | 151 | { "local_pref", 4, BAF_TRANSITIVE, EAF_TYPE_INT, 0, /* BA_LOCAL_PREF */ |
f421cfdd | 152 | NULL, NULL }, |
1c1da87b | 153 | { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */ |
f421cfdd | 154 | NULL, NULL }, |
11cb6202 OZ |
155 | { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AGGREGATOR */ |
156 | bgp_check_aggregator, NULL }, | |
1c1da87b | 157 | { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */ |
1ed2fe96 | 158 | NULL, NULL }, |
4847a894 OZ |
159 | { "originator_id", 4, BAF_OPTIONAL, EAF_TYPE_INT, 0, /* BA_ORIGINATOR_ID */ |
160 | NULL, NULL }, | |
161 | { "cluster_list", -1, BAF_OPTIONAL, EAF_TYPE_INT_SET, 0, /* BA_CLUSTER_LIST */ | |
162 | bgp_check_cluster_list, NULL }, | |
1c1da87b MM |
163 | { NULL, }, /* BA_DPA */ |
164 | { NULL, }, /* BA_ADVERTISER */ | |
165 | { NULL, }, /* BA_RCID_PATH */ | |
166 | { "mp_reach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_REACH_NLRI */ | |
167 | bgp_check_reach_nlri, NULL }, | |
168 | { "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_UNREACH_NLRI */ | |
169 | bgp_check_unreach_nlri, NULL }, | |
11cb6202 OZ |
170 | { NULL, }, /* BA_EXTENDED_COMM */ |
171 | { "as4_path", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */ | |
172 | bgp_check_as4_path, NULL }, | |
173 | { "as4_aggregator", 8, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */ | |
174 | NULL, NULL } | |
f421cfdd MM |
175 | }; |
176 | ||
11cb6202 OZ |
177 | /* BA_AS4_PATH is type EAF_TYPE_OPAQUE and not type EAF_TYPE_AS_PATH because |
178 | * EAF_TYPE_AS_PATH is supposed to have different format (2 or 4 B for each ASN) | |
179 | * depending on bgp_as4_support variable. | |
180 | */ | |
181 | ||
d1a74339 MM |
182 | #define ATTR_KNOWN(code) ((code) < ARRAY_SIZE(bgp_attr_table) && bgp_attr_table[code].name) |
183 | ||
4847a894 OZ |
184 | static inline struct adata * |
185 | bgp_alloc_adata(struct linpool *pool, unsigned len) | |
186 | { | |
187 | struct adata *ad = lp_alloc(pool, sizeof(struct adata) + len); | |
188 | ad->length = len; | |
189 | return ad; | |
190 | } | |
191 | ||
192 | static void | |
193 | bgp_set_attr(eattr *e, unsigned attr, uintptr_t val) | |
cf3d6470 MM |
194 | { |
195 | ASSERT(ATTR_KNOWN(attr)); | |
196 | e->id = EA_CODE(EAP_BGP, attr); | |
197 | e->type = bgp_attr_table[attr].type; | |
198 | e->flags = bgp_attr_table[attr].expected_flags; | |
199 | if (e->type & EAF_EMBEDDED) | |
4847a894 | 200 | e->u.data = val; |
cf3d6470 | 201 | else |
4847a894 | 202 | e->u.ptr = (struct adata *) val; |
cf3d6470 MM |
203 | } |
204 | ||
4847a894 OZ |
205 | static byte * |
206 | bgp_set_attr_wa(eattr *e, struct linpool *pool, unsigned attr, unsigned len) | |
207 | { | |
208 | struct adata *ad = bgp_alloc_adata(pool, len); | |
209 | bgp_set_attr(e, attr, (uintptr_t) ad); | |
210 | return ad->data; | |
211 | } | |
212 | ||
213 | void | |
214 | bgp_attach_attr(ea_list **to, struct linpool *pool, unsigned attr, uintptr_t val) | |
cf3d6470 MM |
215 | { |
216 | ea_list *a = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr)); | |
217 | a->next = *to; | |
218 | *to = a; | |
219 | a->flags = EALF_SORTED; | |
220 | a->count = 1; | |
4847a894 OZ |
221 | bgp_set_attr(a->attrs, attr, val); |
222 | } | |
223 | ||
224 | byte * | |
225 | bgp_attach_attr_wa(ea_list **to, struct linpool *pool, unsigned attr, unsigned len) | |
226 | { | |
227 | struct adata *ad = bgp_alloc_adata(pool, len); | |
228 | bgp_attach_attr(to, pool, attr, (uintptr_t) ad); | |
229 | return ad->data; | |
cf3d6470 MM |
230 | } |
231 | ||
11cb6202 OZ |
232 | static int |
233 | bgp_encode_attr_hdr(byte *dst, unsigned int flags, unsigned code, int len) | |
234 | { | |
235 | int wlen; | |
236 | ||
237 | DBG("\tAttribute %02x (%d bytes, flags %02x)\n", code, len, flags); | |
238 | ||
239 | if (len < 256) | |
240 | { | |
241 | *dst++ = flags; | |
242 | *dst++ = code; | |
243 | *dst++ = len; | |
244 | wlen = 3; | |
245 | } | |
246 | else | |
247 | { | |
248 | *dst++ = flags | BAF_EXT_LEN; | |
249 | *dst++ = code; | |
250 | put_u16(dst, len); | |
251 | wlen = 4; | |
252 | } | |
253 | ||
254 | return wlen; | |
255 | } | |
256 | ||
257 | static void | |
258 | aggregator_convert_to_old(struct adata *aggr, byte *dst, int *new_used) | |
259 | { | |
260 | byte *src = aggr->data; | |
261 | *new_used = 0; | |
262 | ||
263 | u32 as = get_u32(src); | |
264 | if (as > 0xFFFF) | |
265 | { | |
266 | as = AS_TRANS; | |
267 | *new_used = 1; | |
268 | } | |
269 | put_u16(dst, as); | |
270 | ||
271 | /* Copy IPv4 address */ | |
272 | memcpy(dst + 2, src + 4, 4); | |
273 | } | |
274 | ||
275 | static void | |
276 | aggregator_convert_to_new(struct adata *aggr, byte *dst) | |
277 | { | |
278 | byte *src = aggr->data; | |
279 | ||
280 | u32 as = get_u16(src); | |
281 | put_u32(dst, as); | |
282 | ||
283 | /* Copy IPv4 address */ | |
284 | memcpy(dst + 4, src + 2, 4); | |
285 | } | |
286 | ||
287 | static int | |
288 | bgp_get_attr_len(eattr *a) | |
289 | { | |
290 | int len; | |
291 | if (ATTR_KNOWN(EA_ID(a->id))) | |
292 | { | |
293 | int code = EA_ID(a->id); | |
294 | struct attr_desc *desc = &bgp_attr_table[code]; | |
295 | len = desc->expected_length; | |
296 | if (len < 0) | |
297 | { | |
298 | ASSERT(!(a->type & EAF_EMBEDDED)); | |
299 | len = a->u.ptr->length; | |
300 | } | |
301 | } | |
302 | else | |
303 | { | |
304 | ASSERT((a->type & EAF_TYPE_MASK) == EAF_TYPE_OPAQUE); | |
305 | len = a->u.ptr->length; | |
306 | } | |
307 | ||
308 | return len; | |
309 | } | |
310 | ||
311 | #define ADVANCE(w, r, l) do { r -= l; w += l; } while (0) | |
312 | ||
54e55169 MM |
313 | /** |
314 | * bgp_encode_attrs - encode BGP attributes | |
11cb6202 | 315 | * @p: BGP instance |
54e55169 MM |
316 | * @w: buffer |
317 | * @attrs: a list of extended attributes | |
318 | * @remains: remaining space in the buffer | |
319 | * | |
320 | * The bgp_encode_attrs() function takes a list of extended attributes | |
321 | * and converts it to its BGP representation (a part of an Update message). | |
322 | * | |
323 | * Result: Length of the attribute block generated. | |
324 | */ | |
cf3d6470 | 325 | unsigned int |
11cb6202 | 326 | bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains) |
f421cfdd | 327 | { |
f421cfdd MM |
328 | unsigned int i, code, flags; |
329 | byte *start = w; | |
11cb6202 | 330 | int len, rv; |
f421cfdd | 331 | |
cf3d6470 | 332 | for(i=0; i<attrs->count; i++) |
f421cfdd | 333 | { |
cf3d6470 | 334 | eattr *a = &attrs->attrs[i]; |
f421cfdd MM |
335 | ASSERT(EA_PROTO(a->id) == EAP_BGP); |
336 | code = EA_ID(a->id); | |
cf3d6470 MM |
337 | #ifdef IPV6 |
338 | /* When talking multiprotocol BGP, the NEXT_HOP attributes are used only temporarily. */ | |
339 | if (code == BA_NEXT_HOP) | |
340 | continue; | |
341 | #endif | |
11cb6202 OZ |
342 | |
343 | /* When AS4-aware BGP speaker is talking to non-AS4-aware BGP speaker, | |
344 | * we have to convert our 4B AS_PATH to 2B AS_PATH and send our AS_PATH | |
345 | * as optional AS4_PATH attribute. | |
346 | */ | |
ba5ed6f3 | 347 | if ((code == BA_AS_PATH) && bgp_as4_support && (! p->as4_session)) |
f421cfdd | 348 | { |
f421cfdd | 349 | len = a->u.ptr->length; |
11cb6202 OZ |
350 | |
351 | if (remains < (len + 4)) | |
352 | goto err_no_buffer; | |
353 | ||
354 | /* Using temporary buffer because don't know a length of created attr | |
355 | * and therefore a length of a header. Perhaps i should better always | |
356 | * use BAF_EXT_LEN. */ | |
357 | ||
358 | byte buf[len]; | |
359 | int new_used; | |
360 | int nl = as_path_convert_to_old(a->u.ptr, buf, &new_used); | |
361 | ||
362 | rv = bgp_encode_attr_hdr(w, BAF_TRANSITIVE, BA_AS_PATH, nl); | |
363 | ADVANCE(w, remains, rv); | |
364 | memcpy(w, buf, nl); | |
365 | ADVANCE(w, remains, nl); | |
366 | ||
367 | if (! new_used) | |
368 | continue; | |
369 | ||
370 | if (remains < (len + 4)) | |
371 | goto err_no_buffer; | |
372 | ||
373 | /* We should discard AS_CONFED_SEQUENCE or AS_CONFED_SET path segments | |
374 | * here but we don't support confederations and such paths we already | |
375 | * discarded in bgp_check_as_path(). | |
376 | */ | |
377 | ||
378 | rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AS4_PATH, len); | |
379 | ADVANCE(w, remains, rv); | |
380 | memcpy(w, a->u.ptr->data, len); | |
381 | ADVANCE(w, remains, len); | |
382 | ||
383 | continue; | |
f421cfdd | 384 | } |
11cb6202 OZ |
385 | |
386 | /* The same issue with AGGREGATOR attribute */ | |
ba5ed6f3 | 387 | if ((code == BA_AGGREGATOR) && bgp_as4_support && (! p->as4_session)) |
f421cfdd | 388 | { |
11cb6202 OZ |
389 | int new_used; |
390 | ||
391 | len = 6; | |
392 | if (remains < (len + 3)) | |
393 | goto err_no_buffer; | |
394 | ||
395 | rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AGGREGATOR, len); | |
396 | ADVANCE(w, remains, rv); | |
397 | aggregator_convert_to_old(a->u.ptr, w, &new_used); | |
398 | ADVANCE(w, remains, len); | |
399 | ||
400 | if (! new_used) | |
401 | continue; | |
402 | ||
403 | len = 8; | |
404 | if (remains < (len + 3)) | |
405 | goto err_no_buffer; | |
406 | ||
407 | rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AS4_AGGREGATOR, len); | |
408 | ADVANCE(w, remains, rv); | |
409 | memcpy(w, a->u.ptr->data, len); | |
410 | ADVANCE(w, remains, len); | |
411 | ||
412 | continue; | |
f421cfdd | 413 | } |
11cb6202 OZ |
414 | |
415 | /* Standard path continues here ... */ | |
416 | ||
417 | flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL); | |
418 | len = bgp_get_attr_len(a); | |
419 | ||
420 | if (remains < len + 4) | |
421 | goto err_no_buffer; | |
422 | ||
423 | rv = bgp_encode_attr_hdr(w, flags, code, len); | |
424 | ADVANCE(w, remains, rv); | |
425 | ||
f421cfdd MM |
426 | switch (a->type & EAF_TYPE_MASK) |
427 | { | |
428 | case EAF_TYPE_INT: | |
429 | case EAF_TYPE_ROUTER_ID: | |
430 | if (len == 4) | |
431 | put_u32(w, a->u.data); | |
432 | else | |
433 | *w = a->u.data; | |
434 | break; | |
435 | case EAF_TYPE_IP_ADDRESS: | |
436 | { | |
437 | ip_addr ip = *(ip_addr *)a->u.ptr->data; | |
438 | ipa_hton(ip); | |
439 | memcpy(w, &ip, len); | |
440 | break; | |
441 | } | |
1ed2fe96 MM |
442 | case EAF_TYPE_INT_SET: |
443 | { | |
444 | u32 *z = (u32 *)a->u.ptr->data; | |
445 | int i; | |
446 | for(i=0; i<len; i+=4) | |
447 | put_u32(w+i, *z++); | |
448 | break; | |
449 | } | |
f421cfdd MM |
450 | case EAF_TYPE_OPAQUE: |
451 | case EAF_TYPE_AS_PATH: | |
f421cfdd MM |
452 | memcpy(w, a->u.ptr->data, len); |
453 | break; | |
454 | default: | |
455 | bug("bgp_encode_attrs: unknown attribute type %02x", a->type); | |
456 | } | |
11cb6202 | 457 | ADVANCE(w, remains, len); |
f421cfdd | 458 | } |
cf3d6470 | 459 | return w - start; |
11cb6202 OZ |
460 | |
461 | err_no_buffer: | |
462 | log(L_ERR "BGP: attribute list too long, ignoring the remaining attributes"); | |
463 | return w - start; | |
f421cfdd | 464 | } |
ae8f5584 | 465 | |
c2b28c99 MM |
466 | static void |
467 | bgp_init_prefix(struct fib_node *N) | |
468 | { | |
469 | struct bgp_prefix *p = (struct bgp_prefix *) N; | |
f421cfdd | 470 | p->bucket_node.next = NULL; |
c2b28c99 MM |
471 | } |
472 | ||
e3558ab1 MM |
473 | static int |
474 | bgp_compare_u32(const u32 *x, const u32 *y) | |
475 | { | |
476 | return (*x < *y) ? -1 : (*x > *y) ? 1 : 0; | |
477 | } | |
478 | ||
ae8f5584 MM |
479 | static void |
480 | bgp_normalize_set(u32 *dest, u32 *src, unsigned cnt) | |
481 | { | |
482 | memcpy(dest, src, sizeof(u32) * cnt); | |
e3558ab1 | 483 | qsort(dest, cnt, sizeof(u32), (int(*)(const void *, const void *)) bgp_compare_u32); |
ae8f5584 MM |
484 | } |
485 | ||
486 | static void | |
487 | bgp_rehash_buckets(struct bgp_proto *p) | |
488 | { | |
c2b28c99 | 489 | struct bgp_bucket **old = p->bucket_hash; |
ae8f5584 MM |
490 | struct bgp_bucket **new; |
491 | unsigned oldn = p->hash_size; | |
492 | unsigned i, e, mask; | |
493 | struct bgp_bucket *b; | |
494 | ||
495 | p->hash_size = p->hash_limit; | |
496 | DBG("BGP: Rehashing bucket table from %d to %d\n", oldn, p->hash_size); | |
497 | p->hash_limit *= 4; | |
498 | if (p->hash_limit >= 65536) | |
499 | p->hash_limit = ~0; | |
c2b28c99 | 500 | new = p->bucket_hash = mb_allocz(p->p.pool, p->hash_size * sizeof(struct bgp_bucket *)); |
ae8f5584 MM |
501 | mask = p->hash_size - 1; |
502 | for (i=0; i<oldn; i++) | |
503 | while (b = old[i]) | |
504 | { | |
c2b28c99 | 505 | old[i] = b->hash_next; |
ae8f5584 | 506 | e = b->hash & mask; |
c2b28c99 MM |
507 | b->hash_next = new[e]; |
508 | if (b->hash_next) | |
509 | b->hash_next->hash_prev = b; | |
510 | b->hash_prev = NULL; | |
ae8f5584 MM |
511 | new[e] = b; |
512 | } | |
513 | mb_free(old); | |
514 | } | |
515 | ||
516 | static struct bgp_bucket * | |
517 | bgp_new_bucket(struct bgp_proto *p, ea_list *new, unsigned hash) | |
518 | { | |
519 | struct bgp_bucket *b; | |
520 | unsigned ea_size = sizeof(ea_list) + new->count * sizeof(eattr); | |
7fdd338c | 521 | unsigned ea_size_aligned = BIRD_ALIGN(ea_size, CPU_STRUCT_ALIGN); |
ae8f5584 MM |
522 | unsigned size = sizeof(struct bgp_bucket) + ea_size; |
523 | unsigned i; | |
524 | byte *dest; | |
525 | unsigned index = hash & (p->hash_size - 1); | |
526 | ||
527 | /* Gather total size of non-inline attributes */ | |
528 | for (i=0; i<new->count; i++) | |
529 | { | |
530 | eattr *a = &new->attrs[i]; | |
531 | if (!(a->type & EAF_EMBEDDED)) | |
7fdd338c | 532 | size += BIRD_ALIGN(sizeof(struct adata) + a->u.ptr->length, CPU_STRUCT_ALIGN); |
ae8f5584 MM |
533 | } |
534 | ||
535 | /* Create the bucket and hash it */ | |
536 | b = mb_alloc(p->p.pool, size); | |
c2b28c99 MM |
537 | b->hash_next = p->bucket_hash[index]; |
538 | if (b->hash_next) | |
539 | b->hash_next->hash_prev = b; | |
540 | p->bucket_hash[index] = b; | |
541 | b->hash_prev = NULL; | |
ae8f5584 | 542 | b->hash = hash; |
f421cfdd MM |
543 | add_tail(&p->bucket_queue, &b->send_node); |
544 | init_list(&b->prefixes); | |
ae8f5584 MM |
545 | memcpy(b->eattrs, new, ea_size); |
546 | dest = ((byte *)b->eattrs) + ea_size_aligned; | |
547 | ||
548 | /* Copy values of non-inline attributes */ | |
549 | for (i=0; i<new->count; i++) | |
550 | { | |
85368cd4 | 551 | eattr *a = &b->eattrs->attrs[i]; |
ae8f5584 MM |
552 | if (!(a->type & EAF_EMBEDDED)) |
553 | { | |
554 | struct adata *oa = a->u.ptr; | |
555 | struct adata *na = (struct adata *) dest; | |
556 | memcpy(na, oa, sizeof(struct adata) + oa->length); | |
557 | a->u.ptr = na; | |
7fdd338c | 558 | dest += BIRD_ALIGN(sizeof(struct adata) + na->length, CPU_STRUCT_ALIGN); |
ae8f5584 MM |
559 | } |
560 | } | |
561 | ||
562 | /* If needed, rehash */ | |
563 | p->hash_count++; | |
564 | if (p->hash_count > p->hash_limit) | |
565 | bgp_rehash_buckets(p); | |
566 | ||
567 | return b; | |
568 | } | |
569 | ||
bd2d8190 MM |
570 | static int |
571 | bgp_export_check(struct bgp_proto *p, ea_list *new) | |
572 | { | |
573 | eattr *a; | |
574 | struct adata *d; | |
575 | ||
576 | /* Check if next hop is valid */ | |
577 | a = ea_find(new, EA_CODE(EAP_BGP, BA_NEXT_HOP)); | |
578 | if (!a || ipa_equal(p->next_hop, *(ip_addr *)a->u.ptr)) | |
579 | { | |
580 | DBG("\tInvalid NEXT_HOP\n"); | |
581 | return 0; | |
582 | } | |
583 | ||
584 | /* Check if we aren't forbidden to export the route by communities */ | |
585 | a = ea_find(new, EA_CODE(EAP_BGP, BA_COMMUNITY)); | |
586 | if (a) | |
587 | { | |
588 | d = a->u.ptr; | |
589 | if (int_set_contains(d, BGP_COMM_NO_ADVERTISE)) | |
590 | { | |
591 | DBG("\tNO_ADVERTISE\n"); | |
592 | return 0; | |
593 | } | |
594 | if (!p->is_internal && | |
595 | (int_set_contains(d, BGP_COMM_NO_EXPORT) || | |
596 | int_set_contains(d, BGP_COMM_NO_EXPORT_SUBCONFED))) | |
597 | { | |
598 | DBG("\tNO_EXPORT\n"); | |
599 | return 0; | |
600 | } | |
601 | } | |
602 | ||
603 | return 1; | |
604 | } | |
605 | ||
ae8f5584 | 606 | static struct bgp_bucket * |
02bd064a | 607 | bgp_get_bucket(struct bgp_proto *p, ea_list *attrs, int originate) |
ae8f5584 | 608 | { |
e21423ba | 609 | ea_list *new; |
56a2bed4 | 610 | unsigned i, cnt, hash, code; |
ae8f5584 MM |
611 | eattr *a, *d; |
612 | u32 seen = 0; | |
ae8f5584 MM |
613 | struct bgp_bucket *b; |
614 | ||
02bd064a MM |
615 | /* Merge the attribute list */ |
616 | new = alloca(ea_scan(attrs)); | |
617 | ea_merge(attrs, new); | |
8b258e4e | 618 | ea_sort(new); |
ae8f5584 MM |
619 | |
620 | /* Normalize attributes */ | |
621 | d = new->attrs; | |
622 | cnt = new->count; | |
623 | new->count = 0; | |
624 | for(i=0; i<cnt; i++) | |
625 | { | |
626 | a = &new->attrs[i]; | |
627 | #ifdef LOCAL_DEBUG | |
628 | { | |
1ed2fe96 | 629 | byte buf[EA_FORMAT_BUF_SIZE]; |
ae8f5584 MM |
630 | ea_format(a, buf); |
631 | DBG("\t%s\n", buf); | |
632 | } | |
633 | #endif | |
634 | if (EA_PROTO(a->id) != EAP_BGP) | |
635 | continue; | |
56a2bed4 | 636 | code = EA_ID(a->id); |
d1a74339 | 637 | if (ATTR_KNOWN(code)) |
56a2bed4 MM |
638 | { |
639 | if (!bgp_attr_table[code].allow_in_ebgp && !p->is_internal) | |
640 | continue; | |
684c25d9 MM |
641 | /* The flags might have been zero if the attr was added by filters */ |
642 | a->flags = (a->flags & BAF_PARTIAL) | bgp_attr_table[code].expected_flags; | |
d1a74339 MM |
643 | if (code < 32) |
644 | seen |= 1 << code; | |
645 | } | |
646 | else | |
647 | { | |
648 | /* Don't re-export unknown non-transitive attributes */ | |
649 | if (!(a->flags & BAF_TRANSITIVE)) | |
650 | continue; | |
56a2bed4 | 651 | } |
ae8f5584 | 652 | *d = *a; |
e3558ab1 MM |
653 | if ((d->type & EAF_ORIGINATED) && !originate && (d->flags & BAF_TRANSITIVE) && (d->flags & BAF_OPTIONAL)) |
654 | d->flags |= BAF_PARTIAL; | |
ae8f5584 MM |
655 | switch (d->type & EAF_TYPE_MASK) |
656 | { | |
e3558ab1 | 657 | case EAF_TYPE_INT_SET: |
ae8f5584 MM |
658 | { |
659 | struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length); | |
660 | z->length = d->u.ptr->length; | |
661 | bgp_normalize_set((u32 *) z->data, (u32 *) d->u.ptr->data, z->length / 4); | |
662 | d->u.ptr = z; | |
663 | break; | |
664 | } | |
de10a974 | 665 | default: ; |
ae8f5584 MM |
666 | } |
667 | d++; | |
668 | new->count++; | |
669 | } | |
670 | ||
671 | /* Hash */ | |
672 | hash = ea_hash(new); | |
c2b28c99 | 673 | for(b=p->bucket_hash[hash & (p->hash_size - 1)]; b; b=b->hash_next) |
ae8f5584 MM |
674 | if (b->hash == hash && ea_same(b->eattrs, new)) |
675 | { | |
676 | DBG("Found bucket.\n"); | |
677 | return b; | |
678 | } | |
679 | ||
680 | /* Ensure that there are all mandatory attributes */ | |
77506349 | 681 | for(i=0; i<ARRAY_SIZE(bgp_mandatory_attrs); i++) |
ae8f5584 MM |
682 | if (!(seen & (1 << bgp_mandatory_attrs[i]))) |
683 | { | |
684 | log(L_ERR "%s: Mandatory attribute %s missing", p->p.name, bgp_attr_table[bgp_mandatory_attrs[i]].name); | |
685 | return NULL; | |
686 | } | |
687 | ||
bd2d8190 | 688 | if (!bgp_export_check(p, new)) |
f421cfdd MM |
689 | return NULL; |
690 | ||
ae8f5584 MM |
691 | /* Create new bucket */ |
692 | DBG("Creating bucket.\n"); | |
693 | return bgp_new_bucket(p, new, hash); | |
694 | } | |
695 | ||
f421cfdd MM |
696 | void |
697 | bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck) | |
698 | { | |
699 | if (buck->hash_next) | |
700 | buck->hash_next->hash_prev = buck->hash_prev; | |
701 | if (buck->hash_prev) | |
702 | buck->hash_prev->hash_next = buck->hash_next; | |
703 | else | |
704 | p->bucket_hash[buck->hash & (p->hash_size-1)] = buck->hash_next; | |
705 | mb_free(buck); | |
706 | } | |
707 | ||
ef2c708d | 708 | void |
e21423ba | 709 | bgp_rt_notify(struct proto *P, net *n, rte *new, rte *old UNUSED, ea_list *attrs) |
ef2c708d | 710 | { |
ae8f5584 | 711 | struct bgp_proto *p = (struct bgp_proto *) P; |
f421cfdd MM |
712 | struct bgp_bucket *buck; |
713 | struct bgp_prefix *px; | |
ae8f5584 | 714 | |
f421cfdd | 715 | DBG("BGP: Got route %I/%d %s\n", n->n.prefix, n->n.pxlen, new ? "up" : "down"); |
ae8f5584 MM |
716 | |
717 | if (new) | |
718 | { | |
02bd064a | 719 | buck = bgp_get_bucket(p, attrs, new->attrs->source != RTS_BGP); |
ae8f5584 MM |
720 | if (!buck) /* Inconsistent attribute list */ |
721 | return; | |
722 | } | |
f421cfdd MM |
723 | else |
724 | { | |
725 | if (!(buck = p->withdraw_bucket)) | |
726 | { | |
727 | buck = p->withdraw_bucket = mb_alloc(P->pool, sizeof(struct bgp_bucket)); | |
728 | init_list(&buck->prefixes); | |
729 | } | |
730 | } | |
731 | px = fib_get(&p->prefix_fib, &n->n.prefix, n->n.pxlen); | |
732 | if (px->bucket_node.next) | |
733 | { | |
734 | DBG("\tRemoving old entry.\n"); | |
735 | rem_node(&px->bucket_node); | |
736 | } | |
737 | add_tail(&buck->prefixes, &px->bucket_node); | |
738 | bgp_schedule_packet(p->conn, PKT_UPDATE); | |
ef2c708d MM |
739 | } |
740 | ||
4847a894 | 741 | |
48e842cc MM |
742 | static int |
743 | bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *pool) | |
ef2c708d | 744 | { |
8b258e4e | 745 | ea_list *ea = lp_alloc(pool, sizeof(ea_list) + 4*sizeof(eattr)); |
ef2c708d | 746 | rta *rta = e->attrs; |
cf3d6470 | 747 | byte *z; |
ef2c708d | 748 | |
48e842cc MM |
749 | ea->next = *attrs; |
750 | *attrs = ea; | |
ef2c708d | 751 | ea->flags = EALF_SORTED; |
8b258e4e | 752 | ea->count = 4; |
ef2c708d | 753 | |
4847a894 | 754 | bgp_set_attr(ea->attrs, BA_ORIGIN, |
98ac6176 | 755 | ((rta->source == RTS_OSPF_EXT1) || (rta->source == RTS_OSPF_EXT2)) ? ORIGIN_INCOMPLETE : ORIGIN_IGP); |
ef2c708d | 756 | |
ef2c708d | 757 | if (p->is_internal) |
4847a894 | 758 | bgp_set_attr_wa(ea->attrs+1, pool, BA_AS_PATH, 0); |
ef2c708d MM |
759 | else |
760 | { | |
4847a894 | 761 | z = bgp_set_attr_wa(ea->attrs+1, pool, BA_AS_PATH, bgp_as4_support ? 6 : 4); |
8b258e4e | 762 | z[0] = AS_PATH_SEQUENCE; |
ef2c708d | 763 | z[1] = 1; /* 1 AS */ |
11cb6202 OZ |
764 | |
765 | if (bgp_as4_support) | |
766 | put_u32(z+2, p->local_as); | |
767 | else | |
768 | put_u16(z+2, p->local_as); | |
ef2c708d MM |
769 | } |
770 | ||
4847a894 | 771 | z = bgp_set_attr_wa(ea->attrs+2, pool, BA_NEXT_HOP, sizeof(ip_addr)); |
48e842cc MM |
772 | if (p->cf->next_hop_self || |
773 | !p->is_internal || | |
774 | rta->dest != RTD_ROUTER) | |
85ae398a OF |
775 | { |
776 | if (ipa_nonzero(p->cf->source_addr)) | |
777 | *(ip_addr *)z = p->cf->source_addr; | |
778 | else | |
779 | *(ip_addr *)z = p->local_addr; | |
780 | } | |
ef2c708d | 781 | else |
cf3d6470 | 782 | *(ip_addr *)z = e->attrs->gw; |
8b258e4e | 783 | |
4847a894 | 784 | bgp_set_attr(ea->attrs+3, BA_LOCAL_PREF, 0); |
ef2c708d | 785 | |
48e842cc | 786 | return 0; /* Leave decision to the filters */ |
ef2c708d MM |
787 | } |
788 | ||
4847a894 OZ |
789 | |
790 | static inline int | |
791 | bgp_as_path_loopy(struct bgp_proto *p, rta *a) | |
792 | { | |
793 | eattr *e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)); | |
794 | return (e && as_path_is_member(e->u.ptr, p->local_as)); | |
795 | } | |
796 | ||
797 | static inline int | |
798 | bgp_originator_id_loopy(struct bgp_proto *p, rta *a) | |
0a40e973 | 799 | { |
4847a894 OZ |
800 | eattr *e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID)); |
801 | return (e && (e->u.data == p->local_id)); | |
802 | } | |
803 | ||
804 | static inline int | |
805 | bgp_cluster_list_loopy(struct bgp_proto *p, rta *a) | |
806 | { | |
807 | eattr *e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_CLUSTER_LIST)); | |
808 | return (e && p->rr_client && int_set_contains(e->u.ptr, p->rr_cluster_id)); | |
809 | } | |
810 | ||
811 | ||
812 | static inline void | |
813 | bgp_path_prepend(rte *e, ea_list **attrs, struct linpool *pool, u32 as) | |
814 | { | |
815 | eattr *a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)); | |
816 | bgp_attach_attr(attrs, pool, BA_AS_PATH, (uintptr_t) as_path_prepend(pool, a->u.ptr, as)); | |
817 | } | |
818 | ||
819 | static inline void | |
820 | bgp_cluster_list_prepend(rte *e, ea_list **attrs, struct linpool *pool, u32 cid) | |
821 | { | |
822 | eattr *a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_CLUSTER_LIST)); | |
823 | bgp_attach_attr(attrs, pool, BA_CLUSTER_LIST, (uintptr_t) int_set_add(pool, a ? a->u.ptr : NULL, cid)); | |
ef2c708d MM |
824 | } |
825 | ||
48e842cc | 826 | static int |
4847a894 | 827 | bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *pool, int rr) |
ef2c708d | 828 | { |
48e842cc MM |
829 | eattr *a; |
830 | ||
a92fe607 | 831 | if (!p->is_internal && !p->rs_client) |
b6bf284a OZ |
832 | { |
833 | bgp_path_prepend(e, attrs, pool, p->local_as); | |
834 | ||
835 | /* The MULTI_EXIT_DISC attribute received from a neighboring AS MUST NOT be | |
836 | * propagated to other neighboring ASes. | |
837 | * Perhaps it would be better to undefine it. | |
838 | */ | |
839 | a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); | |
840 | if (a) | |
841 | bgp_attach_attr(attrs, pool, BA_MULTI_EXIT_DISC, 0); | |
842 | } | |
ef2c708d | 843 | |
48e842cc MM |
844 | a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); |
845 | if (a && (p->is_internal || (!p->is_internal && e->attrs->iface == p->neigh->iface))) | |
846 | { | |
847 | /* Leave the original next hop attribute, will check later where does it point */ | |
848 | } | |
849 | else | |
850 | { | |
851 | /* Need to create new one */ | |
4847a894 OZ |
852 | bgp_attach_attr_ip(attrs, pool, BA_NEXT_HOP, p->local_addr); |
853 | } | |
854 | ||
855 | if (rr) | |
856 | { | |
857 | /* Handling route reflection, RFC 4456 */ | |
858 | struct bgp_proto *src = (struct bgp_proto *) e->attrs->proto; | |
859 | ||
860 | a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID)); | |
861 | if (!a) | |
862 | bgp_attach_attr(attrs, pool, BA_ORIGINATOR_ID, src->remote_id); | |
863 | ||
864 | /* We attach proper cluster ID according to whether the route is entering or leaving the cluster */ | |
865 | bgp_cluster_list_prepend(e, attrs, pool, src->rr_client ? src->rr_cluster_id : p->rr_cluster_id); | |
866 | ||
867 | /* Two RR clients with different cluster ID, hmmm */ | |
868 | if (src->rr_client && p->rr_client && (src->rr_cluster_id != p->rr_cluster_id)) | |
869 | bgp_cluster_list_prepend(e, attrs, pool, p->rr_cluster_id); | |
48e842cc | 870 | } |
ef2c708d | 871 | |
48e842cc | 872 | return 0; /* Leave decision to the filters */ |
ef2c708d MM |
873 | } |
874 | ||
875 | int | |
876 | bgp_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool) | |
877 | { | |
878 | rte *e = *new; | |
879 | struct bgp_proto *p = (struct bgp_proto *) P; | |
880 | struct bgp_proto *new_bgp = (e->attrs->proto->proto == &proto_bgp) ? (struct bgp_proto *) e->attrs->proto : NULL; | |
881 | ||
48e842cc | 882 | if (p == new_bgp) /* Poison reverse updates */ |
ef2c708d MM |
883 | return -1; |
884 | if (new_bgp) | |
885 | { | |
4847a894 OZ |
886 | /* We should check here for cluster list loop, because the receiving BGP instance |
887 | might have different cluster ID */ | |
888 | if (bgp_cluster_list_loopy(p, e->attrs)) | |
889 | return -1; | |
890 | ||
ef2c708d | 891 | if (p->local_as == new_bgp->local_as && p->is_internal && new_bgp->is_internal) |
4847a894 OZ |
892 | { |
893 | /* Redistribution of internal routes with IBGP */ | |
894 | if (p->rr_client || new_bgp->rr_client) | |
895 | /* Route reflection, RFC 4456 */ | |
896 | return bgp_update_attrs(p, e, attrs, pool, 1); | |
897 | else | |
898 | return -1; | |
899 | } | |
900 | else | |
901 | return bgp_update_attrs(p, e, attrs, pool, 0); | |
ef2c708d MM |
902 | } |
903 | else | |
48e842cc | 904 | return bgp_create_attrs(p, e, attrs, pool); |
ef2c708d MM |
905 | } |
906 | ||
b6bf284a OZ |
907 | static inline u32 |
908 | bgp_get_neighbor(rte *r) | |
909 | { | |
910 | eattr *e = ea_find(r->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)); | |
911 | u32 as; | |
912 | ||
913 | if (e && as_path_get_last(e->u.ptr, &as)) | |
914 | return as; | |
915 | else | |
916 | return ((struct bgp_proto *) r->attrs->proto)->remote_as; | |
917 | } | |
918 | ||
ef2c708d MM |
919 | int |
920 | bgp_rte_better(rte *new, rte *old) | |
921 | { | |
922 | struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->proto; | |
923 | struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->proto; | |
56a2bed4 MM |
924 | eattr *x, *y; |
925 | u32 n, o; | |
ef2c708d MM |
926 | |
927 | /* Start with local preferences */ | |
56a2bed4 MM |
928 | x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF)); |
929 | y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF)); | |
930 | n = x ? x->u.data : new_bgp->cf->default_local_pref; | |
931 | o = y ? y->u.data : old_bgp->cf->default_local_pref; | |
932 | if (n > o) | |
933 | return 1; | |
934 | if (n < o) | |
935 | return 0; | |
936 | ||
4847a894 | 937 | /* RFC 4271 9.1.2.2. a) Use AS path lengths */ |
56a2bed4 | 938 | if (new_bgp->cf->compare_path_lengths || old_bgp->cf->compare_path_lengths) |
ef2c708d | 939 | { |
56a2bed4 MM |
940 | x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)); |
941 | y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)); | |
11cb6202 OZ |
942 | n = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN; |
943 | o = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN; | |
56a2bed4 | 944 | if (n < o) |
ef2c708d | 945 | return 1; |
56a2bed4 | 946 | if (n > o) |
ef2c708d MM |
947 | return 0; |
948 | } | |
949 | ||
4847a894 | 950 | /* RFC 4271 9.1.2.2. b) Use origins */ |
56a2bed4 MM |
951 | x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN)); |
952 | y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN)); | |
cea63664 MM |
953 | n = x ? x->u.data : ORIGIN_INCOMPLETE; |
954 | o = y ? y->u.data : ORIGIN_INCOMPLETE; | |
56a2bed4 MM |
955 | if (n < o) |
956 | return 1; | |
957 | if (n > o) | |
958 | return 0; | |
959 | ||
4847a894 | 960 | /* RFC 4271 9.1.2.2. c) Compare MED's */ |
b6bf284a OZ |
961 | |
962 | if (bgp_get_neighbor(new) == bgp_get_neighbor(old)) | |
963 | { | |
964 | x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); | |
965 | y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); | |
966 | n = x ? x->u.data : new_bgp->cf->default_med; | |
967 | o = y ? y->u.data : old_bgp->cf->default_med; | |
968 | if (n < o) | |
969 | return 1; | |
970 | if (n > o) | |
971 | return 0; | |
972 | } | |
56a2bed4 | 973 | |
4847a894 | 974 | /* RFC 4271 9.1.2.2. d) Prefer external peers */ |
ef2c708d MM |
975 | if (new_bgp->is_internal > old_bgp->is_internal) |
976 | return 0; | |
977 | if (new_bgp->is_internal < old_bgp->is_internal) | |
978 | return 1; | |
ef2c708d | 979 | |
4847a894 OZ |
980 | /* Skipping RFC 4271 9.1.2.2. e) */ |
981 | /* We don't have interior distances */ | |
982 | ||
983 | /* RFC 4456 9. b) Compare cluster list lengths */ | |
984 | x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_CLUSTER_LIST)); | |
985 | y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_CLUSTER_LIST)); | |
986 | n = x ? int_set_get_size(x->u.ptr) : 0; | |
987 | o = y ? int_set_get_size(y->u.ptr) : 0; | |
988 | if (n < o) | |
989 | return 1; | |
990 | if (n > o) | |
991 | return 0; | |
992 | ||
993 | /* RFC 4271 9.1.2.2. f) Compare BGP identifiers */ | |
994 | /* RFC 4456 9. a) Use ORIGINATOR_ID instead of local neighor ID */ | |
995 | x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID)); | |
996 | y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID)); | |
997 | n = x ? x->u.data : new_bgp->remote_id; | |
998 | o = y ? y->u.data : old_bgp->remote_id; | |
999 | if (n < o) | |
1000 | return 1; | |
1001 | if (n > o) | |
1002 | return 0; | |
11cb6202 OZ |
1003 | |
1004 | ||
4847a894 OZ |
1005 | /* RFC 4271 9.1.2.2. g) Compare peer IP adresses */ |
1006 | return (ipa_compare(new_bgp->cf->remote_ip, old_bgp->cf->remote_ip) < 0); | |
1007 | } | |
1008 | ||
11cb6202 OZ |
1009 | static struct adata * |
1010 | bgp_aggregator_convert_to_new(struct adata *old, struct linpool *pool) | |
1011 | { | |
1012 | struct adata *newa = lp_alloc(pool, sizeof(struct adata) + 8); | |
1013 | newa->length = 8; | |
1014 | aggregator_convert_to_new(old, newa->data); | |
1015 | return newa; | |
1016 | } | |
1017 | ||
1018 | ||
1019 | /* Take last req_as ASNs from path old2 (in 2B format), convert to 4B format | |
1020 | * and append path old4 (in 4B format). | |
1021 | */ | |
1022 | static struct adata * | |
1023 | bgp_merge_as_paths(struct adata *old2, struct adata *old4, int req_as, struct linpool *pool) | |
1024 | { | |
1025 | byte buf[old2->length * 2]; | |
1026 | ||
1027 | int ol = as_path_convert_to_new(old2, buf, req_as); | |
1028 | int nl = ol + (old4 ? old4->length : 0); | |
ef2c708d | 1029 | |
11cb6202 OZ |
1030 | struct adata *newa = lp_alloc(pool, sizeof(struct adata) + nl); |
1031 | newa->length = nl; | |
1032 | memcpy(newa->data, buf, ol); | |
1033 | if (old4) memcpy(newa->data + ol, old4->data, old4->length); | |
1034 | ||
1035 | return newa; | |
1036 | } | |
1037 | ||
1038 | ||
4847a894 | 1039 | /* Reconstruct 4B AS_PATH and AGGREGATOR according to RFC 4893 4.2.3 */ |
11cb6202 OZ |
1040 | static void |
1041 | bgp_reconstruct_4b_atts(struct bgp_proto *p, rta *a, struct linpool *pool) | |
1042 | { | |
1043 | eattr *p2 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)); | |
1044 | eattr *p4 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS4_PATH)); | |
1045 | eattr *a2 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AGGREGATOR)); | |
1046 | eattr *a4 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS4_AGGREGATOR)); | |
1047 | ||
1048 | if (a2) | |
ef2c708d | 1049 | { |
11cb6202 OZ |
1050 | u32 a2_as = get_u16(a2->u.ptr->data); |
1051 | ||
1052 | if (a4) | |
ef2c708d | 1053 | { |
11cb6202 OZ |
1054 | if (a2_as != AS_TRANS) |
1055 | { | |
1056 | /* Routes were aggregated by old router and therefore AS4_PATH | |
1057 | * and AS4_AGGREGATOR is invalid | |
1058 | * | |
1059 | * Convert AS_PATH and AGGREGATOR to 4B format and finish. | |
1060 | */ | |
1061 | ||
1062 | a2->u.ptr = bgp_aggregator_convert_to_new(a2->u.ptr, pool); | |
1063 | p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, NULL, AS_PATH_MAXLEN, pool); | |
1064 | ||
1065 | return; | |
1066 | } | |
1067 | else | |
1068 | { | |
1069 | /* Common case, use AS4_AGGREGATOR attribute */ | |
1070 | a2->u.ptr = a4->u.ptr; | |
1071 | } | |
1072 | } | |
1073 | else | |
1074 | { | |
1075 | /* Common case, use old AGGREGATOR attribute */ | |
1076 | a2->u.ptr = bgp_aggregator_convert_to_new(a2->u.ptr, pool); | |
1077 | ||
1078 | if (a2_as == AS_TRANS) | |
1079 | log(L_WARN "BGP: AGGREGATOR attribute contain AS_TRANS, but AS4_AGGREGATOR is missing"); | |
ef2c708d MM |
1080 | } |
1081 | } | |
11cb6202 OZ |
1082 | else |
1083 | if (a4) | |
1084 | log(L_WARN "BGP: AS4_AGGREGATOR attribute received, but AGGREGATOR attribute is missing"); | |
1085 | ||
1086 | int p2_len = as_path_getlen(p2->u.ptr); | |
1087 | int p4_len = p4 ? as_path_getlen(p4->u.ptr) : AS_PATH_MAXLEN; | |
1088 | ||
1089 | if (p2_len < p4_len) | |
1090 | p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, NULL, AS_PATH_MAXLEN, pool); | |
1091 | else | |
1092 | p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, p4->u.ptr, p2_len - p4_len, pool); | |
1093 | ||
1094 | } | |
1095 | ||
1096 | static void | |
1097 | bgp_remove_as4_attrs(struct bgp_proto *p, rta *a) | |
1098 | { | |
1099 | unsigned id1 = EA_CODE(EAP_BGP, BA_AS4_PATH); | |
1100 | unsigned id2 = EA_CODE(EAP_BGP, BA_AS4_AGGREGATOR); | |
1101 | ea_list **el = &(a->eattrs); | |
1102 | ||
1103 | /* We know that ea_lists constructed in bgp_decode_attrs have one attribute per ea_list struct */ | |
1104 | while (*el != NULL) | |
1105 | { | |
1106 | unsigned fid = (*el)->attrs[0].id; | |
1107 | ||
1108 | if ((fid == id1) || (fid == id2)) | |
1109 | { | |
1110 | *el = (*el)->next; | |
ba5ed6f3 | 1111 | if (p->as4_session) |
11cb6202 OZ |
1112 | log(L_WARN "BGP: Unexpected AS4_* attributes received"); |
1113 | } | |
1114 | else | |
1115 | el = &((*el)->next); | |
1116 | } | |
ef2c708d MM |
1117 | } |
1118 | ||
54e55169 MM |
1119 | /** |
1120 | * bgp_decode_attrs - check and decode BGP attributes | |
1121 | * @conn: connection | |
1122 | * @attr: start of attribute block | |
1123 | * @len: length of attribute block | |
1124 | * @pool: linear pool to make all the allocations in | |
1125 | * @mandatory: 1 iff presence of mandatory attributes has to be checked | |
1126 | * | |
1127 | * This function takes a BGP attribute block (a part of an Update message), checks | |
1128 | * its consistency and converts it to a list of BIRD route attributes represented | |
1129 | * by a &rta. | |
1130 | */ | |
c00d31be | 1131 | struct rta * |
2a9e064d | 1132 | bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct linpool *pool, int mandatory) |
c00d31be MM |
1133 | { |
1134 | struct bgp_proto *bgp = conn->bgp; | |
1135 | rta *a = lp_alloc(pool, sizeof(struct rta)); | |
ef2c708d MM |
1136 | unsigned int flags, code, l, i, type; |
1137 | int errcode; | |
c00d31be MM |
1138 | byte *z, *attr_start; |
1139 | byte seen[256/8]; | |
1140 | eattr *e; | |
1141 | ea_list *ea; | |
1142 | struct adata *ad; | |
c00d31be MM |
1143 | |
1144 | a->proto = &bgp->p; | |
1145 | a->source = RTS_BGP; | |
1146 | a->scope = SCOPE_UNIVERSE; | |
1147 | a->cast = RTC_UNICAST; | |
1148 | a->dest = RTD_ROUTER; | |
1149 | a->flags = 0; | |
1150 | a->aflags = 0; | |
1151 | a->from = bgp->cf->remote_ip; | |
1152 | a->eattrs = NULL; | |
1153 | ||
1154 | /* Parse the attributes */ | |
1155 | bzero(seen, sizeof(seen)); | |
1156 | DBG("BGP: Parsing attributes\n"); | |
1157 | while (len) | |
1158 | { | |
1159 | if (len < 2) | |
1160 | goto malformed; | |
1161 | attr_start = attr; | |
1162 | flags = *attr++; | |
1163 | code = *attr++; | |
1164 | len -= 2; | |
1165 | if (flags & BAF_EXT_LEN) | |
1166 | { | |
1167 | if (len < 2) | |
1168 | goto malformed; | |
1169 | l = get_u16(attr); | |
1170 | attr += 2; | |
1171 | len -= 2; | |
1172 | } | |
1173 | else | |
1174 | { | |
1175 | if (len < 1) | |
1176 | goto malformed; | |
1177 | l = *attr++; | |
1178 | len--; | |
1179 | } | |
1180 | if (l > len) | |
1181 | goto malformed; | |
1182 | len -= l; | |
1183 | z = attr; | |
1184 | attr += l; | |
1185 | DBG("Attr %02x %02x %d\n", code, flags, l); | |
1186 | if (seen[code/8] & (1 << (code%8))) | |
1187 | goto malformed; | |
d1a74339 | 1188 | if (ATTR_KNOWN(code)) |
c00d31be MM |
1189 | { |
1190 | struct attr_desc *desc = &bgp_attr_table[code]; | |
1191 | if (desc->expected_length >= 0 && desc->expected_length != (int) l) | |
1192 | { errcode = 5; goto err; } | |
1193 | if ((desc->expected_flags ^ flags) & (BAF_OPTIONAL | BAF_TRANSITIVE)) | |
1194 | { errcode = 4; goto err; } | |
56a2bed4 MM |
1195 | if (!desc->allow_in_ebgp && !bgp->is_internal) |
1196 | continue; | |
ef2c708d MM |
1197 | if (desc->validate) |
1198 | { | |
1199 | errcode = desc->validate(bgp, z, l); | |
1200 | if (errcode > 0) | |
1201 | goto err; | |
1202 | if (errcode < 0) | |
1203 | continue; | |
1204 | } | |
c00d31be MM |
1205 | type = desc->type; |
1206 | } | |
1207 | else /* Unknown attribute */ | |
e3558ab1 | 1208 | { |
c00d31be MM |
1209 | if (!(flags & BAF_OPTIONAL)) |
1210 | { errcode = 2; goto err; } | |
1211 | type = EAF_TYPE_OPAQUE; | |
1212 | } | |
ef2c708d MM |
1213 | seen[code/8] |= (1 << (code%8)); |
1214 | ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr)); | |
c00d31be MM |
1215 | ea->next = a->eattrs; |
1216 | a->eattrs = ea; | |
1217 | ea->flags = 0; | |
1218 | ea->count = 1; | |
1219 | ea->attrs[0].id = EA_CODE(EAP_BGP, code); | |
1220 | ea->attrs[0].flags = flags; | |
1221 | ea->attrs[0].type = type; | |
1222 | if (type & EAF_EMBEDDED) | |
1223 | ad = NULL; | |
1224 | else | |
1225 | { | |
1226 | ad = lp_alloc(pool, sizeof(struct adata) + l); | |
1227 | ea->attrs[0].u.ptr = ad; | |
1228 | ad->length = l; | |
1229 | memcpy(ad->data, z, l); | |
1230 | } | |
1231 | switch (type) | |
1232 | { | |
1233 | case EAF_TYPE_ROUTER_ID: | |
1234 | case EAF_TYPE_INT: | |
10be74da MM |
1235 | if (l == 1) |
1236 | ea->attrs[0].u.data = *z; | |
1237 | else | |
1238 | ea->attrs[0].u.data = get_u32(z); | |
c00d31be MM |
1239 | break; |
1240 | case EAF_TYPE_IP_ADDRESS: | |
f421cfdd | 1241 | ipa_ntoh(*(ip_addr *)ad->data); |
c00d31be | 1242 | break; |
1ed2fe96 MM |
1243 | case EAF_TYPE_INT_SET: |
1244 | { | |
1245 | u32 *z = (u32 *) ad->data; | |
1246 | for(i=0; i<ad->length/4; i++) | |
1247 | z[i] = ntohl(z[i]); | |
1248 | break; | |
1249 | } | |
c00d31be MM |
1250 | } |
1251 | } | |
1252 | ||
1c1da87b MM |
1253 | #ifdef IPV6 |
1254 | if (seen[BA_MP_REACH_NLRI / 8] & (1 << (BA_MP_REACH_NLRI % 8))) | |
1255 | mandatory = 1; | |
1256 | #endif | |
1257 | ||
c00d31be | 1258 | /* Check if all mandatory attributes are present */ |
2a9e064d | 1259 | if (mandatory) |
c00d31be | 1260 | { |
efcece2d | 1261 | for(i=0; i < ARRAY_SIZE(bgp_mandatory_attrs); i++) |
c00d31be | 1262 | { |
2a9e064d MM |
1263 | code = bgp_mandatory_attrs[i]; |
1264 | if (!(seen[code/8] & (1 << (code%8)))) | |
1265 | { | |
efcece2d | 1266 | bgp_error(conn, 3, 3, &bgp_mandatory_attrs[i], 1); |
2a9e064d MM |
1267 | return NULL; |
1268 | } | |
c00d31be MM |
1269 | } |
1270 | } | |
11cb6202 OZ |
1271 | |
1272 | /* When receiving attributes from non-AS4-aware BGP speaker, | |
1273 | * we have to reconstruct 4B AS_PATH and AGGREGATOR attributes | |
1274 | */ | |
ba5ed6f3 | 1275 | if (bgp_as4_support && (! bgp->as4_session)) |
11cb6202 OZ |
1276 | bgp_reconstruct_4b_atts(bgp, a, pool); |
1277 | ||
1278 | if (bgp_as4_support) | |
1279 | bgp_remove_as4_attrs(bgp, a); | |
c00d31be | 1280 | |
ef2c708d | 1281 | /* If the AS path attribute contains our AS, reject the routes */ |
4847a894 OZ |
1282 | if (bgp_as_path_loopy(bgp, a)) |
1283 | goto loop; | |
1284 | ||
1285 | /* Two checks for IBGP loops caused by route reflection, RFC 4456 */ | |
1286 | if (bgp_originator_id_loopy(bgp, a) || | |
1287 | bgp_cluster_list_loopy(bgp, a)) | |
1288 | goto loop; | |
ef2c708d | 1289 | |
8b258e4e | 1290 | /* If there's no local preference, define one */ |
4819c3e1 | 1291 | if (!(seen[0] & (1 << BA_LOCAL_PREF))) |
cf3d6470 | 1292 | bgp_attach_attr(&a->eattrs, pool, BA_LOCAL_PREF, 0); |
2a9e064d | 1293 | return a; |
c00d31be | 1294 | |
4847a894 OZ |
1295 | loop: |
1296 | DBG("BGP: Path loop!\n"); | |
1297 | return NULL; | |
1298 | ||
c00d31be | 1299 | malformed: |
efcece2d | 1300 | bgp_error(conn, 3, 1, NULL, 0); |
c00d31be MM |
1301 | return NULL; |
1302 | ||
1303 | err: | |
2138d3b4 | 1304 | bgp_error(conn, 3, errcode, attr_start, z+l-attr_start); |
c00d31be MM |
1305 | return NULL; |
1306 | } | |
10be74da MM |
1307 | |
1308 | int | |
1309 | bgp_get_attr(eattr *a, byte *buf) | |
1310 | { | |
1311 | unsigned int i = EA_ID(a->id); | |
1312 | struct attr_desc *d; | |
1313 | ||
d1a74339 | 1314 | if (ATTR_KNOWN(i)) |
10be74da MM |
1315 | { |
1316 | d = &bgp_attr_table[i]; | |
1317 | buf += bsprintf(buf, "%s", d->name); | |
1318 | if (d->format) | |
1319 | { | |
1320 | *buf++ = ':'; | |
1321 | *buf++ = ' '; | |
1322 | d->format(a, buf); | |
1323 | return GA_FULL; | |
1324 | } | |
1325 | return GA_NAME; | |
1326 | } | |
d1a74339 | 1327 | bsprintf(buf, "%02x%s", i, (a->flags & BAF_TRANSITIVE) ? " [t]" : ""); |
10be74da MM |
1328 | return GA_NAME; |
1329 | } | |
ae8f5584 MM |
1330 | |
1331 | void | |
1332 | bgp_attr_init(struct bgp_proto *p) | |
1333 | { | |
1334 | p->hash_size = 256; | |
1335 | p->hash_limit = p->hash_size * 4; | |
c2b28c99 MM |
1336 | p->bucket_hash = mb_allocz(p->p.pool, p->hash_size * sizeof(struct bgp_bucket *)); |
1337 | init_list(&p->bucket_queue); | |
1338 | p->withdraw_bucket = NULL; | |
1339 | fib_init(&p->prefix_fib, p->p.pool, sizeof(struct bgp_prefix), 0, bgp_init_prefix); | |
ae8f5584 | 1340 | } |
5e88d730 MM |
1341 | |
1342 | void | |
1343 | bgp_get_route_info(rte *e, byte *buf, ea_list *attrs) | |
1344 | { | |
1345 | eattr *p = ea_find(attrs, EA_CODE(EAP_BGP, BA_AS_PATH)); | |
1346 | eattr *o = ea_find(attrs, EA_CODE(EAP_BGP, BA_ORIGIN)); | |
11cb6202 | 1347 | u32 origas; |
5e88d730 MM |
1348 | |
1349 | buf += bsprintf(buf, " (%d) [", e->pref); | |
11cb6202 OZ |
1350 | if (p && as_path_get_first(p->u.ptr, &origas)) |
1351 | buf += bsprintf(buf, "AS%u", origas); | |
5e88d730 MM |
1352 | if (o) |
1353 | buf += bsprintf(buf, "%c", "ie?"[o->u.data]); | |
1354 | strcpy(buf, "]"); | |
1355 | } |