]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_filter.c
bgpd: Validate as-path access-list before continuing regarding invalid chars
[mirror_frr.git] / bgpd / bgp_filter.c
1 /* AS path filter 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 along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <zebra.h>
22
23 #include "command.h"
24 #include "log.h"
25 #include "memory.h"
26 #include "buffer.h"
27 #include "queue.h"
28 #include "filter.h"
29
30 #include "bgpd/bgpd.h"
31 #include "bgpd/bgp_aspath.h"
32 #include "bgpd/bgp_regex.h"
33 #include "bgpd/bgp_filter.h"
34
35 /* List of AS filter list. */
36 struct as_list_list {
37 struct as_list *head;
38 struct as_list *tail;
39 };
40
41 /* AS path filter master. */
42 struct as_list_master {
43 /* List of access_list which name is number. */
44 struct as_list_list num;
45
46 /* List of access_list which name is string. */
47 struct as_list_list str;
48
49 /* Hook function which is executed when new access_list is added. */
50 void (*add_hook)(char *);
51
52 /* Hook function which is executed when access_list is deleted. */
53 void (*delete_hook)(const char *);
54 };
55
56 /* Element of AS path filter. */
57 struct as_filter {
58 struct as_filter *next;
59 struct as_filter *prev;
60
61 enum as_filter_type type;
62
63 regex_t *reg;
64 char *reg_str;
65 };
66
67 /* AS path filter list. */
68 struct as_list {
69 char *name;
70
71 enum access_type type;
72
73 struct as_list *next;
74 struct as_list *prev;
75
76 struct as_filter *head;
77 struct as_filter *tail;
78 };
79
80 /* ip as-path access-list 10 permit AS1. */
81
82 static struct as_list_master as_list_master = {{NULL, NULL},
83 {NULL, NULL},
84 NULL,
85 NULL};
86
87 /* Allocate new AS filter. */
88 static struct as_filter *as_filter_new(void)
89 {
90 return XCALLOC(MTYPE_AS_FILTER, sizeof(struct as_filter));
91 }
92
93 /* Free allocated AS filter. */
94 static void as_filter_free(struct as_filter *asfilter)
95 {
96 if (asfilter->reg)
97 bgp_regex_free(asfilter->reg);
98 if (asfilter->reg_str)
99 XFREE(MTYPE_AS_FILTER_STR, asfilter->reg_str);
100 XFREE(MTYPE_AS_FILTER, asfilter);
101 }
102
103 /* Make new AS filter. */
104 static struct as_filter *as_filter_make(regex_t *reg, const char *reg_str,
105 enum as_filter_type type)
106 {
107 struct as_filter *asfilter;
108
109 asfilter = as_filter_new();
110 asfilter->reg = reg;
111 asfilter->type = type;
112 asfilter->reg_str = XSTRDUP(MTYPE_AS_FILTER_STR, reg_str);
113
114 return asfilter;
115 }
116
117 static struct as_filter *as_filter_lookup(struct as_list *aslist,
118 const char *reg_str,
119 enum as_filter_type type)
120 {
121 struct as_filter *asfilter;
122
123 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
124 if (strcmp(reg_str, asfilter->reg_str) == 0)
125 return asfilter;
126 return NULL;
127 }
128
129 static void as_list_filter_add(struct as_list *aslist,
130 struct as_filter *asfilter)
131 {
132 asfilter->next = NULL;
133 asfilter->prev = aslist->tail;
134
135 if (aslist->tail)
136 aslist->tail->next = asfilter;
137 else
138 aslist->head = asfilter;
139 aslist->tail = asfilter;
140
141 /* Run hook function. */
142 if (as_list_master.add_hook)
143 (*as_list_master.add_hook)(aslist->name);
144 }
145
146 /* Lookup as_list from list of as_list by name. */
147 struct as_list *as_list_lookup(const char *name)
148 {
149 struct as_list *aslist;
150
151 if (name == NULL)
152 return NULL;
153
154 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
155 if (strcmp(aslist->name, name) == 0)
156 return aslist;
157
158 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
159 if (strcmp(aslist->name, name) == 0)
160 return aslist;
161
162 return NULL;
163 }
164
165 static struct as_list *as_list_new(void)
166 {
167 return XCALLOC(MTYPE_AS_LIST, sizeof(struct as_list));
168 }
169
170 static void as_list_free(struct as_list *aslist)
171 {
172 if (aslist->name) {
173 XFREE(MTYPE_AS_STR, aslist->name);
174 aslist->name = NULL;
175 }
176 XFREE(MTYPE_AS_LIST, aslist);
177 }
178
179 /* Insert new AS list to list of as_list. Each as_list is sorted by
180 the name. */
181 static struct as_list *as_list_insert(const char *name)
182 {
183 size_t i;
184 long number;
185 struct as_list *aslist;
186 struct as_list *point;
187 struct as_list_list *list;
188
189 /* Allocate new access_list and copy given name. */
190 aslist = as_list_new();
191 aslist->name = XSTRDUP(MTYPE_AS_STR, name);
192 assert(aslist->name);
193
194 /* If name is made by all digit character. We treat it as
195 number. */
196 for (number = 0, i = 0; i < strlen(name); i++) {
197 if (isdigit((int)name[i]))
198 number = (number * 10) + (name[i] - '0');
199 else
200 break;
201 }
202
203 /* In case of name is all digit character */
204 if (i == strlen(name)) {
205 aslist->type = ACCESS_TYPE_NUMBER;
206
207 /* Set access_list to number list. */
208 list = &as_list_master.num;
209
210 for (point = list->head; point; point = point->next)
211 if (atol(point->name) >= number)
212 break;
213 } else {
214 aslist->type = ACCESS_TYPE_STRING;
215
216 /* Set access_list to string list. */
217 list = &as_list_master.str;
218
219 /* Set point to insertion point. */
220 for (point = list->head; point; point = point->next)
221 if (strcmp(point->name, name) >= 0)
222 break;
223 }
224
225 /* In case of this is the first element of master. */
226 if (list->head == NULL) {
227 list->head = list->tail = aslist;
228 return aslist;
229 }
230
231 /* In case of insertion is made at the tail of access_list. */
232 if (point == NULL) {
233 aslist->prev = list->tail;
234 list->tail->next = aslist;
235 list->tail = aslist;
236 return aslist;
237 }
238
239 /* In case of insertion is made at the head of access_list. */
240 if (point == list->head) {
241 aslist->next = list->head;
242 list->head->prev = aslist;
243 list->head = aslist;
244 return aslist;
245 }
246
247 /* Insertion is made at middle of the access_list. */
248 aslist->next = point;
249 aslist->prev = point->prev;
250
251 if (point->prev)
252 point->prev->next = aslist;
253 point->prev = aslist;
254
255 return aslist;
256 }
257
258 static struct as_list *as_list_get(const char *name)
259 {
260 struct as_list *aslist;
261
262 aslist = as_list_lookup(name);
263 if (aslist == NULL)
264 aslist = as_list_insert(name);
265
266 return aslist;
267 }
268
269 static const char *filter_type_str(enum as_filter_type type)
270 {
271 switch (type) {
272 case AS_FILTER_PERMIT:
273 return "permit";
274 case AS_FILTER_DENY:
275 return "deny";
276 default:
277 return "";
278 }
279 }
280
281 static void as_list_delete(struct as_list *aslist)
282 {
283 struct as_list_list *list;
284 struct as_filter *filter, *next;
285
286 for (filter = aslist->head; filter; filter = next) {
287 next = filter->next;
288 as_filter_free(filter);
289 }
290
291 if (aslist->type == ACCESS_TYPE_NUMBER)
292 list = &as_list_master.num;
293 else
294 list = &as_list_master.str;
295
296 if (aslist->next)
297 aslist->next->prev = aslist->prev;
298 else
299 list->tail = aslist->prev;
300
301 if (aslist->prev)
302 aslist->prev->next = aslist->next;
303 else
304 list->head = aslist->next;
305
306 as_list_free(aslist);
307 }
308
309 static int as_list_empty(struct as_list *aslist)
310 {
311 if (aslist->head == NULL && aslist->tail == NULL)
312 return 1;
313 else
314 return 0;
315 }
316
317 static void as_list_filter_delete(struct as_list *aslist,
318 struct as_filter *asfilter)
319 {
320 char *name = XSTRDUP(MTYPE_AS_STR, aslist->name);
321
322 if (asfilter->next)
323 asfilter->next->prev = asfilter->prev;
324 else
325 aslist->tail = asfilter->prev;
326
327 if (asfilter->prev)
328 asfilter->prev->next = asfilter->next;
329 else
330 aslist->head = asfilter->next;
331
332 as_filter_free(asfilter);
333
334 /* If access_list becomes empty delete it from access_master. */
335 if (as_list_empty(aslist))
336 as_list_delete(aslist);
337
338 /* Run hook function. */
339 if (as_list_master.delete_hook)
340 (*as_list_master.delete_hook)(name);
341 if (name)
342 XFREE(MTYPE_AS_STR, name);
343 }
344
345 static int as_filter_match(struct as_filter *asfilter, struct aspath *aspath)
346 {
347 if (bgp_regexec(asfilter->reg, aspath) != REG_NOMATCH)
348 return 1;
349 return 0;
350 }
351
352 /* Apply AS path filter to AS. */
353 enum as_filter_type as_list_apply(struct as_list *aslist, void *object)
354 {
355 struct as_filter *asfilter;
356 struct aspath *aspath;
357
358 aspath = (struct aspath *)object;
359
360 if (aslist == NULL)
361 return AS_FILTER_DENY;
362
363 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
364 if (as_filter_match(asfilter, aspath))
365 return asfilter->type;
366 }
367 return AS_FILTER_DENY;
368 }
369
370 /* Add hook function. */
371 void as_list_add_hook(void (*func)(char *))
372 {
373 as_list_master.add_hook = func;
374 }
375
376 /* Delete hook function. */
377 void as_list_delete_hook(void (*func)(const char *))
378 {
379 as_list_master.delete_hook = func;
380 }
381
382 static int as_list_dup_check(struct as_list *aslist, struct as_filter *new)
383 {
384 struct as_filter *asfilter;
385
386 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
387 if (asfilter->type == new->type
388 && strcmp(asfilter->reg_str, new->reg_str) == 0)
389 return 1;
390 }
391 return 0;
392 }
393
394 static int config_bgp_aspath_validate(const char *regstr)
395 {
396 char valid_chars[] = "1234567890_^|[,{}() ]$*+.?-";
397
398 if (strspn(regstr, valid_chars) == strlen(regstr))
399 return 1;
400
401 return 0;
402 }
403
404 DEFUN(ip_as_path, ip_as_path_cmd,
405 "ip as-path access-list WORD <deny|permit> LINE...",
406 IP_STR
407 "BGP autonomous system path filter\n"
408 "Specify an access list name\n"
409 "Regular expression access list name\n"
410 "Specify packets to reject\n"
411 "Specify packets to forward\n"
412 "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n")
413 {
414 int idx = 0;
415 enum as_filter_type type;
416 struct as_filter *asfilter;
417 struct as_list *aslist;
418 regex_t *regex;
419 char *regstr;
420
421 /* Retrieve access list name */
422 argv_find(argv, argc, "WORD", &idx);
423 char *alname = argv[idx]->arg;
424
425 /* Check the filter type. */
426 type = argv_find(argv, argc, "deny", &idx) ? AS_FILTER_DENY
427 : AS_FILTER_PERMIT;
428
429 /* Check AS path regex. */
430 argv_find(argv, argc, "LINE", &idx);
431 regstr = argv_concat(argv, argc, idx);
432
433 regex = bgp_regcomp(regstr);
434 if (!regex) {
435 vty_out(vty, "can't compile regexp %s\n", regstr);
436 XFREE(MTYPE_TMP, regstr);
437 return CMD_WARNING_CONFIG_FAILED;
438 }
439
440 if (!config_bgp_aspath_validate(regstr)) {
441 vty_out(vty, "Invalid character in as-path access-list %s\n",
442 regstr);
443 return CMD_WARNING_CONFIG_FAILED;
444 }
445
446 asfilter = as_filter_make(regex, regstr, type);
447
448 XFREE(MTYPE_TMP, regstr);
449
450 /* Install new filter to the access_list. */
451 aslist = as_list_get(alname);
452
453 /* Duplicate insertion check. */;
454 if (as_list_dup_check(aslist, asfilter))
455 as_filter_free(asfilter);
456 else
457 as_list_filter_add(aslist, asfilter);
458
459 return CMD_SUCCESS;
460 }
461
462 DEFUN(no_ip_as_path, no_ip_as_path_cmd,
463 "no ip as-path access-list WORD <deny|permit> LINE...",
464 NO_STR IP_STR
465 "BGP autonomous system path filter\n"
466 "Specify an access list name\n"
467 "Regular expression access list name\n"
468 "Specify packets to reject\n"
469 "Specify packets to forward\n"
470 "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n")
471 {
472 int idx = 0;
473 enum as_filter_type type;
474 struct as_filter *asfilter;
475 struct as_list *aslist;
476 char *regstr;
477 regex_t *regex;
478
479 char *aslistname =
480 argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL;
481
482 /* Lookup AS list from AS path list. */
483 aslist = as_list_lookup(aslistname);
484 if (aslist == NULL) {
485 vty_out(vty, "ip as-path access-list %s doesn't exist\n",
486 aslistname);
487 return CMD_WARNING_CONFIG_FAILED;
488 }
489
490 /* Check the filter type. */
491 if (argv_find(argv, argc, "permit", &idx))
492 type = AS_FILTER_PERMIT;
493 else if (argv_find(argv, argc, "deny", &idx))
494 type = AS_FILTER_DENY;
495 else {
496 vty_out(vty, "filter type must be [permit|deny]\n");
497 return CMD_WARNING_CONFIG_FAILED;
498 }
499
500 /* Compile AS path. */
501 argv_find(argv, argc, "LINE", &idx);
502 regstr = argv_concat(argv, argc, idx);
503
504 if (!config_bgp_aspath_validate(regstr)) {
505 vty_out(vty, "Invalid character in as-path access-list %s\n",
506 regstr);
507 return CMD_WARNING_CONFIG_FAILED;
508 }
509
510 regex = bgp_regcomp(regstr);
511 if (!regex) {
512 vty_out(vty, "can't compile regexp %s\n", regstr);
513 XFREE(MTYPE_TMP, regstr);
514 return CMD_WARNING_CONFIG_FAILED;
515 }
516
517 /* Lookup asfilter. */
518 asfilter = as_filter_lookup(aslist, regstr, type);
519
520 XFREE(MTYPE_TMP, regstr);
521 bgp_regex_free(regex);
522
523 if (asfilter == NULL) {
524 vty_out(vty, "\n");
525 return CMD_WARNING_CONFIG_FAILED;
526 }
527
528 as_list_filter_delete(aslist, asfilter);
529
530 return CMD_SUCCESS;
531 }
532
533 DEFUN (no_ip_as_path_all,
534 no_ip_as_path_all_cmd,
535 "no ip as-path access-list WORD",
536 NO_STR
537 IP_STR
538 "BGP autonomous system path filter\n"
539 "Specify an access list name\n"
540 "Regular expression access list name\n")
541 {
542 int idx_word = 4;
543 struct as_list *aslist;
544
545 aslist = as_list_lookup(argv[idx_word]->arg);
546 if (aslist == NULL) {
547 vty_out(vty, "ip as-path access-list %s doesn't exist\n",
548 argv[idx_word]->arg);
549 return CMD_WARNING_CONFIG_FAILED;
550 }
551
552 as_list_delete(aslist);
553
554 /* Run hook function. */
555 if (as_list_master.delete_hook)
556 (*as_list_master.delete_hook)(argv[idx_word]->arg);
557
558 return CMD_SUCCESS;
559 }
560
561 static void as_list_show(struct vty *vty, struct as_list *aslist)
562 {
563 struct as_filter *asfilter;
564
565 vty_out(vty, "AS path access list %s\n", aslist->name);
566
567 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
568 vty_out(vty, " %s %s\n", filter_type_str(asfilter->type),
569 asfilter->reg_str);
570 }
571 }
572
573 static void as_list_show_all(struct vty *vty)
574 {
575 struct as_list *aslist;
576 struct as_filter *asfilter;
577
578 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) {
579 vty_out(vty, "AS path access list %s\n", aslist->name);
580
581 for (asfilter = aslist->head; asfilter;
582 asfilter = asfilter->next) {
583 vty_out(vty, " %s %s\n",
584 filter_type_str(asfilter->type),
585 asfilter->reg_str);
586 }
587 }
588
589 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) {
590 vty_out(vty, "AS path access list %s\n", aslist->name);
591
592 for (asfilter = aslist->head; asfilter;
593 asfilter = asfilter->next) {
594 vty_out(vty, " %s %s\n",
595 filter_type_str(asfilter->type),
596 asfilter->reg_str);
597 }
598 }
599 }
600
601 DEFUN (show_ip_as_path_access_list,
602 show_ip_as_path_access_list_cmd,
603 "show ip as-path-access-list WORD",
604 SHOW_STR
605 IP_STR
606 "List AS path access lists\n"
607 "AS path access list name\n")
608 {
609 int idx_word = 3;
610 struct as_list *aslist;
611
612 aslist = as_list_lookup(argv[idx_word]->arg);
613 if (aslist)
614 as_list_show(vty, aslist);
615
616 return CMD_SUCCESS;
617 }
618
619 DEFUN (show_ip_as_path_access_list_all,
620 show_ip_as_path_access_list_all_cmd,
621 "show ip as-path-access-list",
622 SHOW_STR
623 IP_STR
624 "List AS path access lists\n")
625 {
626 as_list_show_all(vty);
627 return CMD_SUCCESS;
628 }
629
630 static int config_write_as_list(struct vty *vty)
631 {
632 struct as_list *aslist;
633 struct as_filter *asfilter;
634 int write = 0;
635
636 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
637 for (asfilter = aslist->head; asfilter;
638 asfilter = asfilter->next) {
639 vty_out(vty, "ip as-path access-list %s %s %s\n",
640 aslist->name, filter_type_str(asfilter->type),
641 asfilter->reg_str);
642 write++;
643 }
644
645 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
646 for (asfilter = aslist->head; asfilter;
647 asfilter = asfilter->next) {
648 vty_out(vty, "ip as-path access-list %s %s %s\n",
649 aslist->name, filter_type_str(asfilter->type),
650 asfilter->reg_str);
651 write++;
652 }
653 return write;
654 }
655
656 static struct cmd_node as_list_node = {AS_LIST_NODE, "", 1};
657
658 /* Register functions. */
659 void bgp_filter_init(void)
660 {
661 install_node(&as_list_node, config_write_as_list);
662
663 install_element(CONFIG_NODE, &ip_as_path_cmd);
664 install_element(CONFIG_NODE, &no_ip_as_path_cmd);
665 install_element(CONFIG_NODE, &no_ip_as_path_all_cmd);
666
667 install_element(VIEW_NODE, &show_ip_as_path_access_list_cmd);
668 install_element(VIEW_NODE, &show_ip_as_path_access_list_all_cmd);
669 }
670
671 void bgp_filter_reset(void)
672 {
673 struct as_list *aslist;
674 struct as_list *next;
675
676 for (aslist = as_list_master.num.head; aslist; aslist = next) {
677 next = aslist->next;
678 as_list_delete(aslist);
679 }
680
681 for (aslist = as_list_master.str.head; aslist; aslist = next) {
682 next = aslist->next;
683 as_list_delete(aslist);
684 }
685
686 assert(as_list_master.num.head == NULL);
687 assert(as_list_master.num.tail == NULL);
688
689 assert(as_list_master.str.head == NULL);
690 assert(as_list_master.str.tail == NULL);
691 }