diff --git a/CHANGES b/CHANGES index b7747e1..17bacd3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ 0.1.32-rc3 (2015-07-01) - feature: option -s can be used to generate sequence numbers in IOS prefix-lists + - feature: option -F can be used to generate output in user-defined + format. Only prefix-lists supported for now. 0.1.32-rc2 (2015-07-01) - bugfix: when no sources provided in command line and via IRRD_SOURCES env, diff --git a/README.md b/README.md index 8decbd8..bc2aab2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ SYNOPSIS -------- ``` - bgpq3 [-h host] [-S sources] [-EP] [-f asn | -G asn] [-2346AbDdJjpsX] [-r len] [-R len] [-m max] [-W len] OBJECTS [...] + bgpq3 [-h host] [-S sources] [-EP] [-f asn | -G asn] [-2346AbDdJjpsX] [-F fmt] [-r len] [-R len] [-m max] [-W len] OBJECTS [...] ``` DESCRIPTION @@ -57,6 +57,10 @@ route-filters (Juniper). Generate input as-path access-list for adjacent as `AS number`. +#### -F `fmt` + +Generate output in user-defined format. + #### -G `number` Generate output as-path access-list. @@ -244,6 +248,28 @@ and the result will be next: `AS196611` is no more in the list, however, `AS23456` (transition AS) would have been added to list if it were not present. +USER-DEFINED FORMAT +------------------- + +If you want to generate configuration not for routers, but for some +other programs/systems, you may use user-defined formatting, like in +example below: + + user@host:~>bgpq3 -F "ipfw add pass all from %n/%l to any\\n" as3254 + ipfw add pass all from 62.244.0.0/18 to any + ipfw add pass all from 91.219.29.0/24 to any + ipfw add pass all from 91.219.30.0/24 to any + ipfw add pass all from 193.193.192.0/19 to any + +Recognized format characters: '%n' - network, '%l' - mask length. +Recognized escape characters: '\n' - new line, '\t' - tabulation. +Please note that no new lines inserted automatically after each sentence, +you have to add them into format string manually, elsewhere output will +be in one line (sometimes it makes sense): + + user@host:~>bgpq3 -6F "%n/%l; " as-eltel + 2001:1b00::/32; 2620:4f:8000::/48; 2a04:bac0::/29; 2a05:3a80::/48; + DIAGNOSTICS ----------- diff --git a/bgpq3.c b/bgpq3.c index a379b22..f7a6c35 100644 --- a/bgpq3.c +++ b/bgpq3.c @@ -40,6 +40,7 @@ usage(int ecode) printf(" -E : generate extended access-list(Cisco) or " "route-filter(Juniper)\n"); printf(" -f number : generate input as-path access-list\n"); + printf(" -F fmt : generate output in user-defined format\n"); printf(" -G number : generate output as-path access-list\n"); printf(" -h host : host running IRRD software (whois.radb.net by " "default)\n"); @@ -77,8 +78,8 @@ exclusive() void vendor_exclusive() { - fprintf(stderr, "-b (BIRD), -J (JunOS), -j (JSON) and -X (IOS XR) options are " - "mutually exclusive\n"); + fprintf(stderr, "-b (BIRD), -F (formatted), -J (JunOS), -j (JSON) " + "and -X (IOS XR) options are mutually exclusive\n"); exit(1); }; @@ -130,7 +131,7 @@ main(int argc, char* argv[]) if (getenv("IRRD_SOURCES")) expander.sources=getenv("IRRD_SOURCES"); - while((c=getopt(argc,argv,"2346AbdDES:jJf:l:m:M:W:Ppr:R:G:Th:Xs"))!=EOF) { + while((c=getopt(argc,argv,"2346AbdDEF:S:jJf:l:m:M:W:Ppr:R:G:Th:Xs"))!=EOF) { switch(c) { case '2': expand_as23456=1; @@ -170,6 +171,10 @@ main(int argc, char* argv[]) case 'E': if(expander.generation) exclusive(); expander.generation=T_EACL; break; + case 'F': if(expander.vendor) exclusive(); + expander.vendor=V_FORMAT; + expander.format=optarg; + break; case 'h': expander.server=optarg; break; case 'J': if(expander.vendor) vendor_exclusive(); @@ -305,6 +310,14 @@ main(int argc, char* argv[]) sx_report(SX_FATAL, "Sorry, only prefix-lists and as-paths supported " "for JSON output\n"); }; + if(expander.vendor==V_FORMAT && expander.generation!=T_PREFIXLIST) + sx_report(SX_FATAL, "Sorry, only prefix-lists supported in formatted " + "output\n"); + if(expander.vendor==V_FORMAT && (refine || refineLow)) { + sx_report(SX_FATAL, "Sorry, formatted output (-F ) in not " + "compatible with -R/-r options\n"); + exit(1); + }; if(expander.asdot && expander.vendor!=V_CISCO) { sx_report(SX_FATAL,"asdot notation supported only for Cisco, " @@ -323,6 +336,12 @@ main(int argc, char* argv[]) exit(1); }; + if(aggregate && expander.vendor==V_FORMAT) { + sx_report(SX_FATAL, "Sorry, aggregation (-A) is not compatible with " + "formatted output (-F )\n"); + exit(1); + }; + if(aggregate && expander.generationf; + struct bgpq_expander* b=fpc->b; + if(n->isGlue) + return; + if(!f) + f=stdout; + memset(prefix, 0, sizeof(prefix)); + sx_prefix_snprintf_fmt(&n->prefix, prefix, sizeof(prefix), b->format); + fprintf(f, "%s", prefix); +}; + + +int +bgpq3_print_format_prefixlist(FILE* f, struct bgpq_expander* b) +{ + struct fpcbdata ff = {.f=f, .b=b}; + sx_radix_tree_foreach(b->tree,bgpq3_print_format_prefix,&ff); + if (strcmp(b->format+strlen(b->format-2), "\n")) + fprintf(f, "\n"); + return 0; +}; + int bgpq3_print_cisco_eacl(FILE* f, struct bgpq_expander* b) { @@ -642,6 +674,7 @@ bgpq3_print_prefixlist(FILE* f, struct bgpq_expander* b) case V_CISCO_XR: return bgpq3_print_ciscoxr_prefixlist(f,b); case V_JSON: return bgpq3_print_json_prefixlist(f,b); case V_BIRD: return bgpq3_print_bird_prefixlist(f,b); + case V_FORMAT: return bgpq3_print_format_prefixlist(f,b); }; return 0; }; @@ -655,6 +688,7 @@ bgpq3_print_eacl(FILE* f, struct bgpq_expander* b) case V_CISCO_XR: sx_report(SX_FATAL, "unreachable point\n"); case V_JSON: sx_report(SX_FATAL, "unreachable point\n"); case V_BIRD: sx_report(SX_FATAL, "unreachable point\n"); + case V_FORMAT: sx_report(SX_FATAL, "unreachable point\n"); }; return 0; }; diff --git a/sx_prefix.c b/sx_prefix.c index 10fd4e0..718b003 100644 --- a/sx_prefix.c +++ b/sx_prefix.c @@ -217,7 +217,7 @@ sx_prefix_range_parse(struct sx_radix_tree* tree, int af, int maxlen, }; SX_DEBUG(debug_expander, "parsed prefix-range %s as %lu-%lu\n", text, min, max); - if (max > maxlen) + if (max > maxlen) max = maxlen; sx_radix_tree_insert_specifics(tree, p, min, max); return 1; @@ -264,6 +264,52 @@ sx_prefix_snprintf(struct sx_prefix* p, char* rbuffer, int srb) return snprintf(rbuffer,srb,"%s/%i",buffer,p->masklen); }; +int +sx_prefix_snprintf_fmt(struct sx_prefix* p, char* buffer, int size, + const char* format) +{ + unsigned off=0; + const char* c=format; + while(*c) { + if(*c=='%') { + switch(*(c+1)) { + case 'r': + case 'n': + inet_ntop(p->family,&p->addr,buffer+off,size-off); + off=strlen(buffer); + break; + case 'l': + off+=snprintf(buffer+off,size-off,"%i",p->masklen); + break; + case '%': + buffer[off++]='%'; + break; + default : + sx_report(SX_ERROR, "Unknown format char '%c'\n", *(c+1)); + return 0; + }; + c+=2; + } else if (*c=='\\') { + switch(*(c+1)) { + case 'n': + buffer[off++]='\n'; break; + case 't': + buffer[off++]='\t'; break; + case '\\': + buffer[off++]='\\'; break; + default: + buffer[off++]=*(c+1); + break; + }; + c+=2; + } else { + buffer[off++]=*c; + c++; + }; + }; + return strlen(buffer); +}; + int sx_prefix_jsnprintf(struct sx_prefix* p, char* rbuffer, int srb) { diff --git a/sx_prefix.h b/sx_prefix.h index 559cf70..5701a2b 100644 --- a/sx_prefix.h +++ b/sx_prefix.h @@ -49,6 +49,8 @@ int sx_prefix_parse(struct sx_prefix* p, int af, char* text); int sx_prefix_range_parse(struct sx_radix_tree* t, int af, int ml, char* text); int sx_prefix_fprint(FILE* f, struct sx_prefix* p); int sx_prefix_snprintf(struct sx_prefix* p, char* rbuffer, int srb); +int sx_prefix_snprintf_fmt(struct sx_prefix* p, char* rbuffer, int srb, + const char* fmt); int sx_prefix_jsnprintf(struct sx_prefix* p, char* rbuffer, int srb); struct sx_radix_tree* sx_radix_tree_new(int af); struct sx_radix_node* sx_radix_node_new(struct sx_prefix* prefix);