]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* BGP Extended Communities Attribute |
2 | Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org> | |
3 | ||
4 | This file is part of GNU Zebra. | |
5 | ||
6 | GNU Zebra is free software; you can redistribute it and/or modify it | |
7 | under the terms of the GNU General Public License as published by the | |
8 | Free Software Foundation; either version 2, or (at your option) any | |
9 | later version. | |
10 | ||
11 | GNU Zebra is distributed in the hope that it will be useful, but | |
12 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GNU Zebra; see the file COPYING. If not, write to the Free | |
18 | Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
19 | 02111-1307, USA. */ | |
20 | ||
21 | #include <zebra.h> | |
22 | ||
23 | #include "hash.h" | |
24 | #include "memory.h" | |
25 | #include "prefix.h" | |
26 | #include "command.h" | |
3f9c7369 | 27 | #include "queue.h" |
718e3744 | 28 | |
29 | #include "bgpd/bgpd.h" | |
30 | #include "bgpd/bgp_ecommunity.h" | |
0b2aa3a0 | 31 | #include "bgpd/bgp_aspath.h" |
718e3744 | 32 | |
33 | /* Hash of community attribute. */ | |
ffa4e2c4 | 34 | static struct hash *ecomhash; |
6b0655a2 | 35 | |
718e3744 | 36 | /* Allocate a new ecommunities. */ |
ffa4e2c4 | 37 | static struct ecommunity * |
66e5cd87 | 38 | ecommunity_new (void) |
718e3744 | 39 | { |
40 | return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY, | |
41 | sizeof (struct ecommunity)); | |
42 | } | |
43 | ||
44 | /* Allocate ecommunities. */ | |
45 | void | |
f6f434b2 | 46 | ecommunity_free (struct ecommunity **ecom) |
718e3744 | 47 | { |
f6f434b2 PJ |
48 | if ((*ecom)->val) |
49 | XFREE (MTYPE_ECOMMUNITY_VAL, (*ecom)->val); | |
50 | if ((*ecom)->str) | |
51 | XFREE (MTYPE_ECOMMUNITY_STR, (*ecom)->str); | |
52 | XFREE (MTYPE_ECOMMUNITY, *ecom); | |
53 | ecom = NULL; | |
718e3744 | 54 | } |
55 | ||
56 | /* Add a new Extended Communities value to Extended Communities | |
57 | Attribute structure. When the value is already exists in the | |
58 | structure, we don't add the value. Newly added value is sorted by | |
59 | numerical order. When the value is added to the structure return 1 | |
60 | else return 0. */ | |
61 | static int | |
62 | ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval) | |
63 | { | |
5228ad27 | 64 | u_int8_t *p; |
718e3744 | 65 | int ret; |
66 | int c; | |
67 | ||
68 | /* When this is fist value, just add it. */ | |
69 | if (ecom->val == NULL) | |
70 | { | |
71 | ecom->size++; | |
72 | ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom)); | |
73 | memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE); | |
74 | return 1; | |
75 | } | |
76 | ||
77 | /* If the value already exists in the structure return 0. */ | |
78 | c = 0; | |
79 | for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) | |
80 | { | |
81 | ret = memcmp (p, eval->val, ECOMMUNITY_SIZE); | |
82 | if (ret == 0) | |
83 | return 0; | |
84 | if (ret > 0) | |
85 | break; | |
86 | } | |
87 | ||
88 | /* Add the value to the structure with numerical sorting. */ | |
89 | ecom->size++; | |
90 | ecom->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length (ecom)); | |
91 | ||
92 | memmove (ecom->val + (c + 1) * ECOMMUNITY_SIZE, | |
93 | ecom->val + c * ECOMMUNITY_SIZE, | |
94 | (ecom->size - 1 - c) * ECOMMUNITY_SIZE); | |
95 | memcpy (ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE); | |
96 | ||
97 | return 1; | |
98 | } | |
99 | ||
100 | /* This function takes pointer to Extended Communites strucutre then | |
101 | create a new Extended Communities structure by uniq and sort each | |
e6b6a564 | 102 | Extended Communities value. */ |
0b597ef0 | 103 | struct ecommunity * |
718e3744 | 104 | ecommunity_uniq_sort (struct ecommunity *ecom) |
105 | { | |
106 | int i; | |
107 | struct ecommunity *new; | |
108 | struct ecommunity_val *eval; | |
109 | ||
110 | if (! ecom) | |
111 | return NULL; | |
112 | ||
66e5cd87 | 113 | new = ecommunity_new (); |
718e3744 | 114 | |
115 | for (i = 0; i < ecom->size; i++) | |
116 | { | |
117 | eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE)); | |
118 | ecommunity_add_val (new, eval); | |
119 | } | |
120 | return new; | |
121 | } | |
122 | ||
123 | /* Parse Extended Communites Attribute in BGP packet. */ | |
124 | struct ecommunity * | |
5228ad27 | 125 | ecommunity_parse (u_int8_t *pnt, u_short length) |
718e3744 | 126 | { |
127 | struct ecommunity tmp; | |
128 | struct ecommunity *new; | |
129 | ||
130 | /* Length check. */ | |
131 | if (length % ECOMMUNITY_SIZE) | |
132 | return NULL; | |
133 | ||
134 | /* Prepare tmporary structure for making a new Extended Communities | |
135 | Attribute. */ | |
136 | tmp.size = length / ECOMMUNITY_SIZE; | |
137 | tmp.val = pnt; | |
138 | ||
139 | /* Create a new Extended Communities Attribute by uniq and sort each | |
140 | Extended Communities value */ | |
141 | new = ecommunity_uniq_sort (&tmp); | |
142 | ||
143 | return ecommunity_intern (new); | |
144 | } | |
145 | ||
146 | /* Duplicate the Extended Communities Attribute structure. */ | |
147 | struct ecommunity * | |
148 | ecommunity_dup (struct ecommunity *ecom) | |
149 | { | |
150 | struct ecommunity *new; | |
151 | ||
152 | new = XCALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity)); | |
153 | new->size = ecom->size; | |
154 | if (new->size) | |
155 | { | |
156 | new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE); | |
157 | memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE); | |
158 | } | |
159 | else | |
160 | new->val = NULL; | |
161 | return new; | |
162 | } | |
163 | ||
4372df71 | 164 | /* Retrun string representation of communities attribute. */ |
165 | char * | |
166 | ecommunity_str (struct ecommunity *ecom) | |
167 | { | |
168 | if (! ecom->str) | |
169 | ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY); | |
170 | return ecom->str; | |
171 | } | |
172 | ||
718e3744 | 173 | /* Merge two Extended Communities Attribute structure. */ |
174 | struct ecommunity * | |
175 | ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2) | |
176 | { | |
177 | if (ecom1->val) | |
178 | ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val, | |
179 | (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); | |
180 | else | |
181 | ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, | |
182 | (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); | |
183 | ||
184 | memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), | |
185 | ecom2->val, ecom2->size * ECOMMUNITY_SIZE); | |
186 | ecom1->size += ecom2->size; | |
187 | ||
188 | return ecom1; | |
189 | } | |
190 | ||
191 | /* Intern Extended Communities Attribute. */ | |
192 | struct ecommunity * | |
193 | ecommunity_intern (struct ecommunity *ecom) | |
194 | { | |
195 | struct ecommunity *find; | |
196 | ||
197 | assert (ecom->refcnt == 0); | |
198 | ||
199 | find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern); | |
200 | ||
201 | if (find != ecom) | |
f6f434b2 | 202 | ecommunity_free (&ecom); |
718e3744 | 203 | |
204 | find->refcnt++; | |
205 | ||
206 | if (! find->str) | |
207 | find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY); | |
208 | ||
209 | return find; | |
210 | } | |
211 | ||
212 | /* Unintern Extended Communities Attribute. */ | |
213 | void | |
f6f434b2 | 214 | ecommunity_unintern (struct ecommunity **ecom) |
718e3744 | 215 | { |
216 | struct ecommunity *ret; | |
217 | ||
f6f434b2 PJ |
218 | if ((*ecom)->refcnt) |
219 | (*ecom)->refcnt--; | |
220 | ||
718e3744 | 221 | /* Pull off from hash. */ |
f6f434b2 | 222 | if ((*ecom)->refcnt == 0) |
718e3744 | 223 | { |
224 | /* Extended community must be in the hash. */ | |
f6f434b2 | 225 | ret = (struct ecommunity *) hash_release (ecomhash, *ecom); |
718e3744 | 226 | assert (ret != NULL); |
227 | ||
228 | ecommunity_free (ecom); | |
229 | } | |
230 | } | |
231 | ||
232 | /* Utinity function to make hash key. */ | |
233 | unsigned int | |
0b2aa3a0 | 234 | ecommunity_hash_make (void *arg) |
718e3744 | 235 | { |
0b2aa3a0 | 236 | const struct ecommunity *ecom = arg; |
c76275ee JBD |
237 | int size = ecom->size * ECOMMUNITY_SIZE; |
238 | u_int8_t *pnt = ecom->val; | |
239 | unsigned int key = 0; | |
718e3744 | 240 | int c; |
718e3744 | 241 | |
c76275ee JBD |
242 | for (c = 0; c < size; c += ECOMMUNITY_SIZE) |
243 | { | |
244 | key += pnt[c]; | |
245 | key += pnt[c + 1]; | |
246 | key += pnt[c + 2]; | |
247 | key += pnt[c + 3]; | |
248 | key += pnt[c + 4]; | |
249 | key += pnt[c + 5]; | |
250 | key += pnt[c + 6]; | |
251 | key += pnt[c + 7]; | |
252 | } | |
718e3744 | 253 | |
254 | return key; | |
255 | } | |
256 | ||
257 | /* Compare two Extended Communities Attribute structure. */ | |
258 | int | |
ffe11cfb | 259 | ecommunity_cmp (const void *arg1, const void *arg2) |
718e3744 | 260 | { |
0b2aa3a0 PJ |
261 | const struct ecommunity *ecom1 = arg1; |
262 | const struct ecommunity *ecom2 = arg2; | |
263 | ||
ffe11cfb SH |
264 | return (ecom1->size == ecom2->size |
265 | && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0); | |
718e3744 | 266 | } |
267 | ||
268 | /* Initialize Extended Comminities related hash. */ | |
269 | void | |
0b2aa3a0 | 270 | ecommunity_init (void) |
718e3744 | 271 | { |
272 | ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp); | |
273 | } | |
228da428 CC |
274 | |
275 | void | |
276 | ecommunity_finish (void) | |
277 | { | |
278 | hash_free (ecomhash); | |
279 | ecomhash = NULL; | |
280 | } | |
6b0655a2 | 281 | |
718e3744 | 282 | /* Extended Communities token enum. */ |
283 | enum ecommunity_token | |
284 | { | |
285 | ecommunity_token_rt, | |
286 | ecommunity_token_soo, | |
287 | ecommunity_token_val, | |
288 | ecommunity_token_unknown | |
289 | }; | |
290 | ||
291 | /* Get next Extended Communities token from the string. */ | |
94f2b392 | 292 | static const char * |
fd79ac91 | 293 | ecommunity_gettoken (const char *str, struct ecommunity_val *eval, |
718e3744 | 294 | enum ecommunity_token *token) |
295 | { | |
296 | int ret; | |
297 | int dot = 0; | |
298 | int digit = 0; | |
299 | int separator = 0; | |
fd79ac91 | 300 | const char *p = str; |
0b2aa3a0 | 301 | char *endptr; |
718e3744 | 302 | struct in_addr ip; |
0b2aa3a0 PJ |
303 | as_t as = 0; |
304 | u_int32_t val = 0; | |
305 | char buf[INET_ADDRSTRLEN + 1]; | |
718e3744 | 306 | |
307 | /* Skip white space. */ | |
308 | while (isspace ((int) *p)) | |
309 | { | |
310 | p++; | |
311 | str++; | |
312 | } | |
313 | ||
314 | /* Check the end of the line. */ | |
315 | if (*p == '\0') | |
316 | return NULL; | |
317 | ||
318 | /* "rt" and "soo" keyword parse. */ | |
319 | if (! isdigit ((int) *p)) | |
320 | { | |
321 | /* "rt" match check. */ | |
322 | if (tolower ((int) *p) == 'r') | |
323 | { | |
324 | p++; | |
325 | if (tolower ((int) *p) == 't') | |
326 | { | |
327 | p++; | |
328 | *token = ecommunity_token_rt; | |
329 | return p; | |
330 | } | |
331 | if (isspace ((int) *p) || *p == '\0') | |
332 | { | |
333 | *token = ecommunity_token_rt; | |
334 | return p; | |
335 | } | |
336 | goto error; | |
337 | } | |
338 | /* "soo" match check. */ | |
339 | else if (tolower ((int) *p) == 's') | |
340 | { | |
341 | p++; | |
342 | if (tolower ((int) *p) == 'o') | |
343 | { | |
344 | p++; | |
345 | if (tolower ((int) *p) == 'o') | |
346 | { | |
347 | p++; | |
348 | *token = ecommunity_token_soo; | |
349 | return p; | |
350 | } | |
351 | if (isspace ((int) *p) || *p == '\0') | |
352 | { | |
353 | *token = ecommunity_token_soo; | |
354 | return p; | |
355 | } | |
356 | goto error; | |
357 | } | |
358 | if (isspace ((int) *p) || *p == '\0') | |
359 | { | |
360 | *token = ecommunity_token_soo; | |
361 | return p; | |
362 | } | |
363 | goto error; | |
364 | } | |
365 | goto error; | |
366 | } | |
367 | ||
0b2aa3a0 PJ |
368 | /* What a mess, there are several possibilities: |
369 | * | |
370 | * a) A.B.C.D:MN | |
371 | * b) EF:OPQR | |
372 | * c) GHJK:MN | |
373 | * | |
374 | * A.B.C.D: Four Byte IP | |
375 | * EF: Two byte ASN | |
376 | * GHJK: Four-byte ASN | |
377 | * MN: Two byte value | |
378 | * OPQR: Four byte value | |
379 | * | |
380 | */ | |
718e3744 | 381 | while (isdigit ((int) *p) || *p == ':' || *p == '.') |
382 | { | |
0b2aa3a0 | 383 | if (*p == ':') |
718e3744 | 384 | { |
385 | if (separator) | |
386 | goto error; | |
387 | ||
388 | separator = 1; | |
389 | digit = 0; | |
0b2aa3a0 PJ |
390 | |
391 | if ((p - str) > INET_ADDRSTRLEN) | |
392 | goto error; | |
393 | memset (buf, 0, INET_ADDRSTRLEN + 1); | |
394 | memcpy (buf, str, p - str); | |
395 | ||
718e3744 | 396 | if (dot) |
397 | { | |
0b2aa3a0 PJ |
398 | /* Parsing A.B.C.D in: |
399 | * A.B.C.D:MN | |
400 | */ | |
401 | ret = inet_aton (buf, &ip); | |
718e3744 | 402 | if (ret == 0) |
0b2aa3a0 | 403 | goto error; |
718e3744 | 404 | } |
0b2aa3a0 PJ |
405 | else |
406 | { | |
407 | /* ASN */ | |
408 | as = strtoul (buf, &endptr, 10); | |
409 | if (*endptr != '\0' || as == BGP_AS4_MAX) | |
410 | goto error; | |
411 | } | |
718e3744 | 412 | } |
413 | else if (*p == '.') | |
414 | { | |
415 | if (separator) | |
416 | goto error; | |
417 | dot++; | |
418 | if (dot > 4) | |
419 | goto error; | |
420 | } | |
421 | else | |
422 | { | |
423 | digit = 1; | |
0b2aa3a0 PJ |
424 | |
425 | /* We're past the IP/ASN part */ | |
426 | if (separator) | |
427 | { | |
428 | val *= 10; | |
429 | val += (*p - '0'); | |
430 | } | |
718e3744 | 431 | } |
432 | p++; | |
433 | } | |
434 | ||
435 | /* Low digit part must be there. */ | |
0b2aa3a0 | 436 | if (!digit || !separator) |
718e3744 | 437 | goto error; |
438 | ||
439 | /* Encode result into routing distinguisher. */ | |
440 | if (dot) | |
441 | { | |
0b2aa3a0 PJ |
442 | if (val > UINT16_MAX) |
443 | goto error; | |
444 | ||
718e3744 | 445 | eval->val[0] = ECOMMUNITY_ENCODE_IP; |
446 | eval->val[1] = 0; | |
447 | memcpy (&eval->val[2], &ip, sizeof (struct in_addr)); | |
0b2aa3a0 PJ |
448 | eval->val[6] = (val >> 8) & 0xff; |
449 | eval->val[7] = val & 0xff; | |
450 | } | |
451 | else if (as > BGP_AS_MAX) | |
452 | { | |
453 | if (val > UINT16_MAX) | |
454 | goto error; | |
455 | ||
456 | eval->val[0] = ECOMMUNITY_ENCODE_AS4; | |
457 | eval->val[1] = 0; | |
458 | eval->val[2] = (as >>24) & 0xff; | |
459 | eval->val[3] = (as >>16) & 0xff; | |
460 | eval->val[4] = (as >>8) & 0xff; | |
461 | eval->val[5] = as & 0xff; | |
462 | eval->val[6] = (val >> 8) & 0xff; | |
463 | eval->val[7] = val & 0xff; | |
718e3744 | 464 | } |
465 | else | |
466 | { | |
467 | eval->val[0] = ECOMMUNITY_ENCODE_AS; | |
468 | eval->val[1] = 0; | |
0b2aa3a0 PJ |
469 | |
470 | eval->val[2] = (as >>8) & 0xff; | |
471 | eval->val[3] = as & 0xff; | |
472 | eval->val[4] = (val >>24) & 0xff; | |
473 | eval->val[5] = (val >>16) & 0xff; | |
474 | eval->val[6] = (val >>8) & 0xff; | |
475 | eval->val[7] = val & 0xff; | |
718e3744 | 476 | } |
477 | *token = ecommunity_token_val; | |
478 | return p; | |
479 | ||
480 | error: | |
481 | *token = ecommunity_token_unknown; | |
482 | return p; | |
483 | } | |
484 | ||
485 | /* Convert string to extended community attribute. | |
486 | ||
487 | When type is already known, please specify both str and type. str | |
488 | should not include keyword such as "rt" and "soo". Type is | |
489 | ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN. | |
490 | keyword_included should be zero. | |
491 | ||
492 | For example route-map's "set extcommunity" command case: | |
493 | ||
494 | "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3" | |
495 | type = ECOMMUNITY_ROUTE_TARGET | |
496 | keyword_included = 0 | |
497 | ||
498 | "soo 100:1" -> str = "100:1" | |
499 | type = ECOMMUNITY_SITE_ORIGIN | |
500 | keyword_included = 0 | |
501 | ||
502 | When string includes keyword for each extended community value. | |
503 | Please specify keyword_included as non-zero value. | |
504 | ||
505 | For example standard extcommunity-list case: | |
506 | ||
507 | "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1" | |
508 | type = 0 | |
509 | keyword_include = 1 | |
510 | */ | |
511 | struct ecommunity * | |
fd79ac91 | 512 | ecommunity_str2com (const char *str, int type, int keyword_included) |
718e3744 | 513 | { |
514 | struct ecommunity *ecom = NULL; | |
ffd0c037 | 515 | enum ecommunity_token token = ecommunity_token_unknown; |
718e3744 | 516 | struct ecommunity_val eval; |
517 | int keyword = 0; | |
518 | ||
519 | while ((str = ecommunity_gettoken (str, &eval, &token))) | |
520 | { | |
521 | switch (token) | |
522 | { | |
523 | case ecommunity_token_rt: | |
524 | case ecommunity_token_soo: | |
525 | if (! keyword_included || keyword) | |
526 | { | |
527 | if (ecom) | |
f6f434b2 | 528 | ecommunity_free (&ecom); |
718e3744 | 529 | return NULL; |
530 | } | |
531 | keyword = 1; | |
532 | ||
533 | if (token == ecommunity_token_rt) | |
534 | { | |
535 | type = ECOMMUNITY_ROUTE_TARGET; | |
536 | } | |
537 | if (token == ecommunity_token_soo) | |
538 | { | |
539 | type = ECOMMUNITY_SITE_ORIGIN; | |
540 | } | |
541 | break; | |
542 | case ecommunity_token_val: | |
543 | if (keyword_included) | |
544 | { | |
545 | if (! keyword) | |
546 | { | |
547 | if (ecom) | |
f6f434b2 | 548 | ecommunity_free (&ecom); |
718e3744 | 549 | return NULL; |
550 | } | |
551 | keyword = 0; | |
552 | } | |
553 | if (ecom == NULL) | |
554 | ecom = ecommunity_new (); | |
555 | eval.val[1] = type; | |
556 | ecommunity_add_val (ecom, &eval); | |
557 | break; | |
558 | case ecommunity_token_unknown: | |
559 | default: | |
560 | if (ecom) | |
f6f434b2 | 561 | ecommunity_free (&ecom); |
718e3744 | 562 | return NULL; |
718e3744 | 563 | } |
564 | } | |
565 | return ecom; | |
566 | } | |
567 | ||
568 | /* Convert extended community attribute to string. | |
569 | ||
570 | Due to historical reason of industry standard implementation, there | |
571 | are three types of format. | |
572 | ||
573 | route-map set extcommunity format | |
574 | "rt 100:1 100:2" | |
575 | "soo 100:3" | |
576 | ||
577 | extcommunity-list | |
578 | "rt 100:1 rt 100:2 soo 100:3" | |
579 | ||
580 | "show ip bgp" and extcommunity-list regular expression matching | |
581 | "RT:100:1 RT:100:2 SoO:100:3" | |
582 | ||
583 | For each formath please use below definition for format: | |
584 | ||
585 | ECOMMUNITY_FORMAT_ROUTE_MAP | |
586 | ECOMMUNITY_FORMAT_COMMUNITY_LIST | |
587 | ECOMMUNITY_FORMAT_DISPLAY | |
588 | */ | |
589 | char * | |
590 | ecommunity_ecom2str (struct ecommunity *ecom, int format) | |
591 | { | |
592 | int i; | |
5228ad27 | 593 | u_int8_t *pnt; |
718e3744 | 594 | int encode = 0; |
595 | int type = 0; | |
0b2aa3a0 | 596 | #define ECOMMUNITY_STR_DEFAULT_LEN 27 |
718e3744 | 597 | int str_size; |
598 | int str_pnt; | |
5228ad27 | 599 | char *str_buf; |
fd79ac91 | 600 | const char *prefix; |
718e3744 | 601 | int len = 0; |
602 | int first = 1; | |
603 | ||
604 | /* For parse Extended Community attribute tupple. */ | |
605 | struct ecommunity_as | |
606 | { | |
607 | as_t as; | |
608 | u_int32_t val; | |
609 | } eas; | |
610 | ||
611 | struct ecommunity_ip | |
612 | { | |
613 | struct in_addr ip; | |
614 | u_int16_t val; | |
615 | } eip; | |
616 | ||
617 | if (ecom->size == 0) | |
618 | { | |
619 | str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1); | |
620 | str_buf[0] = '\0'; | |
621 | return str_buf; | |
622 | } | |
623 | ||
624 | /* Prepare buffer. */ | |
625 | str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1); | |
626 | str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1; | |
627 | str_pnt = 0; | |
628 | ||
629 | for (i = 0; i < ecom->size; i++) | |
630 | { | |
94431dbc C |
631 | /* Make it sure size is enough. */ |
632 | while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size) | |
633 | { | |
634 | str_size *= 2; | |
635 | str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size); | |
636 | } | |
637 | ||
4372df71 | 638 | /* Space between each value. */ |
639 | if (! first) | |
640 | str_buf[str_pnt++] = ' '; | |
641 | ||
718e3744 | 642 | pnt = ecom->val + (i * 8); |
643 | ||
644 | /* High-order octet of type. */ | |
645 | encode = *pnt++; | |
0b2aa3a0 PJ |
646 | if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP |
647 | && encode != ECOMMUNITY_ENCODE_AS4) | |
718e3744 | 648 | { |
4372df71 | 649 | len = sprintf (str_buf + str_pnt, "?"); |
650 | str_pnt += len; | |
651 | first = 0; | |
652 | continue; | |
718e3744 | 653 | } |
654 | ||
655 | /* Low-order octet of type. */ | |
656 | type = *pnt++; | |
657 | if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN) | |
658 | { | |
4372df71 | 659 | len = sprintf (str_buf + str_pnt, "?"); |
660 | str_pnt += len; | |
661 | first = 0; | |
662 | continue; | |
718e3744 | 663 | } |
664 | ||
665 | switch (format) | |
666 | { | |
667 | case ECOMMUNITY_FORMAT_COMMUNITY_LIST: | |
668 | prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo "); | |
669 | break; | |
670 | case ECOMMUNITY_FORMAT_DISPLAY: | |
671 | prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:"); | |
672 | break; | |
673 | case ECOMMUNITY_FORMAT_ROUTE_MAP: | |
674 | prefix = ""; | |
675 | break; | |
676 | default: | |
4372df71 | 677 | prefix = ""; |
718e3744 | 678 | break; |
679 | } | |
680 | ||
718e3744 | 681 | /* Put string into buffer. */ |
0b2aa3a0 PJ |
682 | if (encode == ECOMMUNITY_ENCODE_AS4) |
683 | { | |
684 | eas.as = (*pnt++ << 24); | |
685 | eas.as |= (*pnt++ << 16); | |
686 | eas.as |= (*pnt++ << 8); | |
687 | eas.as |= (*pnt++); | |
688 | ||
689 | eas.val = (*pnt++ << 8); | |
690 | eas.val |= (*pnt++); | |
691 | ||
aea339f7 | 692 | len = sprintf( str_buf + str_pnt, "%s%u:%d", prefix, |
0b2aa3a0 PJ |
693 | eas.as, eas.val ); |
694 | str_pnt += len; | |
695 | first = 0; | |
696 | } | |
718e3744 | 697 | if (encode == ECOMMUNITY_ENCODE_AS) |
698 | { | |
699 | eas.as = (*pnt++ << 8); | |
700 | eas.as |= (*pnt++); | |
701 | ||
702 | eas.val = (*pnt++ << 24); | |
703 | eas.val |= (*pnt++ << 16); | |
704 | eas.val |= (*pnt++ << 8); | |
705 | eas.val |= (*pnt++); | |
706 | ||
aea339f7 | 707 | len = sprintf (str_buf + str_pnt, "%s%u:%d", prefix, |
718e3744 | 708 | eas.as, eas.val); |
709 | str_pnt += len; | |
710 | first = 0; | |
711 | } | |
712 | else if (encode == ECOMMUNITY_ENCODE_IP) | |
713 | { | |
714 | memcpy (&eip.ip, pnt, 4); | |
715 | pnt += 4; | |
716 | eip.val = (*pnt++ << 8); | |
717 | eip.val |= (*pnt++); | |
718 | ||
719 | len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix, | |
720 | inet_ntoa (eip.ip), eip.val); | |
721 | str_pnt += len; | |
722 | first = 0; | |
723 | } | |
724 | } | |
725 | return str_buf; | |
726 | } | |
4372df71 | 727 | |
728 | int | |
fd79ac91 | 729 | ecommunity_match (const struct ecommunity *ecom1, |
730 | const struct ecommunity *ecom2) | |
4372df71 | 731 | { |
732 | int i = 0; | |
733 | int j = 0; | |
734 | ||
735 | if (ecom1 == NULL && ecom2 == NULL) | |
736 | return 1; | |
737 | ||
738 | if (ecom1 == NULL || ecom2 == NULL) | |
739 | return 0; | |
740 | ||
741 | if (ecom1->size < ecom2->size) | |
742 | return 0; | |
743 | ||
744 | /* Every community on com2 needs to be on com1 for this to match */ | |
745 | while (i < ecom1->size && j < ecom2->size) | |
746 | { | |
747 | if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0) | |
748 | j++; | |
749 | i++; | |
750 | } | |
751 | ||
752 | if (j == ecom2->size) | |
753 | return 1; | |
754 | else | |
755 | return 0; | |
756 | } | |
757 |