diff --git a/CHANGES b/CHANGES index a55841f..3b313ef 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,5 @@ -untagged yet (2017-07-19) +untagged yet (2017-09-08) + - initial support for Juniper route-filter-lists (JunOS 16.2+). - too large (>124bytes) sources list was not handled correctly. Reported by Pier Carlo Chiodi. diff --git a/README.md b/README.md index 443804f..85f3f4d 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ SYNOPSIS -------- ``` - bgpq3 [-h host[:port]] [-S sources] [-EP] [-f asn | -F fmt | -G asn] [-2346ABbDdJjpsX] [-a asn] [-r len] [-R len] [-m max] [-W len] OBJECTS [...] EXCEPT OBJECTS + bgpq3 [-h host[:port]] [-S sources] [-EPz] [-f asn | -F fmt | -G asn] [-2346ABbDdJjpsX] [-a asn] [-r len] [-R len] [-m max] [-W len] OBJECTS [...] EXCEPT OBJECTS ``` DESCRIPTION @@ -149,6 +149,10 @@ Generate as-path strings of a given length maximum (0 for infinity). Generate config for Cisco IOS XR devices (plain IOS by default). +#### -z + +Generate Juniper route-filter-list (JunOS 16.2+). + #### `OBJECTS` `OBJECTS` means networks (in prefix format), autonomous systems, as-sets and diff --git a/bgpq3.8 b/bgpq3.8 index aaedc16..08bad2f 100644 --- a/bgpq3.8 +++ b/bgpq3.8 @@ -32,7 +32,7 @@ .Nm .Op Fl h Ar host[:port] .Op Fl S Ar sources -.Op Fl EP +.Op Fl EPz .Oo .Fl f Ar asn | .Fl F Ar fmt | @@ -120,6 +120,8 @@ disable pipelining. generate as-path strings of no more than len items (use 0 for inifinity). .It Fl X generate config for Cisco IOS XR devices (plain IOS by default). +.It Fl z +generate route-filter-lists (JunOS 16.2+). .It Ar OBJECTS means networks (in prefix format), autonomous systems, as-sets and route-sets. .It Ar EXCEPT OBJECTS diff --git a/bgpq3.c b/bgpq3.c index cad32b0..540d5fa 100644 --- a/bgpq3.c +++ b/bgpq3.c @@ -137,7 +137,7 @@ main(int argc, char* argv[]) if (getenv("IRRD_SOURCES")) expander.sources=getenv("IRRD_SOURCES"); - while((c=getopt(argc,argv,"2346a:AbBdDEF:S:jJf:l:L:m:M:NW:Ppr:R:G:Th:Xs")) + while((c=getopt(argc,argv,"2346a:AbBdDEF:S:jJf:l:L:m:M:NW:Ppr:R:G:Th:Xsz")) !=EOF) { switch(c) { case '2': @@ -308,6 +308,10 @@ main(int argc, char* argv[]) case 'X': if(expander.vendor) vendor_exclusive(); expander.vendor=V_CISCO_XR; break; + case 'z': + if(expander.generation) exclusive(); + expander.generation=T_ROUTE_FILTER_LIST; + break; default : usage(1); }; }; @@ -368,6 +372,10 @@ main(int argc, char* argv[]) "compatible with -R/-r options\n"); exit(1); }; + if(expander.generation==T_ROUTE_FILTER_LIST && expander.vendor!=V_JUNIPER) { + sx_report(SX_FATAL, "Route-filter-lists (-z) supported for Juniper (-J)" + " output only\n"); + }; if(expander.asdot && expander.vendor!=V_CISCO) { sx_report(SX_FATAL,"asdot notation supported only for Cisco, " @@ -381,8 +389,9 @@ main(int argc, char* argv[]) if(aggregate && expander.vendor==V_JUNIPER && expander.generation==T_PREFIXLIST) { sx_report(SX_FATAL, "Sorry, aggregation (-A) does not work in" - " Juniper prefix-lists\nYou can try route-filters (-E) instead" - " of prefix-lists (-P, default)\n"); + " Juniper prefix-lists\nYou can try route-filters (-E) " + "or route-filter-lists (-z) instead of prefix-lists " + "(-P, default)\n"); exit(1); }; @@ -447,11 +456,13 @@ main(int argc, char* argv[]) if(refine) { sx_report(SX_FATAL, "Sorry, more-specific filters (-R %u) " "is not supported for Juniper prefix-lists.\n" - "Use router-filters (-E) instead\n", refine); + "Use router-filters (-E) or route-filter-lists (-z) " + "instead\n", refine); } else { sx_report(SX_FATAL, "Sorry, more-specific filters (-r %u) " "is not supported for Juniper prefix-lists.\n" - "Use route-filters (-E) instead\n", refineLow); + "Use route-filters (-E) or route-filter-lists (-z) " + "instead\n", refineLow); }; }; @@ -568,7 +579,6 @@ main(int argc, char* argv[]) sx_radix_tree_aggregate(expander.tree); switch(expander.generation) { - default : case T_NONE: sx_report(SX_FATAL,"Unreachable point... call snar\n"); exit(1); case T_ASPATH: bgpq3_print_aspath(stdout,&expander); @@ -579,6 +589,9 @@ main(int argc, char* argv[]) break; case T_EACL: bgpq3_print_eacl(stdout,&expander); break; + case T_ROUTE_FILTER_LIST: + bgpq3_print_route_filter_list(stdout, &expander); + break; }; return 0; diff --git a/bgpq3.h b/bgpq3.h index 1104acd..a60d14c 100644 --- a/bgpq3.h +++ b/bgpq3.h @@ -26,7 +26,8 @@ typedef enum { T_ASPATH, T_OASPATH, T_PREFIXLIST, - T_EACL + T_EACL, + T_ROUTE_FILTER_LIST } bgpq_gen_t; struct bgpq_expander; @@ -82,6 +83,7 @@ int bgpq3_print_prefixlist(FILE* f, struct bgpq_expander* b); int bgpq3_print_eacl(FILE* f, struct bgpq_expander* b); int bgpq3_print_aspath(FILE* f, struct bgpq_expander* b); int bgpq3_print_oaspath(FILE* f, struct bgpq_expander* b); +int bgpq3_print_route_filter_list(FILE* f, struct bgpq_expander* b); #ifndef HAVE_STRLCPY size_t strlcpy(char* dst, const char* src, size_t size); diff --git a/bgpq3_printer.c b/bgpq3_printer.c index d470bfb..611981e 100644 --- a/bgpq3_printer.c +++ b/bgpq3_printer.c @@ -639,6 +639,8 @@ bgpq3_print_openbgpd_aspath(FILE* f, struct bgpq_expander* b) return 0; }; +static int jrfilter_prefixed=1; + void bgpq3_print_jrfilter(struct sx_radix_node* n, void* ff) { @@ -648,13 +650,17 @@ bgpq3_print_jrfilter(struct sx_radix_node* n, void* ff) if(!f) f=stdout; sx_prefix_snprintf(&n->prefix,prefix,sizeof(prefix)); if(!n->isAggregate) { - fprintf(f," route-filter %s exact;\n", prefix); + fprintf(f," %s%s exact;\n", + jrfilter_prefixed ? "route-filter " : "", prefix); } else { if(n->aggregateLow>n->prefix.masklen) { - fprintf(f," route-filter %s prefix-length-range /%u-/%u;\n", + fprintf(f," %s%s prefix-length-range /%u-/%u;\n", + jrfilter_prefixed ? "route-filter " : "", prefix,n->aggregateLow,n->aggregateHi); } else { - fprintf(f," route-filter %s upto /%u;\n", prefix,n->aggregateHi); + fprintf(f," %s%s upto /%u;\n", + jrfilter_prefixed ? "route-filter " : "", + prefix,n->aggregateHi); }; }; checkSon: @@ -852,6 +858,7 @@ bgpq3_print_juniper_routefilter(FILE* f, struct bgpq_expander* b) fprintf(f," %s;\n",b->match); }; if(!sx_radix_tree_empty(b->tree)) { + jrfilter_prefixed=1; sx_radix_tree_foreach(b->tree,bgpq3_print_jrfilter,f); } else { fprintf(f," route-filter %s/0 orlonger reject;\n", @@ -1057,3 +1064,29 @@ bgpq3_print_eacl(FILE* f, struct bgpq_expander* b) }; return 0; }; + +int +bgpq3_print_juniper_route_filter_list(FILE* f, struct bgpq_expander* b) +{ + fprintf(f, "policy-options {\nreplace:\n route-filter-list %s {\n", + b->name?b->name:"NN"); + if (sx_radix_tree_empty(b->tree)) { + fprintf(f, " route-filter %s/0 orlonger reject;\n", + b->tree->family == AF_INET ? "0.0.0.0" : "::"); + } else { + jrfilter_prefixed=0; + sx_radix_tree_foreach(b->tree,bgpq3_print_jrfilter,f); + }; + fprintf(f, " }\n}\n"); + return 0; +}; + +int +bgpq3_print_route_filter_list(FILE* f, struct bgpq_expander* b) +{ + switch(b->vendor) { + case V_JUNIPER: return bgpq3_print_juniper_route_filter_list(f,b); + default: sx_report(SX_FATAL, "unreachable point\n"); + }; + return 0; +};