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