blob: a2c12100c8adafbface3d444810630aca80ccb0a [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,
paul8708b742003-06-07 02:03:11 +0000118 char *name, int style)
paul718e3742002-12-13 20:15:29 +0000119{
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);
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,
paul8708b742003-06-07 02:03:11 +0000212 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 *
235community_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);
paul8708b742003-06-07 02:03:11 +0000240 if (!list)
paul718e3742002-12-13 20:15:29 +0000241 list = community_list_insert (ch, name, style);
242 return list;
243}
244
245void
246community_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
paul8708b742003-06-07 02:03:11 +0000272int
paul718e3742002-12-13 20:15:29 +0000273community_list_empty_p (struct community_list *list)
274{
275 return (list->head == NULL && list->tail == NULL) ? 1 : 0;
276}
277
278/* Add community-list entry to the list. */
279static void
paul8708b742003-06-07 02:03:11 +0000280community_list_entry_add (struct community_list *list,
281 struct community_entry *entry)
paul718e3742002-12-13 20:15:29 +0000282{
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. */
294static void
295community_list_entry_delete (struct community_list *list,
paul8708b742003-06-07 02:03:11 +0000296 struct community_entry *entry, int style)
paul718e3742002-12-13 20:15:29 +0000297{
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. */
315static struct community_entry *
316community_list_entry_lookup (struct community_list *list, void *arg,
paul8708b742003-06-07 02:03:11 +0000317 int direct)
paul718e3742002-12-13 20:15:29 +0000318{
319 struct community_entry *entry;
320
321 for (entry = list->head; entry; entry = entry->next)
322 {
323 switch (entry->style)
paul8708b742003-06-07 02:03:11 +0000324 {
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 }
paul718e3742002-12-13 20:15:29 +0000341 }
342 return NULL;
343}
344
345/* Internal function to perform regular expression match for community
346 attribute. */
347static int
paul8708b742003-06-07 02:03:11 +0000348community_regexp_match (struct community *com, regex_t * reg)
paul718e3742002-12-13 20:15:29 +0000349{
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
paul8708b742003-06-07 02:03:11 +0000367static int
368ecommunity_regexp_match (struct ecommunity *ecom, regex_t * reg)
369{
370 char *str;
371
372 /* When there is no communities attribute it is treated as empty
373 string. */
374 if (ecom == NULL || ecom->size == 0)
375 str = "";
376 else
377 str = ecommunity_str (ecom);
378
379 /* Regular expression match. */
380 if (regexec (reg, str, 0, NULL, 0) == 0)
381 return 1;
382
383 /* No match. */
384 return 0;
385}
386
paul718e3742002-12-13 20:15:29 +0000387/* Delete community attribute using regular expression match. Return
388 modified communites attribute. */
389static struct community *
paul8708b742003-06-07 02:03:11 +0000390community_regexp_delete (struct community *com, regex_t * reg)
paul718e3742002-12-13 20:15:29 +0000391{
392 int i;
393 u_int32_t comval;
394 /* Maximum is "65535:65535" + '\0'. */
395 char c[12];
396 char *str;
397
paul8708b742003-06-07 02:03:11 +0000398 if (!com)
paul718e3742002-12-13 20:15:29 +0000399 return NULL;
400
401 i = 0;
402 while (i < com->size)
403 {
404 memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t));
405 comval = ntohl (comval);
406
407 switch (comval)
paul8708b742003-06-07 02:03:11 +0000408 {
409 case COMMUNITY_INTERNET:
410 str = "internet";
411 break;
412 case COMMUNITY_NO_EXPORT:
413 str = "no-export";
414 break;
415 case COMMUNITY_NO_ADVERTISE:
416 str = "no-advertise";
417 break;
418 case COMMUNITY_LOCAL_AS:
419 str = "local-AS";
420 break;
421 default:
422 sprintf (c, "%d:%d", (comval >> 16) & 0xFFFF, comval & 0xFFFF);
423 str = c;
424 break;
425 }
paul718e3742002-12-13 20:15:29 +0000426
427 if (regexec (reg, str, 0, NULL, 0) == 0)
paul8708b742003-06-07 02:03:11 +0000428 community_del_val (com, com_nthval (com, i));
paul718e3742002-12-13 20:15:29 +0000429 else
paul8708b742003-06-07 02:03:11 +0000430 i++;
paul718e3742002-12-13 20:15:29 +0000431 }
432 return com;
433}
434
435/* When given community attribute matches to the community-list return
436 1 else return 0. */
437int
438community_list_match (struct community *com, struct community_list *list)
439{
440 struct community_entry *entry;
441
442 for (entry = list->head; entry; entry = entry->next)
443 {
444 if (entry->any)
paul8708b742003-06-07 02:03:11 +0000445 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
paul718e3742002-12-13 20:15:29 +0000446
447 if (entry->style == COMMUNITY_LIST_STANDARD)
paul8708b742003-06-07 02:03:11 +0000448 {
449 if (community_include (entry->u.com, COMMUNITY_INTERNET))
450 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
paul718e3742002-12-13 20:15:29 +0000451
paul8708b742003-06-07 02:03:11 +0000452 if (community_match (com, entry->u.com))
453 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
454 }
paul718e3742002-12-13 20:15:29 +0000455 else if (entry->style == COMMUNITY_LIST_EXPANDED)
paul8708b742003-06-07 02:03:11 +0000456 {
457 if (community_regexp_match (com, entry->reg))
458 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
459 }
460 }
461 return 0;
462}
463
464int
465ecommunity_list_match (struct ecommunity *ecom, struct community_list *list)
466{
467 struct community_entry *entry;
468
469 for (entry = list->head; entry; entry = entry->next)
470 {
471 if (entry->any)
472 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
473
474 if (entry->style == EXTCOMMUNITY_LIST_STANDARD)
475 {
476 if (ecommunity_match (ecom, entry->u.ecom))
477 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
478 }
479 else if (entry->style == EXTCOMMUNITY_LIST_EXPANDED)
480 {
481 if (ecommunity_regexp_match (ecom, entry->reg))
482 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
483 }
paul718e3742002-12-13 20:15:29 +0000484 }
485 return 0;
486}
487
488/* Perform exact matching. In case of expanded community-list, do
489 same thing as community_list_match(). */
490int
paul8708b742003-06-07 02:03:11 +0000491community_list_exact_match (struct community *com,
492 struct community_list *list)
paul718e3742002-12-13 20:15:29 +0000493{
494 struct community_entry *entry;
495
496 for (entry = list->head; entry; entry = entry->next)
497 {
498 if (entry->any)
paul8708b742003-06-07 02:03:11 +0000499 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
paul718e3742002-12-13 20:15:29 +0000500
501 if (entry->style == COMMUNITY_LIST_STANDARD)
paul8708b742003-06-07 02:03:11 +0000502 {
503 if (community_include (entry->u.com, COMMUNITY_INTERNET))
504 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
paul718e3742002-12-13 20:15:29 +0000505
paul8708b742003-06-07 02:03:11 +0000506 if (community_cmp (com, entry->u.com))
507 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
508 }
paul718e3742002-12-13 20:15:29 +0000509 else if (entry->style == COMMUNITY_LIST_EXPANDED)
paul8708b742003-06-07 02:03:11 +0000510 {
511 if (community_regexp_match (com, entry->reg))
512 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
513 }
paul718e3742002-12-13 20:15:29 +0000514 }
515 return 0;
516}
517
paul8708b742003-06-07 02:03:11 +0000518/* Delete all permitted communities in the list from com. */
paul718e3742002-12-13 20:15:29 +0000519struct community *
520community_list_match_delete (struct community *com,
paul8708b742003-06-07 02:03:11 +0000521 struct community_list *list)
paul718e3742002-12-13 20:15:29 +0000522{
523 struct community_entry *entry;
524
525 for (entry = list->head; entry; entry = entry->next)
526 {
paul847375b2003-06-09 18:48:31 +0000527 if (entry->any)
paul8708b742003-06-07 02:03:11 +0000528 {
paul847375b2003-06-09 18:48:31 +0000529 if (entry->direct == COMMUNITY_PERMIT)
530 {
531 /* This is a tricky part. Currently only
532 * route_set_community_delete() uses this function. In the
533 * function com->size is zero, it free the community
534 * structure.
535 */
536 com->size = 0;
537 }
paul8708b742003-06-07 02:03:11 +0000538 return com;
539 }
paul718e3742002-12-13 20:15:29 +0000540
paul847375b2003-06-09 18:48:31 +0000541 if ((entry->style == COMMUNITY_LIST_STANDARD)
542 && (community_include (entry->u.com, COMMUNITY_INTERNET)
543 || community_match (com, entry->u.com) ))
paul8708b742003-06-07 02:03:11 +0000544 {
paul847375b2003-06-09 18:48:31 +0000545 if (entry->direct == COMMUNITY_PERMIT)
546 community_delete (com, entry->u.com);
547 else
548 break;
paul8708b742003-06-07 02:03:11 +0000549 }
paul847375b2003-06-09 18:48:31 +0000550 else if ((entry->style == COMMUNITY_LIST_EXPANDED)
551 && community_regexp_match (com, entry->reg))
paul8708b742003-06-07 02:03:11 +0000552 {
553 if (entry->direct == COMMUNITY_PERMIT)
554 community_regexp_delete (com, entry->reg);
paul847375b2003-06-09 18:48:31 +0000555 else
556 break;
paul8708b742003-06-07 02:03:11 +0000557 }
paul718e3742002-12-13 20:15:29 +0000558 }
559 return com;
560}
561
562/* To avoid duplicated entry in the community-list, this function
563 compares specified entry to existing entry. */
564int
paul8708b742003-06-07 02:03:11 +0000565community_list_dup_check (struct community_list *list,
566 struct community_entry *new)
paul718e3742002-12-13 20:15:29 +0000567{
568 struct community_entry *entry;
paul8708b742003-06-07 02:03:11 +0000569
paul718e3742002-12-13 20:15:29 +0000570 for (entry = list->head; entry; entry = entry->next)
571 {
572 if (entry->style != new->style)
paul8708b742003-06-07 02:03:11 +0000573 continue;
paul718e3742002-12-13 20:15:29 +0000574
575 if (entry->direct != new->direct)
paul8708b742003-06-07 02:03:11 +0000576 continue;
paul718e3742002-12-13 20:15:29 +0000577
578 if (entry->any != new->any)
paul8708b742003-06-07 02:03:11 +0000579 continue;
paul718e3742002-12-13 20:15:29 +0000580
581 if (entry->any)
paul8708b742003-06-07 02:03:11 +0000582 return 1;
paul718e3742002-12-13 20:15:29 +0000583
584 switch (entry->style)
paul8708b742003-06-07 02:03:11 +0000585 {
586 case COMMUNITY_LIST_STANDARD:
587 if (community_cmp (entry->u.com, new->u.com))
588 return 1;
589 break;
590 case EXTCOMMUNITY_LIST_STANDARD:
591 if (ecommunity_cmp (entry->u.ecom, new->u.ecom))
592 return 1;
593 break;
594 case COMMUNITY_LIST_EXPANDED:
595 case EXTCOMMUNITY_LIST_EXPANDED:
596 if (strcmp (entry->config, new->config) == 0)
597 return 1;
598 break;
599 default:
600 break;
601 }
paul718e3742002-12-13 20:15:29 +0000602 }
603 return 0;
604}
605
606/* Set community-list. */
607int
608community_list_set (struct community_list_handler *ch,
paul8708b742003-06-07 02:03:11 +0000609 char *name, char *str, int direct, int style)
paul718e3742002-12-13 20:15:29 +0000610{
611 struct community_entry *entry;
612 struct community_list *list;
613 struct community *com;
614 regex_t *regex;
615
616 entry = NULL;
617
618 /* Get community list. */
619 list = community_list_get (ch, name, style);
620
621 /* When community-list already has entry, new entry should have same
622 style. If you want to have mixed style community-list, you can
623 comment out this check. */
paul8708b742003-06-07 02:03:11 +0000624 if (!community_list_empty_p (list))
paul718e3742002-12-13 20:15:29 +0000625 {
626 struct community_entry *first;
627
628 first = list->head;
629
630 if (style == COMMUNITY_LIST_AUTO)
paul8708b742003-06-07 02:03:11 +0000631 style = first->style;
paul718e3742002-12-13 20:15:29 +0000632 else if (style != first->style)
paul8708b742003-06-07 02:03:11 +0000633 {
634 return (first->style == COMMUNITY_LIST_STANDARD
635 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
636 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
637 }
paul718e3742002-12-13 20:15:29 +0000638 }
639
640 /* When str is NULL, it is matches any. */
paul8708b742003-06-07 02:03:11 +0000641 if (!str)
paul718e3742002-12-13 20:15:29 +0000642 {
643 entry = community_entry_new ();
644 entry->direct = direct;
645 entry->any = 1;
646 if (style == COMMUNITY_LIST_AUTO)
paul8708b742003-06-07 02:03:11 +0000647 entry->style = COMMUNITY_LIST_STANDARD;
paul718e3742002-12-13 20:15:29 +0000648 else
paul8708b742003-06-07 02:03:11 +0000649 entry->style = style;
paul718e3742002-12-13 20:15:29 +0000650 }
651 else
652 {
653 /* Standard community-list parse. String must be converted into
paul8708b742003-06-07 02:03:11 +0000654 community structure without problem. */
paul718e3742002-12-13 20:15:29 +0000655 if (style == COMMUNITY_LIST_STANDARD || style == COMMUNITY_LIST_AUTO)
paul8708b742003-06-07 02:03:11 +0000656 {
657 com = community_str2com (str);
658 if (com)
659 {
660 entry = community_entry_new ();
661 entry->u.com = com;
662 entry->direct = direct;
663 entry->style = COMMUNITY_LIST_STANDARD;
664 }
665 else if (style == COMMUNITY_LIST_STANDARD)
666 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
667
668 /* We can't convert string into communities value. When
669 community-list type is auto, fall dawn to regular expression
670 match. */
671 }
paul718e3742002-12-13 20:15:29 +0000672
673 /* Expanded community-list parse. String may include regular
paul8708b742003-06-07 02:03:11 +0000674 expression. */
675 if (!entry && (style == COMMUNITY_LIST_EXPANDED
676 || style == COMMUNITY_LIST_AUTO))
677 {
678 regex = bgp_regcomp (str);
679 if (regex)
680 {
681 entry = community_entry_new ();
682 entry->reg = regex;
683 entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str);
684 entry->direct = direct;
685 entry->style = COMMUNITY_LIST_EXPANDED;
686 }
687 else
688 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
689 }
paul718e3742002-12-13 20:15:29 +0000690 }
691
692 /* Do not put duplicated community entry. */
693 if (community_list_dup_check (list, entry))
694 community_entry_free (entry);
695 else
696 community_list_entry_add (list, entry);
697
698 return 0;
699}
700
701/* Unset community-list. When str is NULL, delete all of
702 community-list entry belongs to the specified name. */
703int
704community_list_unset (struct community_list_handler *ch,
paul8708b742003-06-07 02:03:11 +0000705 char *name, char *str, int direct, int style)
paul718e3742002-12-13 20:15:29 +0000706{
707 struct community_entry *entry;
708 struct community_list *list;
709 struct community *com;
710 regex_t *regex;
711
712 entry = NULL;
713
714 /* Lookup community list. */
715 list = community_list_lookup (ch, name, style);
716 if (list == NULL)
717 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
718
719 /* Delete all of entry belongs to this community-list. */
paul8708b742003-06-07 02:03:11 +0000720 if (!str)
paul718e3742002-12-13 20:15:29 +0000721 {
722 community_list_delete (list);
723 return 0;
724 }
725
726 /* Community list string is specified. Lookup entry from community
727 list. */
728 if (style == COMMUNITY_LIST_STANDARD || style == COMMUNITY_LIST_AUTO)
729 {
730 com = community_str2com (str);
731 if (com)
paul8708b742003-06-07 02:03:11 +0000732 {
733 entry = community_list_entry_lookup (list, com, direct);
734 community_free (com);
735 }
paul718e3742002-12-13 20:15:29 +0000736 else if (style == COMMUNITY_LIST_STANDARD)
paul8708b742003-06-07 02:03:11 +0000737 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
paul718e3742002-12-13 20:15:29 +0000738
739 /* If we can't convert string into community and community-list
paul8708b742003-06-07 02:03:11 +0000740 type is auto, fall dawn to expanded community-list. */
paul718e3742002-12-13 20:15:29 +0000741 }
742
743 /* Expanded community-list parse. String may include regular
744 expression. */
paul8708b742003-06-07 02:03:11 +0000745 if (!entry
paul718e3742002-12-13 20:15:29 +0000746 && (style == COMMUNITY_LIST_EXPANDED || style == COMMUNITY_LIST_AUTO))
747 {
748 regex = bgp_regcomp (str);
749 if (regex)
paul8708b742003-06-07 02:03:11 +0000750 {
751 entry = community_list_entry_lookup (list, str, direct);
752 bgp_regex_free (regex);
753 }
paul718e3742002-12-13 20:15:29 +0000754 else
paul8708b742003-06-07 02:03:11 +0000755 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
paul718e3742002-12-13 20:15:29 +0000756 }
757
paul8708b742003-06-07 02:03:11 +0000758 if (!entry)
paul718e3742002-12-13 20:15:29 +0000759 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
760
761 community_list_entry_delete (list, entry, style);
762
763 return 0;
764}
765
766/* Set extcommunity-list. */
767int
768extcommunity_list_set (struct community_list_handler *ch,
paul8708b742003-06-07 02:03:11 +0000769 char *name, char *str, int direct, int style)
paul718e3742002-12-13 20:15:29 +0000770{
771 struct community_entry *entry;
772 struct community_list *list;
773 struct ecommunity *ecom;
774 regex_t *regex;
775
776 entry = NULL;
777
778 /* Get community list. */
779 list = community_list_get (ch, name, style);
780
781 /* When community-list already has entry, new entry should have same
782 style. If you want to have mixed style community-list, you can
783 comment out this check. */
paul8708b742003-06-07 02:03:11 +0000784 if (!community_list_empty_p (list))
paul718e3742002-12-13 20:15:29 +0000785 {
786 struct community_entry *first;
787
788 first = list->head;
789
790 if (style == EXTCOMMUNITY_LIST_AUTO)
paul8708b742003-06-07 02:03:11 +0000791 style = first->style;
paul718e3742002-12-13 20:15:29 +0000792 else if (style != first->style)
paul8708b742003-06-07 02:03:11 +0000793 {
794 return (first->style == EXTCOMMUNITY_LIST_STANDARD
795 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
796 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
797 }
paul718e3742002-12-13 20:15:29 +0000798 }
799
800 /* When str is NULL, it is matches any. */
paul8708b742003-06-07 02:03:11 +0000801 if (!str)
paul718e3742002-12-13 20:15:29 +0000802 {
803 entry = community_entry_new ();
804 entry->direct = direct;
805 entry->any = 1;
806 if (style == EXTCOMMUNITY_LIST_AUTO)
paul8708b742003-06-07 02:03:11 +0000807 entry->style = EXTCOMMUNITY_LIST_STANDARD;
paul718e3742002-12-13 20:15:29 +0000808 else
paul8708b742003-06-07 02:03:11 +0000809 entry->style = style;
paul718e3742002-12-13 20:15:29 +0000810 }
811 else
812 {
813 /* Standard extcommunity-list parse. String is converted into
paul8708b742003-06-07 02:03:11 +0000814 ecommunity structure. */
paul718e3742002-12-13 20:15:29 +0000815 if (style == EXTCOMMUNITY_LIST_STANDARD
paul8708b742003-06-07 02:03:11 +0000816 || style == EXTCOMMUNITY_LIST_AUTO)
817 {
818 /* Type is unknown. String includes keyword. */
819 ecom = ecommunity_str2com (str, 0, 1);
820 if (ecom)
821 {
822 entry = community_entry_new ();
823 entry->config
824 =
825 ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST);
826 ecom->str =
827 ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
828 entry->u.ecom = ecom;
829 entry->direct = direct;
830 entry->style = EXTCOMMUNITY_LIST_STANDARD;
831 }
832 else if (style == EXTCOMMUNITY_LIST_STANDARD)
833 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
paul718e3742002-12-13 20:15:29 +0000834
paul8708b742003-06-07 02:03:11 +0000835 /* We can't convert string into communities value. When
836 community-list type is auto, fall dawn to regular expression
837 match. */
838 }
paul718e3742002-12-13 20:15:29 +0000839
840 /* Expanded extcommunity-list parse. String may include regular
paul8708b742003-06-07 02:03:11 +0000841 expression. */
842 if (!entry && (style == EXTCOMMUNITY_LIST_EXPANDED
843 || style == EXTCOMMUNITY_LIST_AUTO))
844 {
845 regex = bgp_regcomp (str);
846 if (regex)
847 {
848 entry = community_entry_new ();
849 entry->reg = regex;
850 entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str);
851 entry->direct = direct;
852 entry->style = EXTCOMMUNITY_LIST_EXPANDED;
853 }
854 else
855 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
856 }
paul718e3742002-12-13 20:15:29 +0000857 }
858
859 /* Do not put duplicated community entry. */
860 if (community_list_dup_check (list, entry))
861 community_entry_free (entry);
862 else
863 community_list_entry_add (list, entry);
864
865 return 0;
866}
867
868/* Unset extcommunity-list. When str is NULL, delete all of
869 extcommunity-list entry belongs to the specified name. */
870int
871extcommunity_list_unset (struct community_list_handler *ch,
paul8708b742003-06-07 02:03:11 +0000872 char *name, char *str, int direct, int style)
paul718e3742002-12-13 20:15:29 +0000873{
874 struct community_entry *entry;
875 struct community_list *list;
876 struct ecommunity *ecom = NULL;
877 regex_t *regex;
878
879 entry = NULL;
880
881 /* Lookup extcommunity list. */
882 list = community_list_lookup (ch, name, style);
883 if (list == NULL)
884 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
885
886 /* Delete all of entry belongs to this extcommunity-list. */
paul8708b742003-06-07 02:03:11 +0000887 if (!str)
paul718e3742002-12-13 20:15:29 +0000888 {
889 community_list_delete (list);
890 return 0;
891 }
892
893 /* Community list string is specified. Lookup entry from community
894 list. */
895 if (style == EXTCOMMUNITY_LIST_STANDARD || style == EXTCOMMUNITY_LIST_AUTO)
896 {
897 ecom = ecommunity_str2com (str, 0, 1);
898 if (ecom)
paul8708b742003-06-07 02:03:11 +0000899 {
900 entry = community_list_entry_lookup (list, ecom, direct);
901 ecommunity_free (ecom);
902 }
paul718e3742002-12-13 20:15:29 +0000903 else if (style == COMMUNITY_LIST_STANDARD)
paul8708b742003-06-07 02:03:11 +0000904 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
paul718e3742002-12-13 20:15:29 +0000905
906 /* If we can't convert string into community and community-list
paul8708b742003-06-07 02:03:11 +0000907 type is auto, fall dawn to expanded community-list. */
paul718e3742002-12-13 20:15:29 +0000908 }
909
910 /* Expanded community-list parse. String may include regular
911 expression. */
paul8708b742003-06-07 02:03:11 +0000912 if (!entry
paul718e3742002-12-13 20:15:29 +0000913 && (style == COMMUNITY_LIST_EXPANDED || style == COMMUNITY_LIST_AUTO))
914 {
915 regex = bgp_regcomp (str);
916 if (regex)
paul8708b742003-06-07 02:03:11 +0000917 {
918 entry = community_list_entry_lookup (list, str, direct);
919 bgp_regex_free (regex);
920 }
paul718e3742002-12-13 20:15:29 +0000921 else
paul8708b742003-06-07 02:03:11 +0000922 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
paul718e3742002-12-13 20:15:29 +0000923 }
924
paul8708b742003-06-07 02:03:11 +0000925 if (!entry)
paul718e3742002-12-13 20:15:29 +0000926 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
927
928 community_list_entry_delete (list, entry, style);
929
930 return 0;
931}
932
933/* Initializa community-list. Return community-list handler. */
934struct community_list_handler *
935community_list_init ()
936{
937 struct community_list_handler *ch;
938 ch = XCALLOC (MTYPE_COMMUNITY_LIST_HANDLER,
paul8708b742003-06-07 02:03:11 +0000939 sizeof (struct community_list_handler));
paul718e3742002-12-13 20:15:29 +0000940 return ch;
941}
942
943/* Terminate community-list. */
944void
945community_list_terminate (struct community_list_handler *ch)
946{
947 struct community_list_master *cm;
948 struct community_list *list;
949
950 cm = &ch->community_list;
951 while ((list = cm->num.head) != NULL)
952 community_list_delete (list);
953 while ((list = cm->str.head) != NULL)
954 community_list_delete (list);
955
956 cm = &ch->extcommunity_list;
957 while ((list = cm->num.head) != NULL)
958 community_list_delete (list);
959 while ((list = cm->str.head) != NULL)
960 community_list_delete (list);
961
962 XFREE (MTYPE_COMMUNITY_LIST_HANDLER, ch);
963}