blob: 572d11c9e1c2b303b47d3c8039ac56c47999da42 [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/* BGP community-list and extcommunity-list.
2 Copyright (C) 1999 Kunihiro Ishiguro
3
4This file is part of GNU Zebra.
5
6GNU Zebra is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by the
8Free Software Foundation; either version 2, or (at your option) any
9later version.
10
11GNU Zebra is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Zebra; see the file COPYING. If not, write to the Free
18Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
1902111-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
34/* Lookup master structure for community-list or
35 extcommunity-list. */
36struct community_list_master *
37community_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:
paul8708b742003-06-07 02:03:11 +000045 return &ch->community_list;
46 break;
paul718e3742002-12-13 20:15:29 +000047 case EXTCOMMUNITY_LIST_STANDARD:
48 case EXTCOMMUNITY_LIST_EXPANDED:
49 case EXTCOMMUNITY_LIST_AUTO:
paul8708b742003-06-07 02:03:11 +000050 return &ch->extcommunity_list;
paul718e3742002-12-13 20:15:29 +000051 }
52 return NULL;
53}
54
55/* Allocate a new community list entry. */
56struct community_entry *
57community_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. */
67void
68community_entry_free (struct community_entry *entry)
69{
70 switch (entry->style)
71 {
72 case COMMUNITY_LIST_STANDARD:
73 if (entry->u.com)
paul8708b742003-06-07 02:03:11 +000074 community_free (entry->u.com);
paul718e3742002-12-13 20:15:29 +000075 break;
76 case EXTCOMMUNITY_LIST_STANDARD:
77 /* In case of standard extcommunity-list, configuration string
paul8708b742003-06-07 02:03:11 +000078 is made by ecommunity_ecom2str(). */
paul718e3742002-12-13 20:15:29 +000079 if (entry->config)
paul8708b742003-06-07 02:03:11 +000080 XFREE (MTYPE_ECOMMUNITY_STR, entry->config);
paul718e3742002-12-13 20:15:29 +000081 if (entry->u.ecom)
paul8708b742003-06-07 02:03:11 +000082 ecommunity_free (entry->u.ecom);
paul718e3742002-12-13 20:15:29 +000083 break;
84 case COMMUNITY_LIST_EXPANDED:
85 case EXTCOMMUNITY_LIST_EXPANDED:
86 if (entry->config)
paul8708b742003-06-07 02:03:11 +000087 XFREE (MTYPE_COMMUNITY_LIST_CONFIG, entry->config);
paul718e3742002-12-13 20:15:29 +000088 if (entry->reg)
paul8708b742003-06-07 02:03:11 +000089 bgp_regex_free (entry->reg);
paul718e3742002-12-13 20:15:29 +000090 default:
91 break;
92 }
93 XFREE (MTYPE_COMMUNITY_LIST_ENTRY, entry);
94}
95
96/* Allocate a new community-list. */
97struct community_list *
98community_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. */
108void
109community_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
116struct community_list *
117community_list_insert (struct community_list_handler *ch,
paulfd79ac92004-10-13 05:06:08 +0000118 const char *name, int style)
paul718e3742002-12-13 20:15:29 +0000119{
paulfd79ac92004-10-13 05:06:08 +0000120 size_t i;
paul718e3742002-12-13 20:15:29 +0000121 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);
paul8708b742003-06-07 02:03:11 +0000129 if (!cm)
paul718e3742002-12-13 20:15:29 +0000130 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]))
paul8708b742003-06-07 02:03:11 +0000141 number = (number * 10) + (name[i] - '0');
paul718e3742002-12-13 20:15:29 +0000142 else
paul8708b742003-06-07 02:03:11 +0000143 break;
paul718e3742002-12-13 20:15:29 +0000144 }
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)
paul8708b742003-06-07 02:03:11 +0000155 if (atol (point->name) >= number)
156 break;
paul718e3742002-12-13 20:15:29 +0000157 }
158 else
159 {
160 new->sort = COMMUNITY_LIST_STRING;
161
162 /* Set access_list to string list. */
163 list = &cm->str;
paul8708b742003-06-07 02:03:11 +0000164
paul718e3742002-12-13 20:15:29 +0000165 /* Set point to insertion point. */
166 for (point = list->head; point; point = point->next)
paul8708b742003-06-07 02:03:11 +0000167 if (strcmp (point->name, name) >= 0)
168 break;
paul718e3742002-12-13 20:15:29 +0000169 }
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
210struct community_list *
211community_list_lookup (struct community_list_handler *ch,
paulfd79ac92004-10-13 05:06:08 +0000212 const char *name, int style)
paul718e3742002-12-13 20:15:29 +0000213{
214 struct community_list *list;
215 struct community_list_master *cm;
216
paul8708b742003-06-07 02:03:11 +0000217 if (!name)
paul718e3742002-12-13 20:15:29 +0000218 return NULL;
219
220 cm = community_list_master_lookup (ch, style);
paul8708b742003-06-07 02:03:11 +0000221 if (!cm)
paul718e3742002-12-13 20:15:29 +0000222 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
234struct community_list *
paulfd79ac92004-10-13 05:06:08 +0000235community_list_get (struct community_list_handler *ch,
236 const char *name, int style)
paul718e3742002-12-13 20:15:29 +0000237{
238 struct community_list *list;
239
240 list = community_list_lookup (ch, name, style);
paul8708b742003-06-07 02:03:11 +0000241 if (!list)
paul718e3742002-12-13 20:15:29 +0000242 list = community_list_insert (ch, name, style);
243 return list;
244}
245
246void
247community_list_delete (struct community_list *list)
248{
249 struct community_list_list *clist;
250 struct community_entry *entry, *next;
251
252 for (entry = list->head; entry; entry = next)
253 {
254 next = entry->next;
255 community_entry_free (entry);
256 }
257
258 clist = list->parent;
259
260 if (list->next)
261 list->next->prev = list->prev;
262 else
263 clist->tail = list->prev;
264
265 if (list->prev)
266 list->prev->next = list->next;
267 else
268 clist->head = list->next;
269
270 community_list_free (list);
271}
272
paul8708b742003-06-07 02:03:11 +0000273int
paul718e3742002-12-13 20:15:29 +0000274community_list_empty_p (struct community_list *list)
275{
276 return (list->head == NULL && list->tail == NULL) ? 1 : 0;
277}
278
279/* Add community-list entry to the list. */
280static void
paul8708b742003-06-07 02:03:11 +0000281community_list_entry_add (struct community_list *list,
282 struct community_entry *entry)
paul718e3742002-12-13 20:15:29 +0000283{
284 entry->next = NULL;
285 entry->prev = list->tail;
286
287 if (list->tail)
288 list->tail->next = entry;
289 else
290 list->head = entry;
291 list->tail = entry;
292}
293
294/* Delete community-list entry from the list. */
295static void
296community_list_entry_delete (struct community_list *list,
paul8708b742003-06-07 02:03:11 +0000297 struct community_entry *entry, int style)
paul718e3742002-12-13 20:15:29 +0000298{
299 if (entry->next)
300 entry->next->prev = entry->prev;
301 else
302 list->tail = entry->prev;
303
304 if (entry->prev)
305 entry->prev->next = entry->next;
306 else
307 list->head = entry->next;
308
309 community_entry_free (entry);
310
311 if (community_list_empty_p (list))
312 community_list_delete (list);
313}
314
315/* Lookup community-list entry from the list. */
316static struct community_entry *
paulfd79ac92004-10-13 05:06:08 +0000317community_list_entry_lookup (struct community_list *list, const void *arg,
paul8708b742003-06-07 02:03:11 +0000318 int direct)
paul718e3742002-12-13 20:15:29 +0000319{
320 struct community_entry *entry;
321
322 for (entry = list->head; entry; entry = entry->next)
323 {
324 switch (entry->style)
paul8708b742003-06-07 02:03:11 +0000325 {
326 case COMMUNITY_LIST_STANDARD:
327 if (community_cmp (entry->u.com, arg))
328 return entry;
329 break;
330 case EXTCOMMUNITY_LIST_STANDARD:
331 if (ecommunity_cmp (entry->u.ecom, arg))
332 return entry;
333 break;
334 case COMMUNITY_LIST_EXPANDED:
335 case EXTCOMMUNITY_LIST_EXPANDED:
336 if (strcmp (entry->config, arg) == 0)
337 return entry;
338 break;
339 default:
340 break;
341 }
paul718e3742002-12-13 20:15:29 +0000342 }
343 return NULL;
344}
345
346/* Internal function to perform regular expression match for community
347 attribute. */
348static int
paul8708b742003-06-07 02:03:11 +0000349community_regexp_match (struct community *com, regex_t * reg)
paul718e3742002-12-13 20:15:29 +0000350{
paulfd79ac92004-10-13 05:06:08 +0000351 const char *str;
paul718e3742002-12-13 20:15:29 +0000352
353 /* When there is no communities attribute it is treated as empty
354 string. */
355 if (com == NULL || com->size == 0)
356 str = "";
357 else
358 str = community_str (com);
359
360 /* Regular expression match. */
361 if (regexec (reg, str, 0, NULL, 0) == 0)
362 return 1;
363
364 /* No match. */
365 return 0;
366}
367
paul8708b742003-06-07 02:03:11 +0000368static int
369ecommunity_regexp_match (struct ecommunity *ecom, regex_t * reg)
370{
paulfd79ac92004-10-13 05:06:08 +0000371 const char *str;
paul8708b742003-06-07 02:03:11 +0000372
373 /* When there is no communities attribute it is treated as empty
374 string. */
375 if (ecom == NULL || ecom->size == 0)
376 str = "";
377 else
378 str = ecommunity_str (ecom);
379
380 /* Regular expression match. */
381 if (regexec (reg, str, 0, NULL, 0) == 0)
382 return 1;
383
384 /* No match. */
385 return 0;
386}
387
paul718e3742002-12-13 20:15:29 +0000388/* Delete community attribute using regular expression match. Return
389 modified communites attribute. */
390static struct community *
paul8708b742003-06-07 02:03:11 +0000391community_regexp_delete (struct community *com, regex_t * reg)
paul718e3742002-12-13 20:15:29 +0000392{
393 int i;
394 u_int32_t comval;
395 /* Maximum is "65535:65535" + '\0'. */
396 char c[12];
paulfd79ac92004-10-13 05:06:08 +0000397 const char *str;
paul718e3742002-12-13 20:15:29 +0000398
paul8708b742003-06-07 02:03:11 +0000399 if (!com)
paul718e3742002-12-13 20:15:29 +0000400 return NULL;
401
402 i = 0;
403 while (i < com->size)
404 {
405 memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t));
406 comval = ntohl (comval);
407
408 switch (comval)
paul8708b742003-06-07 02:03:11 +0000409 {
410 case COMMUNITY_INTERNET:
411 str = "internet";
412 break;
413 case COMMUNITY_NO_EXPORT:
414 str = "no-export";
415 break;
416 case COMMUNITY_NO_ADVERTISE:
417 str = "no-advertise";
418 break;
419 case COMMUNITY_LOCAL_AS:
420 str = "local-AS";
421 break;
422 default:
423 sprintf (c, "%d:%d", (comval >> 16) & 0xFFFF, comval & 0xFFFF);
424 str = c;
425 break;
426 }
paul718e3742002-12-13 20:15:29 +0000427
428 if (regexec (reg, str, 0, NULL, 0) == 0)
paul8708b742003-06-07 02:03:11 +0000429 community_del_val (com, com_nthval (com, i));
paul718e3742002-12-13 20:15:29 +0000430 else
paul8708b742003-06-07 02:03:11 +0000431 i++;
paul718e3742002-12-13 20:15:29 +0000432 }
433 return com;
434}
435
436/* When given community attribute matches to the community-list return
437 1 else return 0. */
438int
439community_list_match (struct community *com, struct community_list *list)
440{
441 struct community_entry *entry;
442
443 for (entry = list->head; entry; entry = entry->next)
444 {
445 if (entry->any)
paul8708b742003-06-07 02:03:11 +0000446 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
paul718e3742002-12-13 20:15:29 +0000447
448 if (entry->style == COMMUNITY_LIST_STANDARD)
paul8708b742003-06-07 02:03:11 +0000449 {
450 if (community_include (entry->u.com, COMMUNITY_INTERNET))
451 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
paul718e3742002-12-13 20:15:29 +0000452
paul8708b742003-06-07 02:03:11 +0000453 if (community_match (com, entry->u.com))
454 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
455 }
paul718e3742002-12-13 20:15:29 +0000456 else if (entry->style == COMMUNITY_LIST_EXPANDED)
paul8708b742003-06-07 02:03:11 +0000457 {
458 if (community_regexp_match (com, entry->reg))
459 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
460 }
461 }
462 return 0;
463}
464
465int
466ecommunity_list_match (struct ecommunity *ecom, struct community_list *list)
467{
468 struct community_entry *entry;
469
470 for (entry = list->head; entry; entry = entry->next)
471 {
472 if (entry->any)
473 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
474
475 if (entry->style == EXTCOMMUNITY_LIST_STANDARD)
476 {
477 if (ecommunity_match (ecom, entry->u.ecom))
478 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
479 }
480 else if (entry->style == EXTCOMMUNITY_LIST_EXPANDED)
481 {
482 if (ecommunity_regexp_match (ecom, entry->reg))
483 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
484 }
paul718e3742002-12-13 20:15:29 +0000485 }
486 return 0;
487}
488
489/* Perform exact matching. In case of expanded community-list, do
490 same thing as community_list_match(). */
491int
paul8708b742003-06-07 02:03:11 +0000492community_list_exact_match (struct community *com,
493 struct community_list *list)
paul718e3742002-12-13 20:15:29 +0000494{
495 struct community_entry *entry;
496
497 for (entry = list->head; entry; entry = entry->next)
498 {
499 if (entry->any)
paul8708b742003-06-07 02:03:11 +0000500 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
paul718e3742002-12-13 20:15:29 +0000501
502 if (entry->style == COMMUNITY_LIST_STANDARD)
paul8708b742003-06-07 02:03:11 +0000503 {
504 if (community_include (entry->u.com, COMMUNITY_INTERNET))
505 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
paul718e3742002-12-13 20:15:29 +0000506
paul8708b742003-06-07 02:03:11 +0000507 if (community_cmp (com, entry->u.com))
508 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
509 }
paul718e3742002-12-13 20:15:29 +0000510 else if (entry->style == COMMUNITY_LIST_EXPANDED)
paul8708b742003-06-07 02:03:11 +0000511 {
512 if (community_regexp_match (com, entry->reg))
513 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
514 }
paul718e3742002-12-13 20:15:29 +0000515 }
516 return 0;
517}
518
paul8708b742003-06-07 02:03:11 +0000519/* Delete all permitted communities in the list from com. */
paul718e3742002-12-13 20:15:29 +0000520struct community *
521community_list_match_delete (struct community *com,
paul8708b742003-06-07 02:03:11 +0000522 struct community_list *list)
paul718e3742002-12-13 20:15:29 +0000523{
524 struct community_entry *entry;
525
526 for (entry = list->head; entry; entry = entry->next)
527 {
paul847375b2003-06-09 18:48:31 +0000528 if (entry->any)
paul8708b742003-06-07 02:03:11 +0000529 {
paul847375b2003-06-09 18:48:31 +0000530 if (entry->direct == COMMUNITY_PERMIT)
531 {
532 /* This is a tricky part. Currently only
533 * route_set_community_delete() uses this function. In the
534 * function com->size is zero, it free the community
535 * structure.
536 */
537 com->size = 0;
538 }
paul8708b742003-06-07 02:03:11 +0000539 return com;
540 }
paul718e3742002-12-13 20:15:29 +0000541
paul847375b2003-06-09 18:48:31 +0000542 if ((entry->style == COMMUNITY_LIST_STANDARD)
543 && (community_include (entry->u.com, COMMUNITY_INTERNET)
544 || community_match (com, entry->u.com) ))
paul8708b742003-06-07 02:03:11 +0000545 {
paul847375b2003-06-09 18:48:31 +0000546 if (entry->direct == COMMUNITY_PERMIT)
547 community_delete (com, entry->u.com);
548 else
549 break;
paul8708b742003-06-07 02:03:11 +0000550 }
paul847375b2003-06-09 18:48:31 +0000551 else if ((entry->style == COMMUNITY_LIST_EXPANDED)
552 && community_regexp_match (com, entry->reg))
paul8708b742003-06-07 02:03:11 +0000553 {
554 if (entry->direct == COMMUNITY_PERMIT)
555 community_regexp_delete (com, entry->reg);
paul847375b2003-06-09 18:48:31 +0000556 else
557 break;
paul8708b742003-06-07 02:03:11 +0000558 }
paul718e3742002-12-13 20:15:29 +0000559 }
560 return com;
561}
562
563/* To avoid duplicated entry in the community-list, this function
564 compares specified entry to existing entry. */
565int
paul8708b742003-06-07 02:03:11 +0000566community_list_dup_check (struct community_list *list,
567 struct community_entry *new)
paul718e3742002-12-13 20:15:29 +0000568{
569 struct community_entry *entry;
paul8708b742003-06-07 02:03:11 +0000570
paul718e3742002-12-13 20:15:29 +0000571 for (entry = list->head; entry; entry = entry->next)
572 {
573 if (entry->style != new->style)
paul8708b742003-06-07 02:03:11 +0000574 continue;
paul718e3742002-12-13 20:15:29 +0000575
576 if (entry->direct != new->direct)
paul8708b742003-06-07 02:03:11 +0000577 continue;
paul718e3742002-12-13 20:15:29 +0000578
579 if (entry->any != new->any)
paul8708b742003-06-07 02:03:11 +0000580 continue;
paul718e3742002-12-13 20:15:29 +0000581
582 if (entry->any)
paul8708b742003-06-07 02:03:11 +0000583 return 1;
paul718e3742002-12-13 20:15:29 +0000584
585 switch (entry->style)
paul8708b742003-06-07 02:03:11 +0000586 {
587 case COMMUNITY_LIST_STANDARD:
588 if (community_cmp (entry->u.com, new->u.com))
589 return 1;
590 break;
591 case EXTCOMMUNITY_LIST_STANDARD:
592 if (ecommunity_cmp (entry->u.ecom, new->u.ecom))
593 return 1;
594 break;
595 case COMMUNITY_LIST_EXPANDED:
596 case EXTCOMMUNITY_LIST_EXPANDED:
597 if (strcmp (entry->config, new->config) == 0)
598 return 1;
599 break;
600 default:
601 break;
602 }
paul718e3742002-12-13 20:15:29 +0000603 }
604 return 0;
605}
606
607/* Set community-list. */
608int
609community_list_set (struct community_list_handler *ch,
paulfd79ac92004-10-13 05:06:08 +0000610 const char *name, const char *str, int direct, int style)
paul718e3742002-12-13 20:15:29 +0000611{
612 struct community_entry *entry;
613 struct community_list *list;
614 struct community *com;
615 regex_t *regex;
616
617 entry = NULL;
618
619 /* Get community list. */
620 list = community_list_get (ch, name, style);
621
622 /* When community-list already has entry, new entry should have same
623 style. If you want to have mixed style community-list, you can
624 comment out this check. */
paul8708b742003-06-07 02:03:11 +0000625 if (!community_list_empty_p (list))
paul718e3742002-12-13 20:15:29 +0000626 {
627 struct community_entry *first;
628
629 first = list->head;
630
631 if (style == COMMUNITY_LIST_AUTO)
paul8708b742003-06-07 02:03:11 +0000632 style = first->style;
paul718e3742002-12-13 20:15:29 +0000633 else if (style != first->style)
paul8708b742003-06-07 02:03:11 +0000634 {
635 return (first->style == COMMUNITY_LIST_STANDARD
636 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
637 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
638 }
paul718e3742002-12-13 20:15:29 +0000639 }
640
641 /* When str is NULL, it is matches any. */
paul8708b742003-06-07 02:03:11 +0000642 if (!str)
paul718e3742002-12-13 20:15:29 +0000643 {
644 entry = community_entry_new ();
645 entry->direct = direct;
646 entry->any = 1;
647 if (style == COMMUNITY_LIST_AUTO)
paul8708b742003-06-07 02:03:11 +0000648 entry->style = COMMUNITY_LIST_STANDARD;
paul718e3742002-12-13 20:15:29 +0000649 else
paul8708b742003-06-07 02:03:11 +0000650 entry->style = style;
paul718e3742002-12-13 20:15:29 +0000651 }
652 else
653 {
654 /* Standard community-list parse. String must be converted into
paul8708b742003-06-07 02:03:11 +0000655 community structure without problem. */
paul718e3742002-12-13 20:15:29 +0000656 if (style == COMMUNITY_LIST_STANDARD || style == COMMUNITY_LIST_AUTO)
paul8708b742003-06-07 02:03:11 +0000657 {
658 com = community_str2com (str);
659 if (com)
660 {
661 entry = community_entry_new ();
662 entry->u.com = com;
663 entry->direct = direct;
664 entry->style = COMMUNITY_LIST_STANDARD;
665 }
666 else if (style == COMMUNITY_LIST_STANDARD)
667 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
668
669 /* We can't convert string into communities value. When
670 community-list type is auto, fall dawn to regular expression
671 match. */
672 }
paul718e3742002-12-13 20:15:29 +0000673
674 /* Expanded community-list parse. String may include regular
paul8708b742003-06-07 02:03:11 +0000675 expression. */
676 if (!entry && (style == COMMUNITY_LIST_EXPANDED
677 || style == COMMUNITY_LIST_AUTO))
678 {
679 regex = bgp_regcomp (str);
680 if (regex)
681 {
682 entry = community_entry_new ();
683 entry->reg = regex;
684 entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str);
685 entry->direct = direct;
686 entry->style = COMMUNITY_LIST_EXPANDED;
687 }
688 else
689 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
690 }
paul718e3742002-12-13 20:15:29 +0000691 }
692
693 /* Do not put duplicated community entry. */
694 if (community_list_dup_check (list, entry))
695 community_entry_free (entry);
696 else
697 community_list_entry_add (list, entry);
698
699 return 0;
700}
701
702/* Unset community-list. When str is NULL, delete all of
703 community-list entry belongs to the specified name. */
704int
705community_list_unset (struct community_list_handler *ch,
paulfd79ac92004-10-13 05:06:08 +0000706 const char *name, const char *str,
707 int direct, int style)
paul718e3742002-12-13 20:15:29 +0000708{
709 struct community_entry *entry;
710 struct community_list *list;
711 struct community *com;
712 regex_t *regex;
713
714 entry = NULL;
715
716 /* Lookup community list. */
717 list = community_list_lookup (ch, name, style);
718 if (list == NULL)
719 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
720
721 /* Delete all of entry belongs to this community-list. */
paul8708b742003-06-07 02:03:11 +0000722 if (!str)
paul718e3742002-12-13 20:15:29 +0000723 {
724 community_list_delete (list);
725 return 0;
726 }
727
728 /* Community list string is specified. Lookup entry from community
729 list. */
730 if (style == COMMUNITY_LIST_STANDARD || style == COMMUNITY_LIST_AUTO)
731 {
732 com = community_str2com (str);
733 if (com)
paul8708b742003-06-07 02:03:11 +0000734 {
735 entry = community_list_entry_lookup (list, com, direct);
736 community_free (com);
737 }
paul718e3742002-12-13 20:15:29 +0000738 else if (style == COMMUNITY_LIST_STANDARD)
paul8708b742003-06-07 02:03:11 +0000739 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
paul718e3742002-12-13 20:15:29 +0000740
741 /* If we can't convert string into community and community-list
paul8708b742003-06-07 02:03:11 +0000742 type is auto, fall dawn to expanded community-list. */
paul718e3742002-12-13 20:15:29 +0000743 }
744
745 /* Expanded community-list parse. String may include regular
746 expression. */
paul8708b742003-06-07 02:03:11 +0000747 if (!entry
paul718e3742002-12-13 20:15:29 +0000748 && (style == COMMUNITY_LIST_EXPANDED || style == COMMUNITY_LIST_AUTO))
749 {
750 regex = bgp_regcomp (str);
751 if (regex)
paul8708b742003-06-07 02:03:11 +0000752 {
753 entry = community_list_entry_lookup (list, str, direct);
754 bgp_regex_free (regex);
755 }
paul718e3742002-12-13 20:15:29 +0000756 else
paul8708b742003-06-07 02:03:11 +0000757 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
paul718e3742002-12-13 20:15:29 +0000758 }
759
paul8708b742003-06-07 02:03:11 +0000760 if (!entry)
paul718e3742002-12-13 20:15:29 +0000761 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
762
763 community_list_entry_delete (list, entry, style);
764
765 return 0;
766}
767
768/* Set extcommunity-list. */
769int
770extcommunity_list_set (struct community_list_handler *ch,
paulfd79ac92004-10-13 05:06:08 +0000771 const char *name, const char *str,
772 int direct, int style)
paul718e3742002-12-13 20:15:29 +0000773{
774 struct community_entry *entry;
775 struct community_list *list;
776 struct ecommunity *ecom;
777 regex_t *regex;
778
779 entry = NULL;
780
781 /* Get community list. */
782 list = community_list_get (ch, name, style);
783
784 /* When community-list already has entry, new entry should have same
785 style. If you want to have mixed style community-list, you can
786 comment out this check. */
paul8708b742003-06-07 02:03:11 +0000787 if (!community_list_empty_p (list))
paul718e3742002-12-13 20:15:29 +0000788 {
789 struct community_entry *first;
790
791 first = list->head;
792
793 if (style == EXTCOMMUNITY_LIST_AUTO)
paul8708b742003-06-07 02:03:11 +0000794 style = first->style;
paul718e3742002-12-13 20:15:29 +0000795 else if (style != first->style)
paul8708b742003-06-07 02:03:11 +0000796 {
797 return (first->style == EXTCOMMUNITY_LIST_STANDARD
798 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
799 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
800 }
paul718e3742002-12-13 20:15:29 +0000801 }
802
803 /* When str is NULL, it is matches any. */
paul8708b742003-06-07 02:03:11 +0000804 if (!str)
paul718e3742002-12-13 20:15:29 +0000805 {
806 entry = community_entry_new ();
807 entry->direct = direct;
808 entry->any = 1;
809 if (style == EXTCOMMUNITY_LIST_AUTO)
paul8708b742003-06-07 02:03:11 +0000810 entry->style = EXTCOMMUNITY_LIST_STANDARD;
paul718e3742002-12-13 20:15:29 +0000811 else
paul8708b742003-06-07 02:03:11 +0000812 entry->style = style;
paul718e3742002-12-13 20:15:29 +0000813 }
814 else
815 {
816 /* Standard extcommunity-list parse. String is converted into
paul8708b742003-06-07 02:03:11 +0000817 ecommunity structure. */
paul718e3742002-12-13 20:15:29 +0000818 if (style == EXTCOMMUNITY_LIST_STANDARD
paul8708b742003-06-07 02:03:11 +0000819 || style == EXTCOMMUNITY_LIST_AUTO)
820 {
821 /* Type is unknown. String includes keyword. */
822 ecom = ecommunity_str2com (str, 0, 1);
823 if (ecom)
824 {
825 entry = community_entry_new ();
826 entry->config
827 =
828 ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST);
829 ecom->str =
830 ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
831 entry->u.ecom = ecom;
832 entry->direct = direct;
833 entry->style = EXTCOMMUNITY_LIST_STANDARD;
834 }
835 else if (style == EXTCOMMUNITY_LIST_STANDARD)
836 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
paul718e3742002-12-13 20:15:29 +0000837
paul8708b742003-06-07 02:03:11 +0000838 /* We can't convert string into communities value. When
839 community-list type is auto, fall dawn to regular expression
840 match. */
841 }
paul718e3742002-12-13 20:15:29 +0000842
843 /* Expanded extcommunity-list parse. String may include regular
paul8708b742003-06-07 02:03:11 +0000844 expression. */
845 if (!entry && (style == EXTCOMMUNITY_LIST_EXPANDED
846 || style == EXTCOMMUNITY_LIST_AUTO))
847 {
848 regex = bgp_regcomp (str);
849 if (regex)
850 {
851 entry = community_entry_new ();
852 entry->reg = regex;
853 entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str);
854 entry->direct = direct;
855 entry->style = EXTCOMMUNITY_LIST_EXPANDED;
856 }
857 else
858 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
859 }
paul718e3742002-12-13 20:15:29 +0000860 }
861
862 /* Do not put duplicated community entry. */
863 if (community_list_dup_check (list, entry))
864 community_entry_free (entry);
865 else
866 community_list_entry_add (list, entry);
867
868 return 0;
869}
870
871/* Unset extcommunity-list. When str is NULL, delete all of
872 extcommunity-list entry belongs to the specified name. */
873int
874extcommunity_list_unset (struct community_list_handler *ch,
paulfd79ac92004-10-13 05:06:08 +0000875 const char *name, const char *str,
876 int direct, int style)
paul718e3742002-12-13 20:15:29 +0000877{
878 struct community_entry *entry;
879 struct community_list *list;
880 struct ecommunity *ecom = NULL;
881 regex_t *regex;
882
883 entry = NULL;
884
885 /* Lookup extcommunity list. */
886 list = community_list_lookup (ch, name, style);
887 if (list == NULL)
888 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
889
890 /* Delete all of entry belongs to this extcommunity-list. */
paul8708b742003-06-07 02:03:11 +0000891 if (!str)
paul718e3742002-12-13 20:15:29 +0000892 {
893 community_list_delete (list);
894 return 0;
895 }
896
897 /* Community list string is specified. Lookup entry from community
898 list. */
899 if (style == EXTCOMMUNITY_LIST_STANDARD || style == EXTCOMMUNITY_LIST_AUTO)
900 {
901 ecom = ecommunity_str2com (str, 0, 1);
902 if (ecom)
paul8708b742003-06-07 02:03:11 +0000903 {
904 entry = community_list_entry_lookup (list, ecom, direct);
905 ecommunity_free (ecom);
906 }
paul718e3742002-12-13 20:15:29 +0000907 else if (style == COMMUNITY_LIST_STANDARD)
paul8708b742003-06-07 02:03:11 +0000908 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
paul718e3742002-12-13 20:15:29 +0000909
910 /* If we can't convert string into community and community-list
paul8708b742003-06-07 02:03:11 +0000911 type is auto, fall dawn to expanded community-list. */
paul718e3742002-12-13 20:15:29 +0000912 }
913
914 /* Expanded community-list parse. String may include regular
915 expression. */
paul8708b742003-06-07 02:03:11 +0000916 if (!entry
paul718e3742002-12-13 20:15:29 +0000917 && (style == COMMUNITY_LIST_EXPANDED || style == COMMUNITY_LIST_AUTO))
918 {
919 regex = bgp_regcomp (str);
920 if (regex)
paul8708b742003-06-07 02:03:11 +0000921 {
922 entry = community_list_entry_lookup (list, str, direct);
923 bgp_regex_free (regex);
924 }
paul718e3742002-12-13 20:15:29 +0000925 else
paul8708b742003-06-07 02:03:11 +0000926 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
paul718e3742002-12-13 20:15:29 +0000927 }
928
paul8708b742003-06-07 02:03:11 +0000929 if (!entry)
paul718e3742002-12-13 20:15:29 +0000930 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
931
932 community_list_entry_delete (list, entry, style);
933
934 return 0;
935}
936
937/* Initializa community-list. Return community-list handler. */
938struct community_list_handler *
939community_list_init ()
940{
941 struct community_list_handler *ch;
942 ch = XCALLOC (MTYPE_COMMUNITY_LIST_HANDLER,
paul8708b742003-06-07 02:03:11 +0000943 sizeof (struct community_list_handler));
paul718e3742002-12-13 20:15:29 +0000944 return ch;
945}
946
947/* Terminate community-list. */
948void
949community_list_terminate (struct community_list_handler *ch)
950{
951 struct community_list_master *cm;
952 struct community_list *list;
953
954 cm = &ch->community_list;
955 while ((list = cm->num.head) != NULL)
956 community_list_delete (list);
957 while ((list = cm->str.head) != NULL)
958 community_list_delete (list);
959
960 cm = &ch->extcommunity_list;
961 while ((list = cm->num.head) != NULL)
962 community_list_delete (list);
963 while ((list = cm->str.head) != NULL)
964 community_list_delete (list);
965
966 XFREE (MTYPE_COMMUNITY_LIST_HANDLER, ch);
967}