]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* BGP community-list and extcommunity-list. |
2 | Copyright (C) 1999 Kunihiro Ishiguro | |
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 "command.h" | |
24 | #include "prefix.h" | |
25 | #include "memory.h" | |
26 | ||
27 | #include "bgpd/bgpd.h" | |
28 | #include "bgpd/bgp_community.h" | |
29 | #include "bgpd/bgp_ecommunity.h" | |
30 | #include "bgpd/bgp_aspath.h" | |
31 | #include "bgpd/bgp_regex.h" | |
32 | #include "bgpd/bgp_clist.h" | |
33 | \f | |
34 | /* Lookup master structure for community-list or | |
35 | extcommunity-list. */ | |
36 | struct community_list_master * | |
37 | community_list_master_lookup (struct community_list_handler *ch, int style) | |
38 | { | |
39 | if (ch) | |
40 | switch (style) | |
41 | { | |
42 | case COMMUNITY_LIST_STANDARD: | |
43 | case COMMUNITY_LIST_EXPANDED: | |
44 | case COMMUNITY_LIST_AUTO: | |
45 | return &ch->community_list; | |
46 | break; | |
47 | case EXTCOMMUNITY_LIST_STANDARD: | |
48 | case EXTCOMMUNITY_LIST_EXPANDED: | |
49 | case EXTCOMMUNITY_LIST_AUTO: | |
50 | return &ch->extcommunity_list; | |
51 | } | |
52 | return NULL; | |
53 | } | |
54 | ||
55 | /* Allocate a new community list entry. */ | |
56 | struct community_entry * | |
57 | community_entry_new () | |
58 | { | |
59 | struct community_entry *new; | |
60 | ||
61 | new = XMALLOC (MTYPE_COMMUNITY_LIST_ENTRY, sizeof (struct community_entry)); | |
62 | memset (new, 0, sizeof (struct community_entry)); | |
63 | return new; | |
64 | } | |
65 | ||
66 | /* Free community list entry. */ | |
67 | void | |
68 | community_entry_free (struct community_entry *entry) | |
69 | { | |
70 | switch (entry->style) | |
71 | { | |
72 | case COMMUNITY_LIST_STANDARD: | |
73 | if (entry->u.com) | |
74 | community_free (entry->u.com); | |
75 | break; | |
76 | case EXTCOMMUNITY_LIST_STANDARD: | |
77 | /* In case of standard extcommunity-list, configuration string | |
78 | is made by ecommunity_ecom2str(). */ | |
79 | if (entry->config) | |
80 | XFREE (MTYPE_ECOMMUNITY_STR, entry->config); | |
81 | if (entry->u.ecom) | |
82 | ecommunity_free (entry->u.ecom); | |
83 | break; | |
84 | case COMMUNITY_LIST_EXPANDED: | |
85 | case EXTCOMMUNITY_LIST_EXPANDED: | |
86 | if (entry->config) | |
87 | XFREE (MTYPE_COMMUNITY_LIST_CONFIG, entry->config); | |
88 | if (entry->reg) | |
89 | bgp_regex_free (entry->reg); | |
90 | default: | |
91 | break; | |
92 | } | |
93 | XFREE (MTYPE_COMMUNITY_LIST_ENTRY, entry); | |
94 | } | |
95 | ||
96 | /* Allocate a new community-list. */ | |
97 | struct community_list * | |
98 | community_list_new () | |
99 | { | |
100 | struct community_list *new; | |
101 | ||
102 | new = XMALLOC (MTYPE_COMMUNITY_LIST, sizeof (struct community_list)); | |
103 | memset (new, 0, sizeof (struct community_list)); | |
104 | return new; | |
105 | } | |
106 | ||
107 | /* Free community-list. */ | |
108 | void | |
109 | community_list_free (struct community_list *list) | |
110 | { | |
111 | if (list->name) | |
112 | XFREE (MTYPE_COMMUNITY_LIST_NAME, list->name); | |
113 | XFREE (MTYPE_COMMUNITY_LIST, list); | |
114 | } | |
115 | ||
116 | struct community_list * | |
117 | community_list_insert (struct community_list_handler *ch, | |
118 | char *name, int style) | |
119 | { | |
120 | int i; | |
121 | long number; | |
122 | struct community_list *new; | |
123 | struct community_list *point; | |
124 | struct community_list_list *list; | |
125 | struct community_list_master *cm; | |
126 | ||
127 | /* Lookup community-list master. */ | |
128 | cm = community_list_master_lookup (ch, style); | |
129 | if (! cm) | |
130 | return NULL; | |
131 | ||
132 | /* Allocate new community_list and copy given name. */ | |
133 | new = community_list_new (); | |
134 | new->name = XSTRDUP (MTYPE_COMMUNITY_LIST_NAME, name); | |
135 | ||
136 | /* If name is made by all digit character. We treat it as | |
137 | number. */ | |
138 | for (number = 0, i = 0; i < strlen (name); i++) | |
139 | { | |
140 | if (isdigit ((int) name[i])) | |
141 | number = (number * 10) + (name[i] - '0'); | |
142 | else | |
143 | break; | |
144 | } | |
145 | ||
146 | /* In case of name is all digit character */ | |
147 | if (i == strlen (name)) | |
148 | { | |
149 | new->sort = COMMUNITY_LIST_NUMBER; | |
150 | ||
151 | /* Set access_list to number list. */ | |
152 | list = &cm->num; | |
153 | ||
154 | for (point = list->head; point; point = point->next) | |
155 | if (atol (point->name) >= number) | |
156 | break; | |
157 | } | |
158 | else | |
159 | { | |
160 | new->sort = COMMUNITY_LIST_STRING; | |
161 | ||
162 | /* Set access_list to string list. */ | |
163 | list = &cm->str; | |
164 | ||
165 | /* Set point to insertion point. */ | |
166 | for (point = list->head; point; point = point->next) | |
167 | if (strcmp (point->name, name) >= 0) | |
168 | break; | |
169 | } | |
170 | ||
171 | /* Link to upper list. */ | |
172 | new->parent = list; | |
173 | ||
174 | /* In case of this is the first element of master. */ | |
175 | if (list->head == NULL) | |
176 | { | |
177 | list->head = list->tail = new; | |
178 | return new; | |
179 | } | |
180 | ||
181 | /* In case of insertion is made at the tail of access_list. */ | |
182 | if (point == NULL) | |
183 | { | |
184 | new->prev = list->tail; | |
185 | list->tail->next = new; | |
186 | list->tail = new; | |
187 | return new; | |
188 | } | |
189 | ||
190 | /* In case of insertion is made at the head of access_list. */ | |
191 | if (point == list->head) | |
192 | { | |
193 | new->next = list->head; | |
194 | list->head->prev = new; | |
195 | list->head = new; | |
196 | return new; | |
197 | } | |
198 | ||
199 | /* Insertion is made at middle of the access_list. */ | |
200 | new->next = point; | |
201 | new->prev = point->prev; | |
202 | ||
203 | if (point->prev) | |
204 | point->prev->next = new; | |
205 | point->prev = new; | |
206 | ||
207 | return new; | |
208 | } | |
209 | ||
210 | struct community_list * | |
211 | community_list_lookup (struct community_list_handler *ch, | |
212 | char *name, int style) | |
213 | { | |
214 | struct community_list *list; | |
215 | struct community_list_master *cm; | |
216 | ||
217 | if (! name) | |
218 | return NULL; | |
219 | ||
220 | cm = community_list_master_lookup (ch, style); | |
221 | if (! cm) | |
222 | return NULL; | |
223 | ||
224 | for (list = cm->num.head; list; list = list->next) | |
225 | if (strcmp (list->name, name) == 0) | |
226 | return list; | |
227 | for (list = cm->str.head; list; list = list->next) | |
228 | if (strcmp (list->name, name) == 0) | |
229 | return list; | |
230 | ||
231 | return NULL; | |
232 | } | |
233 | ||
234 | struct community_list * | |
235 | community_list_get (struct community_list_handler *ch, char *name, int style) | |
236 | { | |
237 | struct community_list *list; | |
238 | ||
239 | list = community_list_lookup (ch, name, style); | |
240 | if (! list) | |
241 | list = community_list_insert (ch, name, style); | |
242 | return list; | |
243 | } | |
244 | ||
245 | void | |
246 | community_list_delete (struct community_list *list) | |
247 | { | |
248 | struct community_list_list *clist; | |
249 | struct community_entry *entry, *next; | |
250 | ||
251 | for (entry = list->head; entry; entry = next) | |
252 | { | |
253 | next = entry->next; | |
254 | community_entry_free (entry); | |
255 | } | |
256 | ||
257 | clist = list->parent; | |
258 | ||
259 | if (list->next) | |
260 | list->next->prev = list->prev; | |
261 | else | |
262 | clist->tail = list->prev; | |
263 | ||
264 | if (list->prev) | |
265 | list->prev->next = list->next; | |
266 | else | |
267 | clist->head = list->next; | |
268 | ||
269 | community_list_free (list); | |
270 | } | |
271 | ||
272 | int | |
273 | community_list_empty_p (struct community_list *list) | |
274 | { | |
275 | return (list->head == NULL && list->tail == NULL) ? 1 : 0; | |
276 | } | |
277 | \f | |
278 | /* Add community-list entry to the list. */ | |
279 | static void | |
280 | community_list_entry_add (struct community_list *list, | |
281 | struct community_entry *entry) | |
282 | { | |
283 | entry->next = NULL; | |
284 | entry->prev = list->tail; | |
285 | ||
286 | if (list->tail) | |
287 | list->tail->next = entry; | |
288 | else | |
289 | list->head = entry; | |
290 | list->tail = entry; | |
291 | } | |
292 | ||
293 | /* Delete community-list entry from the list. */ | |
294 | static void | |
295 | community_list_entry_delete (struct community_list *list, | |
296 | struct community_entry *entry, int style) | |
297 | { | |
298 | if (entry->next) | |
299 | entry->next->prev = entry->prev; | |
300 | else | |
301 | list->tail = entry->prev; | |
302 | ||
303 | if (entry->prev) | |
304 | entry->prev->next = entry->next; | |
305 | else | |
306 | list->head = entry->next; | |
307 | ||
308 | community_entry_free (entry); | |
309 | ||
310 | if (community_list_empty_p (list)) | |
311 | community_list_delete (list); | |
312 | } | |
313 | ||
314 | /* Lookup community-list entry from the list. */ | |
315 | static struct community_entry * | |
316 | community_list_entry_lookup (struct community_list *list, void *arg, | |
317 | int direct) | |
318 | { | |
319 | struct community_entry *entry; | |
320 | ||
321 | for (entry = list->head; entry; entry = entry->next) | |
322 | { | |
323 | switch (entry->style) | |
324 | { | |
325 | case COMMUNITY_LIST_STANDARD: | |
326 | if (community_cmp (entry->u.com, arg)) | |
327 | return entry; | |
328 | break; | |
329 | case EXTCOMMUNITY_LIST_STANDARD: | |
330 | if (ecommunity_cmp (entry->u.ecom, arg)) | |
331 | return entry; | |
332 | break; | |
333 | case COMMUNITY_LIST_EXPANDED: | |
334 | case EXTCOMMUNITY_LIST_EXPANDED: | |
335 | if (strcmp (entry->config, arg) == 0) | |
336 | return entry; | |
337 | break; | |
338 | default: | |
339 | break; | |
340 | } | |
341 | } | |
342 | return NULL; | |
343 | } | |
344 | \f | |
345 | /* Internal function to perform regular expression match for community | |
346 | attribute. */ | |
347 | static int | |
348 | community_regexp_match (struct community *com, regex_t *reg) | |
349 | { | |
350 | char *str; | |
351 | ||
352 | /* When there is no communities attribute it is treated as empty | |
353 | string. */ | |
354 | if (com == NULL || com->size == 0) | |
355 | str = ""; | |
356 | else | |
357 | str = community_str (com); | |
358 | ||
359 | /* Regular expression match. */ | |
360 | if (regexec (reg, str, 0, NULL, 0) == 0) | |
361 | return 1; | |
362 | ||
363 | /* No match. */ | |
364 | return 0; | |
365 | } | |
366 | ||
367 | /* Delete community attribute using regular expression match. Return | |
368 | modified communites attribute. */ | |
369 | static struct community * | |
370 | community_regexp_delete (struct community *com, regex_t *reg) | |
371 | { | |
372 | int i; | |
373 | u_int32_t comval; | |
374 | /* Maximum is "65535:65535" + '\0'. */ | |
375 | char c[12]; | |
376 | char *str; | |
377 | ||
378 | if (! com) | |
379 | return NULL; | |
380 | ||
381 | i = 0; | |
382 | while (i < com->size) | |
383 | { | |
384 | memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t)); | |
385 | comval = ntohl (comval); | |
386 | ||
387 | switch (comval) | |
388 | { | |
389 | case COMMUNITY_INTERNET: | |
390 | str = "internet"; | |
391 | break; | |
392 | case COMMUNITY_NO_EXPORT: | |
393 | str = "no-export"; | |
394 | break; | |
395 | case COMMUNITY_NO_ADVERTISE: | |
396 | str = "no-advertise"; | |
397 | break; | |
398 | case COMMUNITY_LOCAL_AS: | |
399 | str = "local-AS"; | |
400 | break; | |
401 | default: | |
402 | sprintf (c, "%d:%d", (comval >> 16) & 0xFFFF, comval & 0xFFFF); | |
403 | str = c; | |
404 | break; | |
405 | } | |
406 | ||
407 | if (regexec (reg, str, 0, NULL, 0) == 0) | |
408 | community_del_val (com, com_nthval (com, i)); | |
409 | else | |
410 | i++; | |
411 | } | |
412 | return com; | |
413 | } | |
414 | ||
415 | /* When given community attribute matches to the community-list return | |
416 | 1 else return 0. */ | |
417 | int | |
418 | community_list_match (struct community *com, struct community_list *list) | |
419 | { | |
420 | struct community_entry *entry; | |
421 | ||
422 | for (entry = list->head; entry; entry = entry->next) | |
423 | { | |
424 | if (entry->any) | |
425 | return entry->direct == COMMUNITY_PERMIT ? 1 : 0; | |
426 | ||
427 | if (entry->style == COMMUNITY_LIST_STANDARD) | |
428 | { | |
429 | if (community_include (entry->u.com, COMMUNITY_INTERNET)) | |
430 | return entry->direct == COMMUNITY_PERMIT ? 1 : 0; | |
431 | ||
432 | if (community_match (com, entry->u.com)) | |
433 | return entry->direct == COMMUNITY_PERMIT ? 1 : 0; | |
434 | } | |
435 | else if (entry->style == COMMUNITY_LIST_EXPANDED) | |
436 | { | |
437 | if (community_regexp_match (com, entry->reg)) | |
438 | return entry->direct == COMMUNITY_PERMIT ? 1 : 0; | |
439 | } | |
440 | } | |
441 | return 0; | |
442 | } | |
443 | ||
444 | /* Perform exact matching. In case of expanded community-list, do | |
445 | same thing as community_list_match(). */ | |
446 | int | |
447 | community_list_exact_match (struct community *com, struct community_list *list) | |
448 | { | |
449 | struct community_entry *entry; | |
450 | ||
451 | for (entry = list->head; entry; entry = entry->next) | |
452 | { | |
453 | if (entry->any) | |
454 | return entry->direct == COMMUNITY_PERMIT ? 1 : 0; | |
455 | ||
456 | if (entry->style == COMMUNITY_LIST_STANDARD) | |
457 | { | |
458 | if (community_include (entry->u.com, COMMUNITY_INTERNET)) | |
459 | return entry->direct == COMMUNITY_PERMIT ? 1 : 0; | |
460 | ||
461 | if (community_cmp (com, entry->u.com)) | |
462 | return entry->direct == COMMUNITY_PERMIT ? 1 : 0; | |
463 | } | |
464 | else if (entry->style == COMMUNITY_LIST_EXPANDED) | |
465 | { | |
466 | if (community_regexp_match (com, entry->reg)) | |
467 | return entry->direct == COMMUNITY_PERMIT ? 1 : 0; | |
468 | } | |
469 | } | |
470 | return 0; | |
471 | } | |
472 | ||
473 | /* Delete all permitted communities in the list from com1 */ | |
474 | struct community * | |
475 | community_list_match_delete (struct community *com, | |
476 | struct community_list *list) | |
477 | { | |
478 | struct community_entry *entry; | |
479 | ||
480 | for (entry = list->head; entry; entry = entry->next) | |
481 | { | |
482 | if (entry->any && entry->direct == COMMUNITY_PERMIT) | |
483 | { | |
484 | /* This is a tricky part. Currently only | |
485 | route_set_community_delete() uses this function. In the | |
486 | function com->size is zero, it free the community | |
487 | structure. */ | |
488 | com->size = 0; | |
489 | return com; | |
490 | } | |
491 | ||
492 | if (entry->style == COMMUNITY_LIST_STANDARD) | |
493 | { | |
494 | if (entry->direct == COMMUNITY_PERMIT) | |
495 | community_delete (com, entry->u.com); | |
496 | } | |
497 | else if (entry->style == COMMUNITY_LIST_EXPANDED) | |
498 | { | |
499 | if (entry->direct == COMMUNITY_PERMIT) | |
500 | community_regexp_delete (com, entry->reg); | |
501 | } | |
502 | } | |
503 | return com; | |
504 | } | |
505 | ||
506 | /* To avoid duplicated entry in the community-list, this function | |
507 | compares specified entry to existing entry. */ | |
508 | int | |
509 | community_list_dup_check (struct community_list *list, | |
510 | struct community_entry *new) | |
511 | { | |
512 | struct community_entry *entry; | |
513 | ||
514 | for (entry = list->head; entry; entry = entry->next) | |
515 | { | |
516 | if (entry->style != new->style) | |
517 | continue; | |
518 | ||
519 | if (entry->direct != new->direct) | |
520 | continue; | |
521 | ||
522 | if (entry->any != new->any) | |
523 | continue; | |
524 | ||
525 | if (entry->any) | |
526 | return 1; | |
527 | ||
528 | switch (entry->style) | |
529 | { | |
530 | case COMMUNITY_LIST_STANDARD: | |
531 | if (community_cmp (entry->u.com, new->u.com)) | |
532 | return 1; | |
533 | break; | |
534 | case EXTCOMMUNITY_LIST_STANDARD: | |
535 | if (ecommunity_cmp (entry->u.ecom, new->u.ecom)) | |
536 | return 1; | |
537 | break; | |
538 | case COMMUNITY_LIST_EXPANDED: | |
539 | case EXTCOMMUNITY_LIST_EXPANDED: | |
540 | if (strcmp (entry->config, new->config) == 0) | |
541 | return 1; | |
542 | break; | |
543 | default: | |
544 | break; | |
545 | } | |
546 | } | |
547 | return 0; | |
548 | } | |
549 | \f | |
550 | /* Set community-list. */ | |
551 | int | |
552 | community_list_set (struct community_list_handler *ch, | |
553 | char *name, char *str, int direct, int style) | |
554 | { | |
555 | struct community_entry *entry; | |
556 | struct community_list *list; | |
557 | struct community *com; | |
558 | regex_t *regex; | |
559 | ||
560 | entry = NULL; | |
561 | ||
562 | /* Get community list. */ | |
563 | list = community_list_get (ch, name, style); | |
564 | ||
565 | /* When community-list already has entry, new entry should have same | |
566 | style. If you want to have mixed style community-list, you can | |
567 | comment out this check. */ | |
568 | if (! community_list_empty_p (list)) | |
569 | { | |
570 | struct community_entry *first; | |
571 | ||
572 | first = list->head; | |
573 | ||
574 | if (style == COMMUNITY_LIST_AUTO) | |
575 | style = first->style; | |
576 | else if (style != first->style) | |
577 | { | |
578 | return (first->style == COMMUNITY_LIST_STANDARD | |
579 | ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT | |
580 | : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT); | |
581 | } | |
582 | } | |
583 | ||
584 | /* When str is NULL, it is matches any. */ | |
585 | if (! str) | |
586 | { | |
587 | entry = community_entry_new (); | |
588 | entry->direct = direct; | |
589 | entry->any = 1; | |
590 | if (style == COMMUNITY_LIST_AUTO) | |
591 | entry->style = COMMUNITY_LIST_STANDARD; | |
592 | else | |
593 | entry->style = style; | |
594 | } | |
595 | else | |
596 | { | |
597 | /* Standard community-list parse. String must be converted into | |
598 | community structure without problem. */ | |
599 | if (style == COMMUNITY_LIST_STANDARD || style == COMMUNITY_LIST_AUTO) | |
600 | { | |
601 | com = community_str2com (str); | |
602 | if (com) | |
603 | { | |
604 | entry = community_entry_new (); | |
605 | entry->u.com = com; | |
606 | entry->direct = direct; | |
607 | entry->style = COMMUNITY_LIST_STANDARD; | |
608 | } | |
609 | else if (style == COMMUNITY_LIST_STANDARD) | |
610 | return COMMUNITY_LIST_ERR_MALFORMED_VAL; | |
611 | ||
612 | /* We can't convert string into communities value. When | |
613 | community-list type is auto, fall dawn to regular expression | |
614 | match. */ | |
615 | } | |
616 | ||
617 | /* Expanded community-list parse. String may include regular | |
618 | expression. */ | |
619 | if (! entry && (style == COMMUNITY_LIST_EXPANDED | |
620 | || style == COMMUNITY_LIST_AUTO)) | |
621 | { | |
622 | regex = bgp_regcomp (str); | |
623 | if (regex) | |
624 | { | |
625 | entry = community_entry_new (); | |
626 | entry->reg = regex; | |
627 | entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str); | |
628 | entry->direct = direct; | |
629 | entry->style = COMMUNITY_LIST_EXPANDED; | |
630 | } | |
631 | else | |
632 | return COMMUNITY_LIST_ERR_MALFORMED_VAL; | |
633 | } | |
634 | } | |
635 | ||
636 | /* Do not put duplicated community entry. */ | |
637 | if (community_list_dup_check (list, entry)) | |
638 | community_entry_free (entry); | |
639 | else | |
640 | community_list_entry_add (list, entry); | |
641 | ||
642 | return 0; | |
643 | } | |
644 | ||
645 | /* Unset community-list. When str is NULL, delete all of | |
646 | community-list entry belongs to the specified name. */ | |
647 | int | |
648 | community_list_unset (struct community_list_handler *ch, | |
649 | char *name, char *str, int direct, int style) | |
650 | { | |
651 | struct community_entry *entry; | |
652 | struct community_list *list; | |
653 | struct community *com; | |
654 | regex_t *regex; | |
655 | ||
656 | entry = NULL; | |
657 | ||
658 | /* Lookup community list. */ | |
659 | list = community_list_lookup (ch, name, style); | |
660 | if (list == NULL) | |
661 | return COMMUNITY_LIST_ERR_CANT_FIND_LIST; | |
662 | ||
663 | /* Delete all of entry belongs to this community-list. */ | |
664 | if (! str) | |
665 | { | |
666 | community_list_delete (list); | |
667 | return 0; | |
668 | } | |
669 | ||
670 | /* Community list string is specified. Lookup entry from community | |
671 | list. */ | |
672 | if (style == COMMUNITY_LIST_STANDARD || style == COMMUNITY_LIST_AUTO) | |
673 | { | |
674 | com = community_str2com (str); | |
675 | if (com) | |
676 | { | |
677 | entry = community_list_entry_lookup (list, com, direct); | |
678 | community_free (com); | |
679 | } | |
680 | else if (style == COMMUNITY_LIST_STANDARD) | |
681 | return COMMUNITY_LIST_ERR_MALFORMED_VAL; | |
682 | ||
683 | /* If we can't convert string into community and community-list | |
684 | type is auto, fall dawn to expanded community-list. */ | |
685 | } | |
686 | ||
687 | /* Expanded community-list parse. String may include regular | |
688 | expression. */ | |
689 | if (! entry | |
690 | && (style == COMMUNITY_LIST_EXPANDED || style == COMMUNITY_LIST_AUTO)) | |
691 | { | |
692 | regex = bgp_regcomp (str); | |
693 | if (regex) | |
694 | { | |
695 | entry = community_list_entry_lookup (list, str, direct); | |
696 | bgp_regex_free (regex); | |
697 | } | |
698 | else | |
699 | return COMMUNITY_LIST_ERR_MALFORMED_VAL; | |
700 | } | |
701 | ||
702 | if (! entry) | |
703 | return COMMUNITY_LIST_ERR_CANT_FIND_LIST; | |
704 | ||
705 | community_list_entry_delete (list, entry, style); | |
706 | ||
707 | return 0; | |
708 | } | |
709 | ||
710 | /* Set extcommunity-list. */ | |
711 | int | |
712 | extcommunity_list_set (struct community_list_handler *ch, | |
713 | char *name, char *str, int direct, int style) | |
714 | { | |
715 | struct community_entry *entry; | |
716 | struct community_list *list; | |
717 | struct ecommunity *ecom; | |
718 | regex_t *regex; | |
719 | ||
720 | entry = NULL; | |
721 | ||
722 | /* Get community list. */ | |
723 | list = community_list_get (ch, name, style); | |
724 | ||
725 | /* When community-list already has entry, new entry should have same | |
726 | style. If you want to have mixed style community-list, you can | |
727 | comment out this check. */ | |
728 | if (! community_list_empty_p (list)) | |
729 | { | |
730 | struct community_entry *first; | |
731 | ||
732 | first = list->head; | |
733 | ||
734 | if (style == EXTCOMMUNITY_LIST_AUTO) | |
735 | style = first->style; | |
736 | else if (style != first->style) | |
737 | { | |
738 | return (first->style == EXTCOMMUNITY_LIST_STANDARD | |
739 | ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT | |
740 | : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT); | |
741 | } | |
742 | } | |
743 | ||
744 | /* When str is NULL, it is matches any. */ | |
745 | if (! str) | |
746 | { | |
747 | entry = community_entry_new (); | |
748 | entry->direct = direct; | |
749 | entry->any = 1; | |
750 | if (style == EXTCOMMUNITY_LIST_AUTO) | |
751 | entry->style = EXTCOMMUNITY_LIST_STANDARD; | |
752 | else | |
753 | entry->style = style; | |
754 | } | |
755 | else | |
756 | { | |
757 | /* Standard extcommunity-list parse. String is converted into | |
758 | ecommunity structure. */ | |
759 | if (style == EXTCOMMUNITY_LIST_STANDARD | |
760 | || style == EXTCOMMUNITY_LIST_AUTO) | |
761 | { | |
762 | /* Type is unknown. String includes keyword. */ | |
763 | ecom = ecommunity_str2com (str, 0, 1); | |
764 | if (ecom) | |
765 | { | |
766 | entry = community_entry_new (); | |
767 | entry->config | |
768 | = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST); | |
769 | ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY); | |
770 | entry->u.ecom = ecom; | |
771 | entry->direct = direct; | |
772 | entry->style = EXTCOMMUNITY_LIST_STANDARD; | |
773 | } | |
774 | else if (style == EXTCOMMUNITY_LIST_STANDARD) | |
775 | return COMMUNITY_LIST_ERR_MALFORMED_VAL; | |
776 | ||
777 | /* We can't convert string into communities value. When | |
778 | community-list type is auto, fall dawn to regular expression | |
779 | match. */ | |
780 | } | |
781 | ||
782 | /* Expanded extcommunity-list parse. String may include regular | |
783 | expression. */ | |
784 | if (! entry && (style == EXTCOMMUNITY_LIST_EXPANDED | |
785 | || style == EXTCOMMUNITY_LIST_AUTO)) | |
786 | { | |
787 | regex = bgp_regcomp (str); | |
788 | if (regex) | |
789 | { | |
790 | entry = community_entry_new (); | |
791 | entry->reg = regex; | |
792 | entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str); | |
793 | entry->direct = direct; | |
794 | entry->style = EXTCOMMUNITY_LIST_EXPANDED; | |
795 | } | |
796 | else | |
797 | return COMMUNITY_LIST_ERR_MALFORMED_VAL; | |
798 | } | |
799 | } | |
800 | ||
801 | /* Do not put duplicated community entry. */ | |
802 | if (community_list_dup_check (list, entry)) | |
803 | community_entry_free (entry); | |
804 | else | |
805 | community_list_entry_add (list, entry); | |
806 | ||
807 | return 0; | |
808 | } | |
809 | ||
810 | /* Unset extcommunity-list. When str is NULL, delete all of | |
811 | extcommunity-list entry belongs to the specified name. */ | |
812 | int | |
813 | extcommunity_list_unset (struct community_list_handler *ch, | |
814 | char *name, char *str, int direct, int style) | |
815 | { | |
816 | struct community_entry *entry; | |
817 | struct community_list *list; | |
818 | struct ecommunity *ecom = NULL; | |
819 | regex_t *regex; | |
820 | ||
821 | entry = NULL; | |
822 | ||
823 | /* Lookup extcommunity list. */ | |
824 | list = community_list_lookup (ch, name, style); | |
825 | if (list == NULL) | |
826 | return COMMUNITY_LIST_ERR_CANT_FIND_LIST; | |
827 | ||
828 | /* Delete all of entry belongs to this extcommunity-list. */ | |
829 | if (! str) | |
830 | { | |
831 | community_list_delete (list); | |
832 | return 0; | |
833 | } | |
834 | ||
835 | /* Community list string is specified. Lookup entry from community | |
836 | list. */ | |
837 | if (style == EXTCOMMUNITY_LIST_STANDARD || style == EXTCOMMUNITY_LIST_AUTO) | |
838 | { | |
839 | ecom = ecommunity_str2com (str, 0, 1); | |
840 | if (ecom) | |
841 | { | |
842 | entry = community_list_entry_lookup (list, ecom, direct); | |
843 | ecommunity_free (ecom); | |
844 | } | |
845 | else if (style == COMMUNITY_LIST_STANDARD) | |
846 | return COMMUNITY_LIST_ERR_MALFORMED_VAL; | |
847 | ||
848 | /* If we can't convert string into community and community-list | |
849 | type is auto, fall dawn to expanded community-list. */ | |
850 | } | |
851 | ||
852 | /* Expanded community-list parse. String may include regular | |
853 | expression. */ | |
854 | if (! entry | |
855 | && (style == COMMUNITY_LIST_EXPANDED || style == COMMUNITY_LIST_AUTO)) | |
856 | { | |
857 | regex = bgp_regcomp (str); | |
858 | if (regex) | |
859 | { | |
860 | entry = community_list_entry_lookup (list, str, direct); | |
861 | bgp_regex_free (regex); | |
862 | } | |
863 | else | |
864 | return COMMUNITY_LIST_ERR_MALFORMED_VAL; | |
865 | } | |
866 | ||
867 | if (! entry) | |
868 | return COMMUNITY_LIST_ERR_CANT_FIND_LIST; | |
869 | ||
870 | community_list_entry_delete (list, entry, style); | |
871 | ||
872 | return 0; | |
873 | } | |
874 | ||
875 | /* Initializa community-list. Return community-list handler. */ | |
876 | struct community_list_handler * | |
877 | community_list_init () | |
878 | { | |
879 | struct community_list_handler *ch; | |
880 | ch = XCALLOC (MTYPE_COMMUNITY_LIST_HANDLER, | |
881 | sizeof (struct community_list_handler)); | |
882 | return ch; | |
883 | } | |
884 | ||
885 | /* Terminate community-list. */ | |
886 | void | |
887 | community_list_terminate (struct community_list_handler *ch) | |
888 | { | |
889 | struct community_list_master *cm; | |
890 | struct community_list *list; | |
891 | ||
892 | cm = &ch->community_list; | |
893 | while ((list = cm->num.head) != NULL) | |
894 | community_list_delete (list); | |
895 | while ((list = cm->str.head) != NULL) | |
896 | community_list_delete (list); | |
897 | ||
898 | cm = &ch->extcommunity_list; | |
899 | while ((list = cm->num.head) != NULL) | |
900 | community_list_delete (list); | |
901 | while ((list = cm->str.head) != NULL) | |
902 | community_list_delete (list); | |
903 | ||
904 | XFREE (MTYPE_COMMUNITY_LIST_HANDLER, ch); | |
905 | } |