├── db ├── CVS │ ├── Repository │ └── Entries └── CVSROOT │ └── CVS │ ├── Entries │ ├── Repository │ └── Root ├── .gitignore ├── inc ├── helpers.inc ├── status.inc ├── ipv6.inc ├── utils.inc ├── exclusions.inc ├── cvs.inc ├── aggregate.inc ├── irrquery.inc └── pfxlist.inc ├── conf ├── nag.msg ├── irrdb.conf.dist ├── nag.conf.dist ├── exclusions.conf.dist └── irrpt.conf.dist ├── LICENSE ├── TODO ├── bin ├── irrpt_nag ├── irrpt_list_ases ├── irrpt_list_prefixes ├── irrpt_pfxgen └── irrpt_fetch ├── configure.php ├── examples └── junoscript.pl └── README.md /db/CVS/Repository: -------------------------------------------------------------------------------- 1 | . 2 | -------------------------------------------------------------------------------- /db/CVSROOT/CVS/Entries: -------------------------------------------------------------------------------- 1 | D 2 | -------------------------------------------------------------------------------- /db/CVS/Entries: -------------------------------------------------------------------------------- 1 | D/CVSROOT//// 2 | -------------------------------------------------------------------------------- /db/CVSROOT/CVS/Repository: -------------------------------------------------------------------------------- 1 | CVSROOT 2 | -------------------------------------------------------------------------------- /db/CVSROOT/CVS/Root: -------------------------------------------------------------------------------- 1 | /usr/local/irrpt/CVS 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # GIT ignore certain files 2 | # 3 | /conf/exclusions.conf 4 | /conf/irr??.conf 5 | /conf/nag.conf 6 | 7 | /db/* 8 | 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /inc/helpers.inc: -------------------------------------------------------------------------------- 1 | aggregate($input); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /conf/nag.msg: -------------------------------------------------------------------------------- 1 | Hello, 2 | 3 | This is an automated e-mail message requesting a prefix list modification 4 | from providers who still build their prefix filters manually, rather than 5 | from the Internet Routing Registry (IRR) databases. 6 | 7 | A list of requested changes is contained below, followed by a complete 8 | list of prefixes for your reference. Please update the prefix-lists for 9 | all BGP sessions with our ASN. Also please note that the prefixes are 10 | pre-aggregated, and should always be applied with at least "upto /24" 11 | (Juniper) or "le 24" (Cisco). 12 | 13 | Thank you for your time. 14 | -------------------------------------------------------------------------------- /conf/irrdb.conf.dist: -------------------------------------------------------------------------------- 1 | # 2 | # CONF: irrdb.conf.dist 3 | # 4 | # NOTE: Formatting of this file can use spaces or tabs. 5 | # If update eamil is set to "none" or is blank, no email will be sent. 6 | # cvs must be tracking for email to work and send diffs 7 | # 8 | # ex: irrpt_fetch - will pull the IRR object associated in this file 9 | # 10 | # 11 | # 12 | # Example: 13 | # ASN IRR Object (AS-SET/AUT-NUM) Update Email 14 | 15 | 42 AS-PCH noc@example-customer-a.com 16 | 6939 AS-HURRICANE none 17 | 11670 AS-TORIX peering@peering-ixp.com 18 | 393424 AS393424 upstream-notify@example-customer-b.com 19 | -------------------------------------------------------------------------------- /conf/nag.conf.dist: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /conf/exclusions.conf.dist: -------------------------------------------------------------------------------- 1 | # This file contains a list of prefixes which should not be registered, 2 | # in whole or in part. 3 | # 4 | # Remember, if you put unallocated space (bogons) in this list, it is your 5 | # responsibility to keep the list up to date as new blocks are released by 6 | # IANA. A good source for the current list is: 7 | # 8 | # https://team-cymru.com/community-services/bogon-reference/bogon-reference-http/ 9 | # 10 | # Examples: 11 | # 12 | # 0.0.0.0/8 13 | # 2.56.0.0/14 14 | # 5.45.32.0/20 15 | # .. 16 | # ::/8 17 | # 100::/8 18 | # 200::/7 19 | # .. 20 | 0.0.0.0/8 21 | 10.0.0.0/8 22 | 100.64.0.0/10 23 | 127.0.0.0/8 24 | 169.254.0.0/16 25 | 172.16.0.0/12 26 | 192.0.0.0/24 27 | 192.0.2.0/24 28 | 192.168.0.0/16 29 | 198.18.0.0/15 30 | 198.51.100.0/24 31 | 203.0.113.0/24 32 | 224.0.0.0/3 33 | 34 | # IPv6 35 | ::/128 36 | ::1/128 37 | ::ffff:0:0/96 38 | ::/96 39 | 100::/64 40 | 2001::/40 41 | 2001:10::/28 42 | 2001:db8::/32 43 | 2002::/16 44 | fc00::/7 45 | fe80::/10 46 | fec0::/10 47 | ff00::/8 48 | -------------------------------------------------------------------------------- /inc/status.inc: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2004-2006, Richard A. Steenbergen 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 16 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 17 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 19 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /inc/ipv6.inc: -------------------------------------------------------------------------------- 1 | 2602:fde5:0000:0000:0000:0000:0000:0000 */ 29 | function _v6_expand($ip) 30 | { 31 | $hex = unpack("H*hex", inet_pton($ip)); 32 | $ip = substr(preg_replace("/([A-f0-9]{4})/", "$1:", $hex['hex']), 0, -1); 33 | return $ip; 34 | } 35 | 36 | /* compress v6 notation and all its zeros - 2602:fde5:0000:0000:0000:0000:0000:0000 => 2602:fde5:: */ 37 | function _v6_compress($ip) 38 | { 39 | $ip = inet_ntop(inet_pton($ip)); 40 | return $ip; 41 | } 42 | 43 | /* return network portion of the prefix - 2602:fde5:1::90A/36 => 2602:fde5:0000:0000:0000:0000:0000:0000 */ 44 | function _v6_to_network($pfx, $mask) 45 | { 46 | /* convert v6 address into bin form */ 47 | $pfx_bin = inet_pton( $pfx ); 48 | 49 | /* do the same with the provided mask */ 50 | $mask_bin = inet_pton( _v6_mask_int2hex($mask)); 51 | 52 | /* apply mask to prefix and reverse the binary conversion to a printable string to get the network portion*/ 53 | $network = inet_ntop( pack( "A16", $pfx_bin & $mask_bin ) ); 54 | 55 | return _v6_expand($network); 56 | } 57 | 58 | ?> 59 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Project Goals: IRR PowerTools 3.0 2 | --------------------------------- 3 | 4 | * Develop a web-based frontend for IRRPT user operation. Features include 5 | configuration management, a work queue for changes which need to be 6 | processed, an integrated route history viewer, and integrated AS-SET 7 | explorer and manual IRR query tools. 8 | 9 | * Move to an all SQL backend for route storage and configuration, in 10 | order to scale and reduce dependancy on external packages. 11 | 12 | * Add tracking of the version of the prefix-list which has been deployed 13 | to routers, sent to upstream providers via Nags, etc. 14 | 15 | * More tightly integrate prefix list deployment code into the package. 16 | This is tricky, since you need to query a db to know which devices a 17 | particular customer has sessions on. We don't want to get into the 18 | business of being a complete customer database, and we expect that 19 | most serious users will already have (or should have) an existing BGP 20 | customer and BGP session database which will probably not easily speak 21 | to our code. This needs more work. 22 | 23 | * Optimize code to reasonably support the automated and frequent 24 | (e.g. hourly) polling and deployment of hundreds or thousands of prefix 25 | lists. Optimize configuration deployment to pack multiple customers and 26 | BGP session updates onto a single router update. 27 | 28 | * Add support for automatic updating of bogon list from Cymru. 29 | 30 | * Add per-customer AS-SET member exclusion capability, for handling folks 31 | who have customers with broken records and who don't know how to fix it. 32 | 33 | * Add object-specific configuration options which can be applied in place 34 | of or in addition to the global configurations. Per-object options 35 | include the whois server to query, bogon filters, e-mail settings, 36 | etc. 37 | 38 | * Add support for parallel WHOIS queries to improve transaction speed. 39 | 40 | * Add checks to spot common PHP configuration issues which are not 41 | appropriate for this tool (e.g. safe_mode, limited memory use, limited 42 | execution time, etc). 43 | -------------------------------------------------------------------------------- /conf/irrpt.conf.dist: -------------------------------------------------------------------------------- 1 | 61 | -------------------------------------------------------------------------------- /inc/utils.inc: -------------------------------------------------------------------------------- 1 | 91 | -------------------------------------------------------------------------------- /inc/exclusions.inc: -------------------------------------------------------------------------------- 1 | 88 | -------------------------------------------------------------------------------- /inc/cvs.inc: -------------------------------------------------------------------------------- 1 | cvsroot = $cvsroot; 10 | putenv("CVSROOT={$cvsroot}"); 11 | } 12 | 13 | function get_diff($file, $rev) 14 | { 15 | global $cfg; 16 | 17 | $results = ""; 18 | 19 | if (!file_exists($cfg['tools']['cvs']) || !is_executable($cfg['tools']['cvs'])) 20 | return FALSE; 21 | 22 | if (!file_exists($file) || !is_readable($file)) 23 | return FALSE; 24 | 25 | $cmd = "{$cfg['tools']['cvs']} diff {$cfg['diff']['params']} "; 26 | $cmd .= "-r {$rev['old']} -r {$rev['new']} {$file}"; 27 | 28 | if (!($cvscmd = popen($cmd, "r"))) 29 | return FALSE; 30 | 31 | $format = strtolower($cfg['diff']['output_format']); 32 | 33 | while (!feof($cvscmd)) { 34 | $line = fgets($cvscmd, 256); 35 | $line = str_replace($cfg['paths']['base'], "", $line); 36 | 37 | if ($format != "fulldiff") { 38 | $pfx = substr($line, 0, 1); 39 | 40 | if (($pfx != "+") && ($pfx != "-")) 41 | continue; 42 | } 43 | 44 | if ($format == "english") { 45 | if (substr($line, 0, 3) == "+++") { 46 | $line = str_replace("+++", "New Revision", $line); 47 | } else if (substr($line, 0, 3) == "---") { 48 | $line = str_replace("---", "Old Revision", $line); 49 | } else { 50 | $line = str_replace("+", "Add ", $line); 51 | $line = str_replace("-", "Remove ", $line); 52 | } 53 | } 54 | 55 | $results .= $line; 56 | } 57 | 58 | fclose($cvscmd); 59 | 60 | return $results; 61 | } 62 | 63 | function get_complete($file) 64 | { 65 | $results = ""; 66 | 67 | if (!($routes = @fopen($file, "r"))) 68 | return FALSE; 69 | 70 | while (!feof($routes)) { 71 | $results .= fgets($routes, 256); 72 | } 73 | 74 | fclose($routes); 75 | 76 | return $results; 77 | } 78 | 79 | function update($file) 80 | { 81 | global $cfg; 82 | 83 | if (!file_exists($cfg['tools']['cvs']) || !is_executable($cfg['tools']['cvs'])) 84 | return FALSE; 85 | 86 | chdir($cfg['paths']['db']); 87 | 88 | /* CVS add */ 89 | if (!($results = popen("{$cfg['tools']['cvs']} add ${file} 2>&1", "r"))) 90 | return FALSE; 91 | 92 | while (!feof($results)) { 93 | $line = rtrim(fgets($results, 256)); 94 | 95 | if (strstr($line, "already exists")) { 96 | $tmp = explode(" ", $line); 97 | $rev['old'] = $tmp[8]; 98 | $rev['new'] = $tmp[8]; 99 | } 100 | } 101 | 102 | pclose($results); 103 | 104 | 105 | 106 | /* CVS commit */ 107 | if (!($results = popen("{$cfg['tools']['cvs']} commit -m update {$file}", "r"))) 108 | return FALSE; 109 | 110 | while (!feof($results)) { 111 | $line = rtrim(fgets($results, 256)); 112 | 113 | if (strstr($line, "initial revision")) { 114 | $tmp = explode(":", $line); 115 | $rev['old'] = "0.00"; 116 | $rev['new'] = trim($tmp[1]); 117 | 118 | return $rev; 119 | } 120 | 121 | if (strstr($line, "new revision")) { 122 | $tmp = explode(":", $line); 123 | $rev['old'] = trim($tmp[2]); 124 | $tmp = explode(";", $tmp[1]); 125 | $rev['new'] = trim($tmp[0]); 126 | 127 | return $rev; 128 | } 129 | } 130 | 131 | pclose($results); 132 | 133 | return $rev; 134 | } 135 | } 136 | 137 | ?> 138 | -------------------------------------------------------------------------------- /bin/irrpt_nag: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | init($cfg['paths']['cvsroot']); 16 | 17 | function usage($progname) 18 | { 19 | printf("Usage: %s [-hp] [-c config] [-m message] \n\n", $progname); 20 | printf("Options:\n"); 21 | printf(" -p Preview mode (for diagnostic use). Print results to screen instead of\n"); 22 | printf(" e-mail.\n"); 23 | exit(1); 24 | } 25 | 26 | /* Parse through the cmdline options. */ 27 | for ($offset = 1; $offset < $_SERVER['argc']; $offset++) { 28 | if (substr($_SERVER['argv'][$offset], 0, 1) != "-") 29 | break; 30 | 31 | switch($_SERVER['argv'][$offset]) { 32 | case "-h": 33 | case "--help": 34 | usage($_SERVER['argv'][0]); 35 | 36 | case "-p": 37 | case "--preview": 38 | $o_preview = 1; 39 | break; 40 | 41 | case "-c": 42 | case "--config": 43 | $o_cfgfile = $_SERVER['argv'][++$offset]; 44 | break; 45 | 46 | case "-m": 47 | case "--msgfile": 48 | $o_msgfile = $_SERVER['argv'][++$offset]; 49 | break; 50 | } 51 | } 52 | 53 | /* Check for minimum number of args after cmdline */ 54 | if (($_SERVER['argc'] - $offset) < 2) 55 | usage($_SERVER['argv'][0]); 56 | 57 | 58 | $rev['old'] = $_SERVER['argv'][$offset+0]; 59 | $rev['new'] = $_SERVER['argv'][$offset+1]; 60 | 61 | 62 | /* Import the selected configuration file */ 63 | if (file_exists($o_cfgfile) === FALSE || is_readable($o_cfgfile) === FALSE) { 64 | status(STATUS_ERROR, "Can not open selected configuration file, aborting."); 65 | exit(1); 66 | } 67 | 68 | require($o_cfgfile); 69 | 70 | /* Open message file, read in data */ 71 | if (($msgfile = @fopen($o_msgfile, "r")) === FALSE) { 72 | status(STATUS_ERROR, "Unable to open nag message file {$o_msgfile}, aborting.\n"); 73 | exit(1); 74 | } 75 | 76 | while (!feof($msgfile)) { 77 | $msg .= fgets($msgfile, 256); 78 | } 79 | 80 | fclose($msgfile); 81 | 82 | $asnfile = $cfg['paths']['db'] . $local['asnrecord']; 83 | 84 | $headers = "From: {$local['from']}\n"; 85 | $headers .= "Reply-To: {$local['reply-to']}\n"; 86 | $headers .= "Date: " . date("r") . "\n"; 87 | 88 | for($i=0;$i < sizeof($provider); $i++) 89 | { 90 | $content = $msg; 91 | 92 | $content .= "\n"; 93 | $content .= "Requesting Company.......................... {$local['company']}\n"; 94 | $content .= "Requesting Autonomous System Number (ASN)... {$local['asn']}\n"; 95 | $content .= "Customer Identifying Information............ {$provider[$i]['custid']}\n"; 96 | $content .= "Routine / Successful Completion Notices..... {$local['email_routine']}\n"; 97 | $content .= "Failures, Problems, Questions, Etc.......... {$local['email_failure']}\n"; 98 | 99 | if (isset($provider[$i]['notes'])) 100 | $content .= "Other Notes: {$provider[$i]['notes']}\n"; 101 | 102 | $content .= "\n"; 103 | $content .= "Changes to the prefix list:\n\n"; 104 | $content .= $cvs->get_diff($asnfile, $rev); 105 | $content .= "\n\n"; 106 | $content .= "Complete prefix list:\n"; 107 | $content .= $cvs->get_complete($asnfile); 108 | 109 | if ($o_preview) { 110 | printf("IRRPT Preview Mode:\n"); 111 | printf(" E-mail Recepient: %s\n", $provider[$i]['email']); 112 | printf(" E-mail Headers:\n%s\n", $headers); 113 | printf(" E-mail Body:\n%s\n", $content); 114 | printf(" E-mail not sent.\n"); 115 | } else { 116 | mail($provider[$i]['email'], $local['subject'], $content, $headers); 117 | 118 | if (isset($local['localcopy'])) 119 | mail($local['localcopy'], $local['subject'], $content, $headers); 120 | } 121 | } 122 | 123 | ?> 124 | -------------------------------------------------------------------------------- /bin/irrpt_list_ases: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 0) 25 | { 26 | 27 | // the v4 switch is enabled, only pring ASes in the set with registered v4 prefixes 28 | if( $o_4 == 1 && $o_6 == 0 ) 29 | { 30 | $resolve_v4_result = resolve_v4($irr, $object); 31 | list($routes4, $asnlist4) = $resolve_v4_result; 32 | 33 | if (empty($routes4) == FALSE && sizeof($routes4) >= 1 ) 34 | { 35 | printf("%s%s\n", str_repeat(" ", $depth), strtoupper($object)); 36 | } 37 | } 38 | 39 | // the v6 switch is enabled, only pring ASes in the set with registered v6 prefixes 40 | elseif( $o_6 == 1 && $o_4 == 0 ) 41 | { 42 | $resolve_v6_result = resolve_v6($irr, $object); 43 | list($routes6, $asnlist6) = $resolve_v6_result; 44 | 45 | if (empty($routes6) == FALSE && sizeof($routes6) >= 1 ) 46 | { 47 | printf("%s%s\n", str_repeat(" ", $depth), strtoupper($object)); 48 | } 49 | } 50 | 51 | // if no switch is enabled, print the entire set 52 | // unless it's the initial set, or we would print it double 53 | else { 54 | printf("%s%s\n", str_repeat(" ", $depth), strtoupper($object)); 55 | } 56 | } 57 | 58 | // expand object 59 | //print "OBJECT: $object\n"; 60 | if (preg_match("/AS-./", $object)) { 61 | printf("%s%s", str_repeat(" ", $depth), strtoupper($object)); 62 | 63 | if (!isset($done[strval($object)])) { 64 | $done[strval($object)] = true; 65 | 66 | if (!($members = $irr->get_members_by_set($object, FALSE))) { 67 | printf(" (error)\n"); 68 | return FALSE; 69 | } 70 | 71 | printf("\n%s%s\n", str_repeat(" ", $depth), "{"); 72 | 73 | for($i=0;$i\n", $_SERVER['argv'][0]); 99 | exit(1); 100 | 101 | case "-4": 102 | case "--4": 103 | $o_4 = 1; 104 | break; 105 | 106 | case "-6": 107 | case "--6": 108 | $o_6 = 1; 109 | break; 110 | } 111 | } 112 | 113 | if (($_SERVER['argc'] - $offset) < 1) { 114 | printf("Usage: %s [-h46] \n", $_SERVER['argv'][0]); 115 | exit(1); 116 | } 117 | 118 | if (preg_match("/^AS./i", $_SERVER['argv'][$offset+0])) { 119 | $object = strtoupper($_SERVER['argv'][$offset+0]); 120 | } 121 | elseif ( (int)$_SERVER['argv'][$offset+0] > 0 && (int) $_SERVER['argv'][$offset+0] <= 4294967295) { 122 | $object = "AS" . $_SERVER['argv'][$offset+0]; 123 | } 124 | else { 125 | status(STATUS_ERROR, "Invalid AS or AS-SET input, aborting."); 126 | exit(-1); 127 | } 128 | 129 | /* Establish a connection with our IRR Query whois server */ 130 | if ($irr->connect($cfg['fetch']['host'], $cfg['fetch']['port']) == FALSE) { 131 | status(STATUS_ERROR, "Unable to connect to IRR Query whois server, aborting."); 132 | exit(-1); 133 | } 134 | 135 | /* If the user doesn't want to query all IRR sources, set the new sources now */ 136 | if ($cfg['fetch']['sources'] != "all") 137 | $irr->set_sources($cfg['fetch']['sources']); 138 | 139 | explorer($object, 0, $o_4, $o_6); 140 | 141 | ?> 142 | -------------------------------------------------------------------------------- /bin/irrpt_list_prefixes: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | \n", $_SERVER['argv'][0]); 28 | exit(1); 29 | 30 | case "-v": 31 | case "--verbose": 32 | $o_verbose = 1; 33 | break; 34 | 35 | case "-a": 36 | case "--aggregate": 37 | $o_aggregate = 1; 38 | break; 39 | 40 | case "-4": 41 | case "--4": 42 | $o_4 = 1; 43 | break; 44 | 45 | case "-6": 46 | case "--6": 47 | $o_6 = 1; 48 | break; 49 | } 50 | } 51 | 52 | if (($_SERVER['argc'] - $offset) < 1) { 53 | printf("Usage: %s [-h46va] \n", $_SERVER['argv'][0]); 54 | exit(1); 55 | } 56 | 57 | // check AS parameter if provided 58 | if (isset($_SERVER['argv'][$offset+0])) 59 | { 60 | if (preg_match("/^AS./i", $_SERVER['argv'][$offset+0])) { 61 | $object = strtoupper($_SERVER['argv'][$offset+0]); 62 | } 63 | elseif ( is_numeric($_SERVER['argv'][$offset+0]) && (int)$_SERVER['argv'][$offset+0] > 0 && (int)$_SERVER['argv'][$offset+0] <= 4294967295) { 64 | $object = "AS" . $_SERVER['argv'][$offset+0]; 65 | } 66 | else { 67 | status(STATUS_ERROR, "Invalid AS or AS-SET input, aborting."); 68 | exit(-1); 69 | } 70 | } 71 | 72 | /* Establish a connection with our IRR Query whois server */ 73 | if ($irr->connect($cfg['fetch']['host'], $cfg['fetch']['port']) == FALSE) { 74 | status(STATUS_ERROR, "Unable to connect to IRR Query whois server, aborting."); 75 | exit(-1); 76 | } 77 | 78 | /* Optionally enable a local cache of prefixes per aut-num record */ 79 | if ($cfg['fetch']['cache']) { 80 | $irr->cache_set(TRUE); 81 | } 82 | 83 | /* If the user doesn't want to query all IRR sources, set the new sources now */ 84 | if ($cfg['fetch']['sources'] != "all") 85 | $irr->set_sources($cfg['fetch']['sources']); 86 | 87 | /* Figure out if we have an AUT-NUM or an AS-SET, and resolve it */ 88 | status(STATUS_NOTICE, "Querying IRR Object {$object}"); 89 | 90 | $routes = array(); 91 | $routes4 = array(); 92 | $routes6 = array(); 93 | 94 | // FETCH: only v4 95 | if( $o_4 == 1 && $o_6 == 0 ) 96 | { 97 | $resolve_v4_result = resolve_v4($irr, $object); 98 | list($routes4, $asnlist4) = $resolve_v4_result; 99 | } 100 | // FETCH: only v6 101 | elseif( $o_6 == 1 && $o_4 == 0 ) 102 | { 103 | $resolve_v6_result = resolve_v6($irr, $object); 104 | list($routes6, $asnlist6) = $resolve_v6_result; 105 | } 106 | // FETCH: both v4/v6 107 | else 108 | { 109 | $resolve_v4_result = resolve_v4($irr, $object); 110 | list($routes4, $asnlist4) = $resolve_v4_result; 111 | $resolve_v6_result = resolve_v6($irr, $object); 112 | list($routes6, $asnlist6) = $resolve_v6_result; 113 | } 114 | 115 | // CHECK: if either result set came back with nothing 116 | if (empty($routes4)) { $routes4 = array(); } 117 | if (empty($routes6)) { $routes6 = array(); } 118 | 119 | 120 | $routes = array_merge($routes4, $routes6); 121 | 122 | if (!$o_aggregate) { 123 | for($i=0;$i 154 | -------------------------------------------------------------------------------- /bin/irrpt_pfxgen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | \n", $progname); 31 | printf(" pfxstr - The prefix-list name format string (default: %s)\n", $o_pfxstr); 32 | printf(" pfxstr_v6 - The prefix-list name format string (default: %s)\n", $o_pfxstr_v6); 33 | printf(" pfxlen - The max length more-specific that will be allowed (default: %s)\n", $o_pfxlen); 34 | printf(" pfxlen_v6 - The max length more-specific that will be allowed for v6 (default: %s)\n", $o_pfxlen_v6); 35 | printf(" format - The output format for a specific router type (default: %s)\n", $o_format); 36 | printf(" Currently supported values are:\n"); 37 | printf(" cisco\n"); 38 | printf(" ciscoxr\n"); 39 | printf(" extreme\n"); 40 | printf(" foundry\n"); 41 | printf(" force10\n"); 42 | printf(" juniper\n"); 43 | printf(" openbgpd\n"); 44 | printf(" edgeos\n"); 45 | printf(" huawei\n"); 46 | exit(1); 47 | } 48 | 49 | 50 | /* Parse through the cmdline options. */ 51 | for ($offset = 1; $offset < $_SERVER['argc']; $offset++) { 52 | if (substr($_SERVER['argv'][$offset], 0, 1) != "-") 53 | break; 54 | 55 | switch($_SERVER['argv'][$offset]) { 56 | case "-h": 57 | case "--help": 58 | usage($_SERVER['argv'][0]); 59 | 60 | case "-p": 61 | case "--pfxstr": 62 | $o_pfxstr = $_SERVER['argv'][++$offset]; 63 | break; 64 | 65 | case "-p6": 66 | case "--pfxstr_v6": 67 | $o_pfxstr_v6 = $_SERVER['argv'][++$offset]; 68 | break; 69 | 70 | case "-l": 71 | case "--pfxlen": 72 | $o_pfxlen = $_SERVER['argv'][++$offset]; 73 | break; 74 | 75 | case "-l6": 76 | case "--pfxlen_v6": 77 | $o_pfxlen_v6 = $_SERVER['argv'][++$offset]; 78 | break; 79 | 80 | case "-4": 81 | case "--4": 82 | $o_4 = 1; 83 | break; 84 | 85 | case "-6": 86 | case "--6": 87 | $o_6 = 1; 88 | break; 89 | 90 | case "-f": 91 | case "--format": 92 | switch (strtolower($_SERVER['argv'][++$offset])) { 93 | case "cisco": 94 | case "csco": 95 | case "foundry": 96 | case "fdry": 97 | $o_format = "cisco"; 98 | break; 99 | 100 | case "ciscoxr": 101 | case "iosxr": 102 | $o_format = "iosxr"; 103 | break; 104 | 105 | case "juniper": 106 | case "junos": 107 | $o_format = "juniper"; 108 | break; 109 | 110 | case "extreme": 111 | case "extr": 112 | $o_format = "extreme"; 113 | break; 114 | 115 | case "force10": 116 | case "f10": 117 | $o_format = "force10"; 118 | break; 119 | 120 | case "openbgpd": 121 | case "obgpd": 122 | $o_format = "openbgpd"; 123 | break; 124 | 125 | case "edgeos": 126 | $o_format = "edgeos"; 127 | break; 128 | 129 | case "huawei": 130 | $o_format = "huawei"; 131 | break; 132 | 133 | default: 134 | printf("Unknown format, aborting.\n"); 135 | exit(1); 136 | } // end: switch offset format 137 | break; 138 | } // end: switch argv offset 139 | } 140 | 141 | /* Check for minimum number of args after cmdline */ 142 | if (($_SERVER['argc'] - $offset) < 1) 143 | usage($_SERVER['argv'][0]); 144 | 145 | $asn = $_SERVER['argv'][$offset+0]; 146 | 147 | 148 | if (pfxlist_generate($o_format, $asn, $o_pfxstr, $o_pfxstr_v6, $o_pfxlen, $o_pfxlen_v6, $o_4, $o_6) < 0) { 149 | printf("Error generating prefix-list, aborting.\n"); 150 | exit(1); 151 | } 152 | 153 | ?> 154 | -------------------------------------------------------------------------------- /configure.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | installed path changed.\n"); 126 | $line = '$cfg[\'paths\'][\'base\'] ' . "= \"$i_path/\";\n"; 127 | } 128 | 129 | if ( preg_match('/^\$cfg\[\'tools\'\]\[\'cvs\'\]/', $line) ) 130 | { 131 | printf(" -> cvs path changed.\n"); 132 | $line = '$cfg[\'tools\'][\'cvs\'] ' . "= \"$cvs_path\";\n"; 133 | } 134 | 135 | if ( preg_match('/^\$cfg\[\'tools\'\]\[\'nocvs\'\]/', $line) ) 136 | { 137 | printf(" -> CVS option changed.\n"); 138 | $line = '$cfg[\'tools\'][\'nocvs\'] ' . "= $cvs_notsupported;\n"; 139 | } 140 | 141 | $newContent .= $line; 142 | } 143 | printf("\n"); 144 | fclose($fh); 145 | } else { 146 | printf("ERROR: Unable to locate or read $i_path/conf/irrpt.conf. Configuration aborted.\n"); 147 | exit(1); 148 | } 149 | 150 | if (empty($newContent)) { 151 | printf("ERROR: Unable to to find any content in $i_path/conf/irrpt.conf. Configuration aborted.\n"); 152 | exit(1); 153 | } 154 | 155 | $irrpt_conf = @fopen("$i_path/conf/irrpt.conf", "w"); 156 | if ($irrpt_conf) { 157 | fwrite($irrpt_conf, $newContent); 158 | } 159 | else { 160 | fclose($irrpt_conf); 161 | printf("ERROR: Could not write $i_path/conf/irrpt.conf.\n\n"); 162 | printf("Please ensure the file is writeable by the current user.\n"); 163 | exit(1); 164 | } 165 | 166 | log_setup("irrpt configuration verified.\n"); 167 | log_setup(" Please edit ./conf/irrpt.conf to set more options\n"); 168 | log_setup(" Please edit ./conf/irrdb.conf to add the ASNs and objects you wish to track.\n"); 169 | 170 | // ---- 171 | function log_setup($text) { 172 | print sprintf("%-30s", $text); 173 | } 174 | 175 | ?> 176 | -------------------------------------------------------------------------------- /examples/junoscript.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use JUNOS::Device; 4 | use JUNOS::Trace; 5 | use Getopt::Std; 6 | use Term::ReadKey; 7 | 8 | # Define the constants used in this example 9 | use constant REPORT_SUCCESS => 1; 10 | use constant REPORT_FAILURE => 0; 11 | use constant STATE_CONNECTED => 1; 12 | use constant STATE_LOCKED => 2; 13 | use constant STATE_CONFIG_LOADED => 3; 14 | 15 | use constant VALID_ACCESSES => "telnet|ssh|clear-text|ssl"; 16 | use constant VALID_ACTIONS => "merge|replace|override"; 17 | 18 | my $load_action = "merge"; 19 | my $login = ""; 20 | my $password = ""; 21 | 22 | # print the usage of this script 23 | sub output_usage 24 | { 25 | my $usage = "Usage: $0 [options] 26 | 27 | Where: 28 | 29 | The hostname of the target router. 30 | name of a specific file containing the configuration 31 | 32 | Options: 33 | 34 | -l A login name accepted by the target router. 35 | -p The password for the login name. 36 | -m Access method, can be clear-text, ssl, ssh or telnet. 37 | -a Load action, can be 'merge', 'replace' or 'override'. 38 | The default is 'merge'. 39 | -d turn on debug, full blast.\n\n"; 40 | 41 | die $usage; 42 | } 43 | 44 | # grace_shutdown 45 | # To gracefully shutdown. Recognized 3 states: 1 connected, 2 locked, 46 | # 3 config_loaded 47 | # Put eval around each step to make sure the next step is performed no 48 | # matter what. 49 | sub graceful_shutdown 50 | { 51 | my ($jnx, $req, $state, $success) = @_; 52 | 53 | if ($state >= STATE_CONFIG_LOADED) { 54 | print "Rolling back configuration ...\n"; 55 | eval { 56 | $jnx->load_configuration(rollback => 0); 57 | }; 58 | } 59 | 60 | if ($state >= STATE_LOCKED) { 61 | print "Unlocking configuration database ...\n"; 62 | eval { 63 | $jnx->unlock_configuration(); 64 | }; 65 | } 66 | 67 | if ($state >= STATE_CONNECTED) { 68 | print "Disconnecting from the router ...\n"; 69 | eval { 70 | $jnx->request_end_session(); 71 | $jnx->disconnect(); 72 | } 73 | } 74 | 75 | if ($success) { 76 | die "REQUEST $req SUCCEEDED\n"; 77 | } else { 78 | die "REQUEST $req FAILED\n"; 79 | } 80 | } 81 | 82 | sub read_file 83 | { 84 | my $input_file = shift; 85 | my $input_string = ""; 86 | my %escape_symbols = ( 87 | qq(") => '"', 88 | qq(>) => '>', 89 | qq(<) => '<', 90 | qq(') => ''', 91 | qq(&) => '&' 92 | ); 93 | my $char_class = join ("|", map { "($_)" } keys %escape_symbols); 94 | 95 | open(FH, $input_file) or return undef; 96 | 97 | while() { 98 | my $line = $_; 99 | $line =~ s///g; 100 | $line =~ s/<\/configuration-text>//g; 101 | $line =~ s/($char_class)/$escape_symbols{$1}/ge; 102 | $input_string .= $line; 103 | } 104 | 105 | return "$input_string"; 106 | } 107 | 108 | 109 | 110 | 111 | # Set AUTOFLUSH to true 112 | $| = 1; 113 | 114 | # Check arguments 115 | my %opt; 116 | getopts('l:p:dm:hta:', \%opt) || output_usage(); 117 | output_usage() if $opt{h}; 118 | 119 | # Check whether trace should be turned on 120 | JUNOS::Trace::init(1) if $opt{d}; 121 | 122 | $load_action = $opt{a} if $opt{a}; 123 | output_usage() unless (VALID_ACTIONS =~ /$load_action/); 124 | 125 | # Retrieve command line arguments 126 | my $hostname = shift || output_usage(); 127 | my $cfgfile = shift || output_usage(); 128 | 129 | # Retrieve the access method, can only be telnet or ssh. 130 | my $access = $opt{m} || "telnet"; 131 | output_usage() unless (VALID_ACCESSES =~ /$access/); 132 | 133 | # Check whether login name has been entered. Otherwise prompt for it 134 | if ($opt{l}) { 135 | $login = $opt{l}; 136 | } else { 137 | print STDERR "login: "; 138 | $login = ReadLine 0; 139 | chomp $login; 140 | } 141 | 142 | # Check whether password has been entered. Otherwise prompt for it 143 | if ($opt{p}) { 144 | $password = $opt{p}; 145 | } else { 146 | print STDERR "password: "; 147 | ReadMode 'noecho'; 148 | $password = ReadLine 0; 149 | chomp $password; 150 | ReadMode 'normal'; 151 | print STDERR "\n"; 152 | } 153 | 154 | my %deviceinfo = ( 155 | access => $access, 156 | login => $login, 157 | password => $password, 158 | hostname => $hostname, 159 | ); 160 | 161 | # Initialize the XML Parser 162 | my $parser = new XML::DOM::Parser; 163 | 164 | # connect TO the JUNOScript server 165 | my $jnx = new JUNOS::Device(%deviceinfo); 166 | unless ( ref $jnx ) { 167 | die "ERROR: $deviceinfo{hostname}: failed to connect.\n"; 168 | } 169 | 170 | # Lock the configuration database before making any changes 171 | print "Locking configuration database ...\n"; 172 | my $res = $jnx->lock_configuration(); 173 | my $err = $res->getFirstError(); 174 | if ($err) { 175 | print "ERROR: $deviceinfo{hostname}: failed to lock configuration. Reason: $err->{message}.\n"; 176 | graceful_shutdown($jnx, $cfgfile, STATE_CONNECTED, REPORT_FAILURE); 177 | } 178 | 179 | # Load the configuration 180 | print "Loading configuration from $cfgfile ...\n"; 181 | if (! -f $cfgfile) { 182 | print "ERROR: Cannot load configuration in $cfgfile\n"; 183 | graceful_shutdown($jnx, $cfgfile, STATE_LOCKED, REPORT_FAILURE); 184 | } 185 | 186 | my $cfg = $parser->parsestring(read_file($cfgfile)); 187 | 188 | unless ( ref $cfg ) { 189 | print "ERROR: Cannot parse $cfgfile\n"; 190 | graceful_shutdown($jnx, $xmlfile, STATE_LOCKED, REPORT_FAILURE); 191 | } 192 | 193 | # 194 | # Put the load_configuration in an eval block to make sure if the rpc-reply 195 | # has any parsing errors, the grace_shutdown will still take place. Do 196 | # not leave the database in an exclusive lock state. 197 | # 198 | eval { 199 | $res = $jnx->load_configuration( 200 | format => "text", 201 | action => $load_action, 202 | configuration => $cfg); 203 | }; 204 | 205 | if ($@) { 206 | print "ERROR: Failed to load the configuration from $xmlfile. Reason: $@\n"; 207 | graceful_shutdown($jnx, $xmlfile, STATE_CONFIG_LOADED, REPORT_FAILURE); 208 | exit(1); 209 | } 210 | 211 | unless ( ref $res ) { 212 | print "ERROR: Failed to load the configuration from $xmlfile\n"; 213 | graceful_shutdown($jnx, $xmlfile, STATE_LOCKED, REPORT_FAILURE); 214 | } 215 | 216 | $err = $res->getFirstError(); 217 | if ($err) { 218 | print "ERROR: Failed to load the configuration. Reason: $err->{message}\n"; 219 | graceful_shutdown($jnx, $xmlfile, STATE_CONFIG_LOADED, REPORT_FAILURE); 220 | } 221 | 222 | # Commit the change 223 | print "Commiting configuration from $cfgfile ...\n"; 224 | $res = $jnx->commit_configuration(); 225 | $err = $res->getFirstError(); 226 | if ($err) { 227 | print "ERROR: Failed to commit configuration. Reason: $err->{message}.\n"; 228 | graceful_shutdown($jnx, $cfgfile, STATE_CONFIG_LOADED, REPORT_FAILURE); 229 | } 230 | 231 | # Cleanup 232 | graceful_shutdown($jnx, $cfgfile, STATE_LOCKED, REPORT_SUCCESS); 233 | -------------------------------------------------------------------------------- /inc/aggregate.inc: -------------------------------------------------------------------------------- 1 | 204 | -------------------------------------------------------------------------------- /inc/irrquery.inc: -------------------------------------------------------------------------------- 1 | get_v4_routes_by_origin($object); 16 | * if ($routes == FALSE) { 17 | * $data = $irr->get_data_by_set($object); 18 | * $routes = array_merge($routes, $data['routes4']); 19 | * } 20 | * } 21 | */ 22 | 23 | /* Figure out if we have an AUT-NUM or an AS-SET, and resolve it for v4 addresses */ 24 | function resolve_v4($irr, $object) 25 | { 26 | $asnlist = array(); 27 | $routes4 = $irr->get_v4_routes_by_origin($object); 28 | if ($routes4 == FALSE) 29 | { 30 | if (($data = $irr->get_data_by_set($object, '4')) != FALSE) 31 | { 32 | $routes4 = $data['routes4']; 33 | $asnlist = $data['asns']; 34 | } 35 | } 36 | else 37 | { 38 | $asnlist[0] = $object; 39 | } 40 | 41 | if ($routes4 == FALSE) { 42 | status(STATUS_WARNING, "No registered v4 routes found for this AS."); 43 | } 44 | 45 | $result = array($routes4, $asnlist); 46 | return $result; 47 | } 48 | 49 | /* Figure out if we have an AUT-NUM or an AS-SET, and resolve it for v6 addresses */ 50 | function resolve_v6($irr, $object) 51 | { 52 | $asnlist = array(); 53 | $routes6 = $irr->get_v6_routes_by_origin($object); 54 | if ($routes6 == FALSE) 55 | { 56 | if (($data = $irr->get_data_by_set($object, '6')) != FALSE) 57 | { 58 | $routes6 = $data['routes6']; 59 | $asnlist = $data['asns']; 60 | } 61 | } 62 | else 63 | { 64 | $asnlist[0] = $object; 65 | } 66 | 67 | if ($routes6 == FALSE) { 68 | status(STATUS_WARNING, "No registered v6 routes found for this AS."); 69 | } 70 | 71 | $result = array($routes6, $asnlist); 72 | return $result; 73 | } 74 | 75 | 76 | class IRRQuery { 77 | var $host; 78 | var $port; 79 | var $fp; 80 | 81 | var $caching; 82 | var $cache; 83 | 84 | var $lastcommand; 85 | 86 | function __construct() 87 | { 88 | $this->caching = FALSE; 89 | } 90 | 91 | function _multi_mode() 92 | { 93 | $this->_send("!!\n"); 94 | return TRUE; 95 | } 96 | 97 | function _identify() 98 | { 99 | $this->_send("!nIRRPowerTools\n"); 100 | $this->_response(); 101 | return TRUE; 102 | } 103 | 104 | function _send($output) 105 | { 106 | $this->lastcommand = rtrim($output); 107 | // Attempt to send the command 108 | $r = @fwrite($this->fp, $output); 109 | 110 | if ($r === FALSE) { 111 | status(STATUS_ERROR, "IRR Query - Error on write. Re-connect issue..."); 112 | return FALSE; 113 | } 114 | 115 | return TRUE; 116 | } 117 | 118 | function _readline() 119 | { 120 | if (($data = fgets($this->fp, 256)) == FALSE) { 121 | status(STATUS_ERROR, "IRR Query - Unexpected read() error on socket."); 122 | return FALSE; 123 | } 124 | 125 | return $data; 126 | } 127 | 128 | function _response() 129 | { 130 | $data = ""; 131 | $datalen = 0; 132 | 133 | $header = rtrim($this->_readline()); 134 | 135 | if( $header == FALSE ) 136 | { 137 | status(STATUS_ERROR, "IRR Query - possible timeout, no data..."); 138 | return FALSE; 139 | } 140 | 141 | switch ($header[0]) { 142 | case "C": 143 | return TRUE; 144 | case "D": 145 | status(STATUS_WARNING, "IRR Query - key not found - Query: " . $this->lastcommand); 146 | return FALSE; 147 | case "E": 148 | return TRUE; 149 | case "F": 150 | status(STATUS_WARNING, "Query ({$this->lastcommand}) failed:" . substr($header, 1)); 151 | return FALSE; 152 | } 153 | 154 | $header = rtrim($header); 155 | 156 | if ($header[0] == 'A') 157 | $datalen = intval(substr($header,1)); 158 | else { 159 | status(STATUS_ERROR, "IRR Query - Parse error looking for data length."); 160 | return FALSE; 161 | } 162 | 163 | while (strlen($data) < $datalen) 164 | $data .= $this->_readline(); 165 | 166 | if ($datalen != strlen($data)) 167 | status(STATUS_ERROR, "IRR Query - Data read doesn't match expected length."); 168 | 169 | $footer = $this->_readline(); 170 | 171 | return rtrim($data); 172 | } 173 | 174 | 175 | /* 176 | * Not sure why this is a good idea, but IRR still lets people register 177 | * classful routes with no /prefixlength. Scan for this and fix it. 178 | */ 179 | 180 | function _classful_fix($routes) 181 | { 182 | for ($i=0;$i 0 && ($first < 128)) 188 | $length = "/8"; 189 | else if (($first) > 0 && ($first < 192)) 190 | $length = "/16"; 191 | else if (($first) > 0 && ($first < 255)) 192 | $length = "/24"; 193 | else { 194 | status(STATUS_ERROR, "Invalid prefix: {$routes[$i]}"); 195 | unset($routes[$i]); 196 | } 197 | 198 | $routes[$i] .= $length; 199 | } 200 | } 201 | 202 | return $routes; 203 | } 204 | 205 | function connect($host, $port) 206 | { 207 | $this->host = $host; 208 | $this->port = $port; 209 | 210 | @$this->fp = fsockopen($host, $port, $errno, $errstr, 15); 211 | 212 | if (!$this->fp) { 213 | status(STATUS_ERROR, "{$errstr} (errno {$errno})"); 214 | return FALSE; 215 | } 216 | 217 | $this->_multi_mode(); 218 | $this->_identify(); 219 | 220 | return $this; 221 | } 222 | 223 | function close() 224 | { 225 | $this->_send("!q\n"); 226 | status(STATUS_INFO,"Send IRR connection quit"); 227 | fclose($this->fp); 228 | } 229 | 230 | function set_sources($sources) 231 | { 232 | $this->_send("!s-{$sources}\n"); 233 | 234 | if (($results = $this->_response()) == FALSE) { 235 | return FALSE; 236 | } 237 | 238 | return explode(" ", $results); 239 | } 240 | 241 | function get_sources() 242 | { 243 | $this->_send("!s-lc\n"); 244 | 245 | if (($results = $this->_response()) == FALSE) { 246 | return FALSE; 247 | } 248 | 249 | return $results; 250 | } 251 | 252 | function cache_set($mode) 253 | { 254 | if ($mode) { 255 | $this->caching = TRUE; 256 | } else { 257 | $this->caching = FALSE; 258 | } 259 | } 260 | 261 | function cache_clear() 262 | { 263 | unset($this->cache); 264 | } 265 | 266 | 267 | function _cache_query($type, $record) 268 | { 269 | if ($this->caching) { 270 | if (isset($this->cache[$type][$record])) { 271 | /* printf("Cache hit: %s\n", $record); */ 272 | $this->cache[$type][$record]['hits']++; 273 | 274 | return $this->cache[$type][$record]['data']; 275 | } 276 | } 277 | 278 | return FALSE; 279 | } 280 | 281 | function _cache_add($type, $record, $data) 282 | { 283 | $this->cache[$type][$record]['data'] = $data; 284 | $this->cache[$type][$record]['time'] = time(); 285 | $this->cache[$type][$record]['hits'] = 0; 286 | } 287 | 288 | function get_v4_routes_by_origin($origin) 289 | { 290 | 291 | /* Prepend 'AS' to set if not provided */ 292 | if (preg_match("/^AS./i", $origin) == 0) 293 | { 294 | $origin = 'AS'.$origin; 295 | } 296 | 297 | /* Anything cached? */ 298 | if (($results = $this->_cache_query("origin", $origin)) != FALSE) 299 | return $results; 300 | 301 | /* Validate if socket connection is exists */ 302 | if (feof($this->fp) === true) 303 | { 304 | status(STATUS_ERROR, "get_v4_routes_by_origin socket closed."); 305 | fclose($this->fp); 306 | sleep(2); 307 | $this->connect($this->host, $this->port); 308 | status(STATUS_ERROR, "get_v4_routes_by_origin reconnecting socket."); 309 | } 310 | 311 | 312 | /* Get v4 prefixes */ 313 | $this->_send("!g{$origin}\n"); 314 | 315 | if (($results = $this->_response()) == FALSE) 316 | { 317 | return FALSE; 318 | } 319 | else 320 | { 321 | $results = $this->_classful_fix(explode(" ", $results)); 322 | 323 | natsort($results); 324 | $results = array_values(array_unique($results)); 325 | 326 | if ($this->caching) 327 | $this->_cache_add("origin", $origin, $results); 328 | } 329 | 330 | return $results; 331 | } 332 | 333 | function get_v6_routes_by_origin($origin) 334 | { 335 | /* Prepend 'AS' to set if not provided */ 336 | if (preg_match("/^AS./i", $origin) == 0) 337 | { 338 | $origin = 'AS'.$origin; 339 | } 340 | 341 | /* Anything cached? */ 342 | 343 | if (($results = $this->_cache_query("origin6", $origin)) != FALSE) 344 | return $results; 345 | 346 | /* Validate if socket connection is exists */ 347 | if (feof($this->fp) === true) 348 | { 349 | status(STATUS_ERROR, "get_v6_routes_by_origin socket closed."); 350 | fclose($this->fp); 351 | sleep(2); 352 | $this->connect($this->host, $this->port); 353 | status(STATUS_ERROR, "get_v6_routes_by_origin reconnecting socket"); 354 | } 355 | 356 | /* Get v6 prefixes */ 357 | $this->_send("!6{$origin}\n"); 358 | if (($results = $this->_response()) == FALSE) 359 | { 360 | return FALSE; 361 | } else { 362 | $results = explode(" ", $results); 363 | $results = array_map('strtolower', $results); 364 | 365 | /* FIX: Re-process all entries to force IPv6 compression */ 366 | $routes6tmp = array(); 367 | foreach( $results as $loop_v6 ) 368 | { 369 | list($v6_network, $v6_mask) = explode("/", $loop_v6); 370 | $v6_compress = inet_ntop(inet_pton($v6_network)) . "/$v6_mask"; 371 | array_push($routes6tmp, $v6_compress); 372 | } 373 | $results = $routes6tmp; 374 | 375 | natsort($results); 376 | $results = array_values(array_unique($results)); 377 | 378 | if ($this->caching) 379 | $this->_cache_add("origin6", $origin, $results); 380 | } 381 | 382 | return $results; 383 | } 384 | 385 | function get_data_by_set($set, $version) 386 | { 387 | $routes4 = array(); 388 | $routes6 = array(); 389 | $results4 = array(); 390 | $results6 = array(); 391 | 392 | $stats['missing_autnum'] = 0; 393 | $stats['missing_autnum_v6'] = 0; 394 | 395 | /* XXX - This should be caching results, maybe? */ 396 | 397 | /* Prepend 'AS' to set if not provided */ 398 | if (preg_match("/^AS./i", $set) == 0) 399 | { 400 | $set = 'AS'.$set; 401 | } 402 | 403 | /* Query the set data */ 404 | $this->_send("!i{$set},1\n"); 405 | if (($response = $this->_response()) == FALSE) 406 | return FALSE; 407 | $autnumlist = explode(" ", $response); 408 | 409 | /* Detect route-set, otherwise assume as-set or autnum */ 410 | if ((strchr($response, '.') != FALSE) && (stristr($response, "AS") == FALSE)) { 411 | $routes4 = $this->_classful_fix($autnumlist); 412 | } 413 | else 414 | { 415 | for ($i = 0; $i < sizeof($autnumlist); $i++) 416 | { 417 | if ($version == '4') 418 | { 419 | $results4 = $this->get_v4_routes_by_origin($autnumlist[$i]); 420 | if ($results4 == FALSE) 421 | { 422 | $stats['missing_autnum']++; 423 | continue; 424 | } 425 | } 426 | 427 | if ($version == '6') 428 | { 429 | $results6 = $this->get_v6_routes_by_origin($autnumlist[$i]); 430 | if ($results6 == FALSE) 431 | { 432 | $stats['missing_autnum_v6']++; 433 | continue; 434 | } 435 | } 436 | 437 | $routes4 = array_merge($routes4, $results4); 438 | $routes6 = array_merge($routes6, $results6); 439 | } 440 | } 441 | 442 | natsort($routes4); 443 | natsort($routes6); 444 | 445 | $results['routes4'] = array_values(array_unique($routes4)); 446 | $results['routes6'] = array_values(array_unique($routes6)); 447 | 448 | $results['asns'] = array_values(array_unique($autnumlist)); 449 | $results['stats'] = $stats; 450 | 451 | return $results; 452 | } 453 | 454 | function get_members_by_set($set, $recursive) 455 | { 456 | $routes = array(); 457 | 458 | /* Prepend 'AS' to set if not provided */ 459 | if (preg_match("/^AS./i", $set) == 0) 460 | { 461 | $set = 'AS'.$set; 462 | } 463 | 464 | /* Query the set data */ 465 | if ($recursive == TRUE) 466 | $this->_send("!i{$set},1\n"); 467 | else 468 | $this->_send("!i{$set}\n"); 469 | 470 | if (($response = $this->_response()) == FALSE) 471 | return FALSE; 472 | 473 | $data = explode(" ", $response); 474 | 475 | $data = array_values(array_unique($data)); 476 | return $data; 477 | } 478 | } 479 | 480 | ?> 481 | -------------------------------------------------------------------------------- /bin/irrpt_fetch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | update($file)) == FALSE) { 36 | status(STATUS_ERROR, "Error: Unable to perform CVS update, aborting."); 37 | exit(1); 38 | } 39 | 40 | if ($rev['old'] == "0.00") 41 | status(STATUS_INFO, sprintf(" - Importing %-45s version %s", $file, $rev['new'])); 42 | else if ($rev['old'] != $rev['new']) 43 | status(STATUS_INFO, sprintf(" - Updating %-45s version %s -> %s", $file, $rev['old'], $rev['new'])); 44 | 45 | return $rev; 46 | } 47 | 48 | function update_email($file, $email, $asString, $object, $rev, $type) 49 | { 50 | global $cvs; 51 | global $cfg; 52 | $content = ""; 53 | 54 | if ($rev['old'] == $rev['new']) { 55 | return; 56 | } 57 | 58 | $headers = "From: {$cfg['update']['from']}\n"; 59 | $headers .= "Reply-To: {$cfg['update']['reply-to']}\n"; 60 | $headers .= "Date: " . date("r") . "\n"; 61 | 62 | if ($rev['old'] != "0.00") { 63 | $content .= "Changes for {$asString} (object {$object}) ({$type}):\n\n"; 64 | $content .= $cvs->get_diff($file, $rev); 65 | $subject = $cfg['update']['subject'] . "{$asString} ({$type} Changes)"; 66 | } else { 67 | $subject = $cfg['update']['subject'] . "{$asString} ({$type} Initial Import)"; 68 | } 69 | 70 | $content .= "Complete list for {$asString} (object {$object}) ({$type}):\n"; 71 | $content .= $cvs->get_complete($file); 72 | 73 | 74 | if (!($email == "-" || $email == "none" || !isset($email))) { 75 | status(STATUS_INFO, " - Sending update notification to {$email}"); 76 | mail($email, $subject, $content, $headers); 77 | } 78 | 79 | if (isset($cfg['update']['localcopy'])) { 80 | status(STATUS_INFO, " - Sending local-copy notification to {$email}"); 81 | mail($cfg['update']['localcopy'], $subject, $content, $headers); 82 | } 83 | } 84 | 85 | 86 | function process_v4($irr, $object, $routefile4, $aggfile4) 87 | { 88 | global $cfg; 89 | 90 | $routes4 = array(); 91 | $asnlist4 = array(); 92 | 93 | $resolve_v4_result = resolve_v4($irr, $object); 94 | list($routes4, $asnlist4) = $resolve_v4_result; 95 | 96 | /* Strip out excluded routes that can not be registered */ 97 | status(STATUS_NOTICE, "Filtering against excluded routes."); 98 | $routes4 = exclusions_filter($cfg['cfgfiles']['exclusions'], $routes4); 99 | 100 | /* Write the routes we've just looked up into a file */ 101 | status(STATUS_NOTICE, "Writing routes to local database."); 102 | 103 | if (($output4 = @fopen($routefile4, "w")) == FALSE) 104 | { 105 | status(STATUS_WARNING, "Can not open IRRDB output file for {$asString}, skipping this record. (check perms?)"); 106 | } 107 | 108 | if (is_array($routes4)) 109 | { 110 | for($i = 0; $i < sizeof($routes4); $i++) 111 | { 112 | fwrite($output4, sprintf("%s\n", $routes4[$i])); 113 | } 114 | } 115 | fclose($output4); 116 | update_file_permissions($routefile4, $cfg['cfgfiles']['umask']); 117 | 118 | /* Aggregate the route file */ 119 | status(STATUS_NOTICE, "Aggregating v4 routes."); 120 | $aggregated_routes4 = aggregate_routes($routefile4); 121 | 122 | /* print result to a file */ 123 | if (($afile4 = @fopen($aggfile4, "w")) == FALSE) 124 | { 125 | status(STATUS_WARNING, "Can not open agg route file, skipping this record."); 126 | } 127 | 128 | if (is_array($aggregated_routes4)) 129 | { 130 | for ($i = 0; $i < sizeof($aggregated_routes4); $i++) 131 | { 132 | fwrite($afile4, "$aggregated_routes4[$i]"); 133 | } 134 | } 135 | fclose($afile4); 136 | 137 | update_file_permissions($aggfile4, $cfg['cfgfiles']['umask']); 138 | 139 | return $asnlist4; 140 | } 141 | 142 | function process_v6($irr, $object, $routefile6, $aggfile6) 143 | { 144 | global $cfg; 145 | 146 | $routes6 = array(); 147 | $asnlist6 = array(); 148 | 149 | $resolve_v6_result = resolve_v6($irr, $object); 150 | $routes6tmp = array(); 151 | list($routes6, $asnlist6) = $resolve_v6_result; 152 | 153 | /* Strip out excluded routes that can not be registered */ 154 | status(STATUS_NOTICE, "Filtering against excluded routes."); 155 | $routes6 = exclusions_filter($cfg['cfgfiles']['exclusions'], $routes6); 156 | 157 | 158 | /* Write the routes we've just looked up into a file */ 159 | status(STATUS_NOTICE, "Writing routes to local database."); 160 | 161 | /* Write routes to file */ 162 | if ( ($output6 = @fopen($routefile6, "w")) == FALSE ) 163 | { 164 | status(STATUS_WARNING, "Can not open IRRDB output file for {$asString}, skipping this record."); 165 | } 166 | 167 | if ( is_array($routes6) ) 168 | { 169 | for($i = 0; $i < sizeof($routes6); $i++) 170 | { 171 | fwrite($output6, sprintf("%s\n", $routes6[$i])); 172 | } 173 | } 174 | fclose($output6); 175 | update_file_permissions($routefile6, $cfg['cfgfiles']['umask']); 176 | 177 | /* Aggregate the route file */ 178 | status(STATUS_NOTICE, "Aggregating v6 routes."); 179 | $aggregated_routes6 = aggregate_routes($routefile6); 180 | 181 | /* print result to a file */ 182 | if (($afile6 = @fopen($aggfile6, "w")) == FALSE) 183 | { 184 | status(STATUS_WARNING, "Can not open agg route file, skipping this record. (Check perms?)"); 185 | } 186 | 187 | if (is_array($aggregated_routes6)) 188 | { 189 | for ($i = 0; $i < sizeof($aggregated_routes6); $i++) 190 | { 191 | fwrite($afile6, "$aggregated_routes6[$i]"); 192 | } 193 | } 194 | fclose($afile6); 195 | update_file_permissions($aggfile6, $cfg['cfgfiles']['umask']); 196 | 197 | return $asnlist6; 198 | } 199 | 200 | function process_as($asString, $asNumber, $count, $object, $irr, $email, $o_quiet, $o_4, $o_6, $o_cvs) 201 | { 202 | global $cvs; 203 | global $cfg; 204 | 205 | status(STATUS_INFO, "Processing {$asString} [$object] (Record {$count})"); 206 | 207 | /* Figure out if we have an AUT-NUM or an AS-SET, and resolve it */ 208 | status(STATUS_NOTICE, "Parsed IRR Object {$object}"); 209 | 210 | /* init files and ensure files are writeable by the effective user id*/ 211 | $routefile4 = $cfg['paths']['db'] . $asNumber . ".4"; 212 | check_file_perms($routefile4); 213 | 214 | $aggfile4 = $routefile4 . $cfg['aggregate']['suffix']; 215 | check_file_perms($aggfile4); 216 | 217 | $routefile6 = $cfg['paths']['db'] . $asNumber . ".6"; 218 | check_file_perms($routefile6); 219 | 220 | $aggfile6 = $routefile6 . $cfg['aggregate']['suffix']; 221 | check_file_perms($aggfile6); 222 | 223 | /* call v4 / v6 processing and aggregation */ 224 | $asnlist4 = array(); 225 | $asnlist6 = array(); 226 | 227 | /* Process V4 */ 228 | if( $o_4 == 1 || ($o_4 == 0 && $o_6 == 0) ) 229 | { 230 | status(STATUS_NOTICE, "Fetching v4 routes."); 231 | status(STATUS_DEBUG, "process_v4(\$irr, $object, $routefile4, $aggfile4);"); 232 | $asnlist4 = process_v4($irr, $object, $routefile4, $aggfile4); 233 | } 234 | 235 | /* Process V6 */ 236 | if( $o_6 == 1 || ($o_4 == 0 && $o_6 == 0) ) 237 | { 238 | status(STATUS_NOTICE, "Fetching v6 routes."); 239 | $asnlist6 = process_v6($irr, $object, $routefile6, $aggfile6); 240 | } 241 | 242 | 243 | /* merge files into common v4 & v6 file */ 244 | $routefile = $cfg['paths']['db'] . $asNumber; 245 | check_file_perms($routefile); 246 | $aggfile = $routefile . $cfg['aggregate']['suffix']; 247 | check_file_perms($aggfile); 248 | 249 | concat_files($routefile4, $routefile6, $routefile); 250 | concat_files($aggfile4, $aggfile6, $aggfile); 251 | 252 | update_file_permissions($routefile, $cfg['cfgfiles']['umask']); 253 | update_file_permissions($aggfile, $cfg['cfgfiles']['umask']); 254 | 255 | /* Log the ASNs behind this object, for future AS-PATH use */ 256 | $asnlist = array_merge($asnlist4, $asnlist6); 257 | $asnfile = $routefile . $cfg['fetch']['asn_suffix']; 258 | status(STATUS_NOTICE, "Writing ASN list to local database."); 259 | if (($output = @fopen($asnfile, "w")) == FALSE) 260 | { 261 | status(STATUS_WARNING, "Can not open ASN list output file for {$asString}, skipping this record."); 262 | } 263 | 264 | for($i = 0; $i < sizeof($asnlist); $i++) 265 | { 266 | fwrite($output, sprintf("%s\n", preg_replace("/[aA][sS]/", "", $asnlist[$i]))); 267 | } 268 | fclose($output); 269 | update_file_permissions($asnfile, $cfg['cfgfiles']['umask']); 270 | 271 | /* Perform CVS tracking */ 272 | if ($o_cvs == 1) 273 | { 274 | status(STATUS_NOTICE, "Tracking data in CVS."); 275 | $cvs->init($cfg['paths']['cvsroot']); 276 | check_file_perms($cfg['paths']['cvsroot']); 277 | 278 | $rev = track($routefile4); 279 | $rev = track($routefile6); 280 | $rev = track($aggfile4); 281 | $rev = track($aggfile6); 282 | $rev = track($routefile); 283 | $rev_a = track($aggfile); 284 | 285 | /* Send e-mail updates */ 286 | status(STATUS_NOTICE, "Send update email."); 287 | switch ($cfg['fetch']['emailonchange']) { 288 | case "both": 289 | update_email($routefile, $email, $asString, $object, $rev, "Full"); 290 | update_email($aggfile, $email, $asString, $object, $rev_a, "Aggregated"); 291 | break; 292 | case "full": 293 | update_email($routefile, $email, $asString, $object, $rev, "Full"); 294 | break; 295 | case "aggregate": 296 | update_email($aggfile, $email, $asString, $object, $rev_a, "Aggregated"); 297 | break; 298 | case "none": 299 | case "no": 300 | break; 301 | } 302 | } 303 | } 304 | 305 | function load_irrdb($o_quiet, $o_irrdbfile) 306 | { 307 | global $cfg; 308 | global $o_irrdbConf; 309 | $count = 0; 310 | 311 | // Load the IRRDB file and store entries into a hash 312 | if (!($irrdb = @fopen($cfg['cfgfiles']['irrdb_list'], "r"))) 313 | { 314 | status(STATUS_ERROR, "Unable to open irrdb config file, aborting."); 315 | exit(-1); 316 | } 317 | 318 | /* Parse the IRRDB config file */ 319 | while( !feof($irrdb) ) 320 | { 321 | $line = rtrim(fgets($irrdb, 256)); 322 | 323 | /* Skip comments and junk lines */ 324 | if ((strlen($line) == 0) || ($line[0] == "#")) 325 | continue; 326 | 327 | /* Skip lines that do not start with a number */ 328 | if ( ! preg_match("/^[\d+]/", $line) ) 329 | continue; 330 | 331 | $results = preg_split( "/[ \t]+/", $line); 332 | $asNumber = $results[0]; 333 | $object = $results[1]; 334 | 335 | if ( isset($results[2]) && filter_var($results[2], FILTER_VALIDATE_EMAIL) ) 336 | { 337 | // EMail is optional in the configuration file 338 | $email = $results[2]; 339 | } else { 340 | $email = NULL; 341 | } 342 | 343 | $asString = "AS" . $asNumber; 344 | 345 | $count++; 346 | 347 | if ( ! isset($o_irrdbConf[$asNumber]) ) 348 | { 349 | $o_irrdbConf[$asNumber]['asn'] = $asNumber; 350 | $o_irrdbConf[$asNumber]['email'] = $email; 351 | $o_irrdbConf[$asNumber]['object'] = $object; 352 | } 353 | } 354 | fclose($irrdb); 355 | } 356 | 357 | /********** PROCESSING STARTS HERE *********/ 358 | 359 | 360 | /* Set UID specified in the config file */ 361 | if (posix_geteuid() == 0) { 362 | if ($cfg['fetch']['set_uid']) { 363 | if (!($user = posix_getpwnam($cfg['fetch']['set_uid']))) 364 | $user = posix_getpwuid($cfg['fetch']['set_uid']); 365 | 366 | if (!$user) { 367 | printf("Unable to change to the specified UID, aborting.\n"); 368 | exit(1); 369 | } 370 | 371 | posix_setuid($user['uid']); 372 | posix_seteuid($user['uid']); 373 | } 374 | } 375 | 376 | // disable cvs tracking per config file option 377 | if ($cfg['tools']['nocvs']) { 378 | $o_cvs = 0; 379 | } 380 | 381 | 382 | /* Parse through the cmdline options. */ 383 | for ($offset = 1; $offset < $_SERVER['argc']; $offset++) 384 | { 385 | if (substr($_SERVER['argv'][$offset], 0, 1) != "-") 386 | break; 387 | 388 | switch($_SERVER['argv'][$offset]) { 389 | case "-h": 390 | case "--help": 391 | printf("Usage: %s [-dh46qv] [-f file] [--nocvs] [object]\n", $_SERVER['argv'][0]); 392 | exit(1); 393 | case "-q": 394 | case "--quiet": 395 | $o_quiet = 1; 396 | break; 397 | case "-d": 398 | case "--debug": 399 | $o_debug = 1; 400 | break; 401 | case "-v": 402 | case "--verbose": 403 | $o_verbose = 1; 404 | break; 405 | case "--nocvs": 406 | $o_cvs = 0; 407 | break; 408 | case "-4": 409 | case "--4": 410 | $o_4 = 1; 411 | break; 412 | case "-6": 413 | case "--6": 414 | $o_6 = 1; 415 | break; 416 | case "-f": 417 | case "--file": 418 | $o_irrdb = $_SERVER['argv'][$offset+1]; 419 | $offset++; 420 | break; 421 | } 422 | } 423 | 424 | /* Set Timezone */ 425 | date_default_timezone_set($cfg['global']['timezone']); 426 | 427 | /* Set Memory Limit */ 428 | ini_set("memory_limit",$cfg['global']['memory_limit']); 429 | 430 | /* Open the file with the list of IRR objects we will be tracking */ 431 | /* provided via command line or through the config file */ 432 | if( $o_irrdb ) 433 | { 434 | $cfg['cfgfiles']['irrdb_list'] = $o_irrdb; 435 | } 436 | load_irrdb($o_quiet,$cfg['cfgfiles']['irrdb_list']); 437 | 438 | /* Establish a connection with our IRR Query whois server */ 439 | if ($irr->connect($cfg['fetch']['host'], $cfg['fetch']['port']) == FALSE) 440 | { 441 | status(STATUS_ERROR, "Unable to connect to IRR Query whois server " . 442 | $cfg['fetch']['host'] . ", aborting."); 443 | exit(-1); 444 | } 445 | 446 | /* Optionally enable a local cache of prefixes per aut-num record */ 447 | if ($cfg['fetch']['cache']) { 448 | $irr->cache_set(TRUE); 449 | } 450 | 451 | /* If we don't want to query all IRR sources, set the new sources now */ 452 | if ($cfg['fetch']['sources'] != "all") 453 | { 454 | $irr->set_sources($cfg['fetch']['sources']); 455 | } 456 | 457 | // check AS parameter if provided 458 | if( isset($_SERVER['argv'][$offset+0]) ) 459 | { 460 | if (preg_match("/^AS./i", $_SERVER['argv'][$offset+0])) 461 | { 462 | // MATCH: Object listed as AS12345 or AS-ALPHATEST 463 | $asString = strtoupper($_SERVER['argv'][$offset+0]); 464 | $object = $asString; 465 | 466 | $asNumber = preg_replace("/[aA][sS]/", "", $asString); 467 | 468 | status(STATUS_DEBUG, "\$asNumber = $asNumber"); 469 | if ( is_numeric($asNumber) == FALSE ) 470 | { 471 | $asNumber = $asString; 472 | } 473 | 474 | $count++; 475 | 476 | // NOTE: NOT ENABLED 477 | // This section intentionally left out for people who wish 478 | // to pull routes for a single ASN, ie: AS12345 instead of 479 | // having it lookup the object ID for AS 12345 which might be 480 | // AS-ALPHATEST 481 | // 482 | //status(STATUS_INFO, "DBG: matched /^AS./i as $asNumber."); 483 | //if( isset($o_irrdbConf[$asNumber]['object']) ) 484 | //{ 485 | // $object = $o_irrdbConf[$asNumber]['object']; 486 | //} else { 487 | // $object = $asString; 488 | //} 489 | status(STATUS_DEBUG, "process_as( $asString, $asNumber, cnt: $count, str: $object, 'none', $o_quiet, $o_4, $o_6, $o_cvs)"); 490 | process_as($asString, $asNumber, $count, $object, $irr, 'none', $o_quiet, $o_4, $o_6, $o_cvs); 491 | } 492 | elseif ( is_numeric($_SERVER['argv'][$offset+0]) && 493 | (int)$_SERVER['argv'][$offset+0] > 0 494 | && (int)$_SERVER['argv'][$offset+0] <= 4294967295) 495 | { 496 | $asString = "AS" . $_SERVER['argv'][$offset+0]; 497 | $asNumber = $_SERVER['argv'][$offset+0]; 498 | $count++; 499 | 500 | // 501 | if (isset($o_irrdbConf[$asNumber]['object'])) 502 | { 503 | $object = $o_irrdbConf[$asNumber]['object']; 504 | } else { 505 | $object = $asString; 506 | } 507 | status(STATUS_DEBUG, "process_as( $asString, $asNumber, cnt: $count, str: $object, 'none', $o_quiet, $o_4, $o_6, $o_cvs)"); 508 | process_as($asString, $asNumber, $count, $object, $irr, 'none', $o_quiet, $o_4, $o_6, $o_cvs); 509 | } 510 | else 511 | { 512 | status(STATUS_ERROR, "Invalid AS or AS-SET input, aborting."); 513 | exit(-1); 514 | } 515 | } 516 | else { 517 | // NOTE: Cycle through array for ASN numbers and their objects 518 | status(STATUS_INFO, "Reading irrdbConf array list for ASNs, Objects, Email"); 519 | $count = 0; 520 | 521 | foreach( $o_irrdbConf as $v1 ) 522 | { 523 | $count++; 524 | foreach( $v1 as $v2 ) 525 | { 526 | //print "----\n"; 527 | //print "V1 ASN: " . $v1['asn'] . "\n"; 528 | //print "V1 Obj: " . $v1['object'] . "\n"; 529 | //print "V1 Ema: " . $v1['email'] . "\n"; 530 | 531 | $asString = "AS" . $v1['asn']; 532 | $asNumber = $v1['asn']; 533 | $object = $v1['object']; 534 | $email = $v1['email']; 535 | } 536 | 537 | process_as( $asString, $asNumber, $count, $object, $irr, $email, $o_quiet, $o_4, $o_6, $o_cvs); 538 | } // END: foreach $o_irrdbConf 539 | } 540 | 541 | if( $o_quiet == 0 ) 542 | { 543 | status(STATUS_INFO, "Completed processing of {$count} IRR object(s)."); 544 | } 545 | 546 | 547 | ?> 548 | -------------------------------------------------------------------------------- /inc/pfxlist.inc: -------------------------------------------------------------------------------- 1 | $pfxlen ) 87 | continue; 88 | 89 | if( $subnet_parts[1] == $pfxlen ) 90 | { 91 | $formatted_line = sprintf(" route-filter %s exact;\n", $line); 92 | } else { 93 | $formatted_line = sprintf(" route-filter %s upto /%d;\n", $line, $pfxlen); 94 | } 95 | $v4_list[] = $formatted_line; 96 | } 97 | elseif (_ip_version($line) == '6') 98 | { 99 | $subnet_parts = explode("/", $line); 100 | if( $subnet_parts[1] > $pfxlen_v6 ) 101 | continue; 102 | 103 | if( $subnet_parts[1] == $pfxlen_v6 ) 104 | { 105 | $formatted_line = sprintf(" route-filter %s exact;\n", $line); 106 | } else { 107 | $formatted_line = sprintf(" route-filter %s prefix-length-range /%d-/%d;\n", $line, $subnet_parts[1], $pfxlen_v6); 108 | } 109 | $v6_list[] = $formatted_line; 110 | } 111 | else 112 | { 113 | status(STATUS_ERROR, "Prefix not recognized: {$line}"); 114 | return FALSE; 115 | } 116 | } 117 | 118 | pfxlist_print_juniper($v4_policy_name, $v4_list); 119 | pfxlist_print_juniper($v6_policy_name, $v6_list); 120 | return 0; 121 | } 122 | 123 | function pfxlist_print_juniper($policy_name, $list) { 124 | 125 | if( empty($list) === FALSE ) 126 | { 127 | printf("policy-options {\n"); 128 | printf(" replace: policy-statement %s {\n", $policy_name); 129 | printf(" term prefixes {\n"); 130 | printf(" from {\n"); 131 | 132 | foreach($list as $line) { 133 | print $line; 134 | } 135 | printf(" }\n"); 136 | printf(" then next policy;\n"); 137 | printf(" }\n"); 138 | printf(" then reject;\n"); 139 | printf(" }\n"); 140 | printf("}\n"); 141 | } else { 142 | // Reject all entries 143 | printf("policy-options {\n"); 144 | printf(" replace: policy-statement %s {\n", $policy_name); 145 | printf(" then reject;\n"); 146 | printf(" }\n"); 147 | printf("}\n"); 148 | } 149 | } 150 | 151 | function pfxlist_generate_cisco($pfxfile, $asn, $pfxstr, $pfxstr_v6, $pfxlen, $pfxlen_v6, $o_4, $o_6) 152 | { 153 | $pfxname = sprintf($pfxstr, $asn); 154 | $pfxname_v6 = sprintf($pfxstr_v6, $asn); 155 | global $cfg; 156 | 157 | printf("conf t\n"); 158 | if( $o_4 == 1 && $o_6 == 0 ) 159 | { 160 | printf("no ip prefix-list %s\n", $pfxname); 161 | } 162 | elseif( $o_6 == 1 && $o_4 == 0 ) 163 | { 164 | printf("no ipv6 prefix-list %s\n", $pfxname_v6); 165 | } 166 | else 167 | { 168 | printf("no ip prefix-list %s\n", $pfxname); 169 | printf("no ipv6 prefix-list %s\n", $pfxname_v6); 170 | } 171 | 172 | while (!feof($pfxfile)) { 173 | if (!($line = rtrim(fgets($pfxfile, 64)))) 174 | continue; 175 | 176 | $prefix = explode("/", rtrim($line)); 177 | // $prefix[0] = cidr 10.0.0.0 178 | // $prefix[1] = mask /26 179 | 180 | if (_ip_version($line) == '4') 181 | { 182 | if ( $prefix[1] > $pfxlen ) 183 | continue; 184 | 185 | printf("ip prefix-list %s permit %s/%d", $pfxname, $prefix[0], $prefix[1]); 186 | if ($prefix[1] < $pfxlen) 187 | printf(" le %s", $pfxlen); 188 | } 189 | 190 | elseif (_ip_version($line) == '6') 191 | { 192 | if( $prefix[1] > $pfxlen_v6 ) 193 | continue; 194 | 195 | printf("ipv6 prefix-list %s permit %s/%d", $pfxname_v6, $prefix[0], $prefix[1]); 196 | 197 | if ($prefix[1] < $pfxlen_v6) 198 | printf(" le %s", $pfxlen_v6); 199 | } 200 | 201 | printf("\n"); 202 | } 203 | 204 | printf("end\n"); 205 | printf("write mem\n"); 206 | 207 | return 0; 208 | } 209 | 210 | function pfxlist_generate_iosxr($pfxfile, $asn, $pfxstr, $pfxstr_v6, $pfxlen, $pfxlen_v6, $o_4, $o_6) 211 | { 212 | $pfxname = sprintf($pfxstr, $asn); 213 | $pfxname_v6 = sprintf($pfxstr_v6, $asn); 214 | $pfx1st = 'Y'; 215 | $pfx1st_v6 = 'Y'; 216 | global $cfg; 217 | 218 | printf("conf\n"); 219 | if( $o_4 == 1 && $o_6 == 0 ) 220 | { 221 | printf("no prefix-set %s\n", $pfxname); 222 | } 223 | elseif( $o_6 == 1 && $o_4 == 0 ) 224 | { 225 | printf("no prefix-set %s\n", $pfxname_v6); 226 | } 227 | else 228 | { 229 | printf("no prefix-set %s\n", $pfxname); 230 | printf("no prefix-set %s\n", $pfxname_v6); 231 | } 232 | 233 | $skipcomma = false; 234 | 235 | while (!feof($pfxfile)) { 236 | if (!($line = rtrim(fgets($pfxfile, 64)))) 237 | continue; 238 | 239 | $prefix = explode("/", rtrim($line)); 240 | if ($pfx1st_v6 == 'N' && preg_match('/:/', $prefix[0])) { 241 | if ($skipcomma) 242 | $skipcomma = false; 243 | else 244 | printf(",\n"); 245 | } 246 | elseif (preg_match('/:/', $prefix[0]) && $pfx1st == 'N') 247 | { 248 | printf("\nend-set\n"); 249 | printf("prefix-set %s\n", $pfxname_v6); 250 | $pfx1st_v6 = 'N'; 251 | } 252 | elseif ($pfx1st_v6 == 'Y' && preg_match('/:/', $prefix[0])) 253 | { 254 | printf("prefix-set %s\n", $pfxname_v6); 255 | $pfx1st_v6 = 'N'; 256 | } 257 | elseif ($pfx1st == 'N') 258 | { 259 | if ($skipcomma) 260 | $skipcomma = false; 261 | else 262 | printf(",\n"); 263 | } 264 | else 265 | { 266 | $pfx1st = 'N'; 267 | printf("prefix-set %s\n", $pfxname); 268 | } 269 | 270 | 271 | if (_ip_version($line) == '4') 272 | { 273 | if( $prefix[1] > $pfxlen ) 274 | { 275 | $skipcomma = true; 276 | continue; 277 | } 278 | 279 | printf(" %s/%d", $prefix[0], $prefix[1]); 280 | if ($prefix[1] < $pfxlen) 281 | printf(" le %s", $pfxlen); 282 | } 283 | 284 | elseif (_ip_version($line) == '6') 285 | { 286 | if( $prefix[1] > $pfxlen_v6 ) 287 | { 288 | $skipcomma = true; 289 | continue; 290 | } 291 | 292 | printf(" %s/%d", $prefix[0], $prefix[1]); 293 | 294 | if ($prefix[1] < $pfxlen_v6) 295 | printf(" le %s", $pfxlen_v6); 296 | } 297 | 298 | } 299 | 300 | if ($pfx1st == 'N' || $pfx1st_v6 == 'N') 301 | { 302 | printf("\n"); 303 | printf("end-set\n"); 304 | } 305 | printf("commit\n"); 306 | printf("exit\n"); 307 | 308 | return 0; 309 | } 310 | 311 | function pfxlist_generate_extreme($pfxfile, $asn, $pfxstr, $pfxstr_v6, $pfxlen, $pfxlen_v6, $o_4, $o_6) 312 | { 313 | /* ExtremeWare can not handle : names, replace with - */ 314 | $pfxname = str_replace(":","-", sprintf($pfxstr, $asn)); 315 | $pfxname_v6 = str_replace(":","-", sprintf($pfxstr_v6, $asn)); 316 | global $cfg; 317 | 318 | if( $o_4 == 1 && $o_6 == 0 ) 319 | { 320 | printf("delete access-profile %s\n", $pfxname); 321 | printf("create access-profile %s type ipaddress\n", $pfxname); 322 | } 323 | elseif( $o_6 == 1 && $o_4 == 0 ) 324 | { 325 | printf("delete access-profile %s\n", $pfxname_v6); 326 | printf("create access-profile %s type ipaddress\n", $pfxname_v6); 327 | } 328 | else 329 | { 330 | printf("delete access-profile %s\n", $pfxname); 331 | printf("create access-profile %s type ipaddress\n", $pfxname); 332 | printf("delete access-profile %s\n", $pfxname_v6); 333 | printf("create access-profile %s type ipaddress\n", $pfxname_v6); 334 | } 335 | 336 | $counter = 10; 337 | 338 | while (!feof($pfxfile)) { 339 | if (!($line = rtrim(fgets($pfxfile, 64)))) 340 | continue; 341 | 342 | $prefix = explode("/", rtrim($line)); 343 | 344 | 345 | if (_ip_version($line) == '4') 346 | { 347 | if( $prefix[1] > $pfxlen ) 348 | continue; 349 | 350 | printf("configure access-profile %s add %d permit ipaddress %s/%d", $pfxname, $counter += 10, $prefix[0], $prefix[1]); 351 | if ($prefix[1] < $pfxlen) 352 | { 353 | printf("\n", $pfxlen); 354 | } 355 | else 356 | { 357 | printf(" exact\n"); 358 | } 359 | } 360 | elseif (_ip_version($line) == '6') 361 | { 362 | if( $prefix[1] > $pfxlen_v6 ) 363 | continue; 364 | 365 | printf("configure access-profile %s add %d permit ipaddress %s/%d", $pfxname_v6, $counter += 10, $prefix[0], $prefix[1]); 366 | if ($prefix[1] < $pfxlen_v6) 367 | { 368 | printf("\n", $pfxlen_v6); 369 | } 370 | else 371 | { 372 | printf(" exact\n"); 373 | } 374 | } 375 | } 376 | 377 | printf("save\n"); 378 | 379 | return 0; 380 | } 381 | 382 | function pfxlist_generate_force10($pfxfile, $asn, $pfxstr, $pfxstr_v6, $pfxlen, $pfxlen_v6, $o_4, $o_6) 383 | { 384 | $pfxname = sprintf($pfxstr, $asn); 385 | $pfxname_v6 = sprintf($pfxstr_v6, $asn); 386 | $v4_list = $v6_list = array(); 387 | global $cfg; 388 | 389 | printf("conf t\n"); 390 | 391 | while (!feof($pfxfile)) { 392 | if (!($line = rtrim(fgets($pfxfile, 64)))) 393 | continue; 394 | 395 | $prefix = explode("/", rtrim($line)); 396 | 397 | if (_ip_version($line) == '4') 398 | { 399 | if( $prefix[1] > $pfxlen ) 400 | continue; 401 | 402 | $formatted_line = sprintf(" permit %s/%d", $prefix[0], $prefix[1]); 403 | if ($prefix[1] < $pfxlen) 404 | $formatted_line .= sprintf(" le %s", $pfxlen); 405 | 406 | $v4_list[] = $formatted_line; 407 | } 408 | elseif (_ip_version($line) == '6') 409 | { 410 | if( $prefix[1] > $pfxlen_v6 ) 411 | continue; 412 | 413 | $formatted_line = sprintf(" permit %s/%d", $prefix[0], $prefix[1]); 414 | if ($prefix[1] < $pfxlen_v6) 415 | $formatted_line .= sprintf(" le %s", $pfxlen_v6); 416 | 417 | $v6_list[] = $formatted_line; 418 | } 419 | else 420 | { 421 | status(STATUS_ERROR, "Prefix not recognized: {$line}"); 422 | return FALSE; 423 | } 424 | } //end while 425 | 426 | pfxlist_print_force10($pfxname, $v4_list, 4); 427 | pfxlist_print_force10($pfxname_v6, $v6_list, 6); 428 | 429 | printf("end\n"); 430 | printf("write mem\n"); 431 | 432 | return 0; 433 | } 434 | 435 | function pfxlist_print_force10($policy_name, $list, $AF) 436 | { 437 | if( count($list) > 0 ) 438 | { 439 | if( $AF == 4 ) 440 | { 441 | printf("no ip prefix-list %s\n", $policy_name); 442 | printf("ip prefix-list %s\n", $policy_name); 443 | } else { 444 | printf("no ipv6 prefix-list %s\n", $policy_name); 445 | printf("ipv6 prefix-list %s\n", $policy_name); 446 | } 447 | } 448 | 449 | foreach( $list as $line ) 450 | { 451 | print $line; 452 | printf("\n"); 453 | } 454 | } 455 | 456 | function pfxlist_generate_openbgpd($pfxfile, $asn, $pfxstr, $pfxstr_v6, $pfxlen, $pfxlen_v6, $o_4, $o_6) 457 | { 458 | $pfxname = sprintf($pfxstr, $asn); 459 | $pfxname_v6 = sprintf($pfxstr_v6, $asn); 460 | $v4_list = $v6_list = array(); 461 | global $cfg; 462 | 463 | // printf("conf t\n"); 464 | 465 | while (!feof($pfxfile)) { 466 | if (!($line = rtrim(fgets($pfxfile, 64)))) 467 | continue; 468 | 469 | $prefix = explode("/", rtrim($line)); 470 | 471 | if (_ip_version($line) == '4') 472 | { 473 | if( $prefix[1] > $pfxlen ) 474 | continue; 475 | 476 | $formatted_line = sprintf(" allow from any peer-as %d prefix %s/%d", $asn, $prefix[0], $prefix[1]); 477 | if ($prefix[1] < $pfxlen) 478 | $formatted_line .= sprintf(" <= %s", $pfxlen); 479 | 480 | $v4_list[] = $formatted_line; 481 | printf($formatted_line . "\n"); 482 | } 483 | elseif (_ip_version($line) == '6') 484 | { 485 | if( $prefix[1] > $pfxlen_v6 ) 486 | continue; 487 | 488 | $formatted_line = sprintf(" allow from any peer-as %d prefix %s/%d", $asn, $prefix[0], $prefix[1]); 489 | if ($prefix[1] < $pfxlen_v6) 490 | $formatted_line .= sprintf(" <= %s", $pfxlen_v6); 491 | 492 | $v6_list[] = $formatted_line; 493 | printf($formatted_line . "\n"); 494 | } 495 | else 496 | { 497 | status(STATUS_ERROR, "Prefix not recognized: {$line}"); 498 | return FALSE; 499 | } 500 | } //end while 501 | 502 | // pfxlist_print_openbgpd($pfxname, $v4_list, 4); 503 | // pfxlist_print_openbgpd($pfxname_v6, $v6_list, 6); 504 | 505 | return 0; 506 | } 507 | 508 | function pfxlist_generate_edgeos($pfxfile, $asn, $pfxstr, $pfxstr_v6, $pfxlen, $pfxlen_v6, $o_4, $o_6) 509 | { 510 | $pfxname = sprintf($pfxstr, $asn); 511 | $pfxname_v6 = sprintf($pfxstr_v6, $asn); 512 | global $cfg; 513 | $rule = 0; 514 | $rule_v6 = 0; 515 | 516 | printf("configure\n"); 517 | 518 | if( $o_4 == 1 && $o_6 == 0 ) 519 | { 520 | printf("delete policy prefix-list %s\n", $pfxname); 521 | } 522 | elseif( $o_6 == 1 && $o_4 == 0 ) 523 | { 524 | printf("delete policy prefix-list6 %s\n", $pfxname_v6); 525 | } 526 | else 527 | { 528 | printf("delete policy prefix-list %s\n", $pfxname); 529 | printf("delete policy prefix-list6 %s\n", $pfxname_v6); 530 | } 531 | 532 | while (!feof($pfxfile)) { 533 | if (!($line = rtrim(fgets($pfxfile, 64)))) 534 | continue; 535 | 536 | $prefix = explode("/", rtrim($line)); 537 | 538 | if (_ip_version($line) == '4') 539 | { 540 | if( $prefix[1] > $pfxlen ) 541 | continue; 542 | 543 | $rule = $rule + 1; 544 | 545 | printf("set policy prefix-list %s rule %d prefix %s/%d\n", $pfxname, $rule, $prefix[0], $prefix[1]); 546 | 547 | if ($prefix[1] < $pfxlen) 548 | printf("set policy prefix-list %s rule %d le %s\n", $pfxname, $rule, $pfxlen); 549 | } 550 | 551 | elseif (_ip_version($line) == '6') 552 | { 553 | 554 | if( $prefix[1] > $pfxlen_v6 ) 555 | continue; 556 | 557 | $rule_v6 = $rule_v6 + 1; 558 | 559 | printf("set policy prefix-list6 %s rule %d prefix %s/%d\n", $pfxname_v6, $rule_v6, $prefix[0], $prefix[1]); 560 | 561 | if ($prefix[1] < $pfxlen_v6) 562 | printf("set policy prefix-list6 %s rule %d le %s\n", $pfxname_v6, $rule_v6, $pfxlen_v6); 563 | 564 | } 565 | else 566 | { 567 | status(STATUS_ERROR, "Prefix not recognized: {$line}"); 568 | return FALSE; 569 | } 570 | } //end while 571 | 572 | printf("commit\n"); 573 | printf("save\n"); 574 | 575 | return 0; 576 | } 577 | 578 | function pfxlist_generate_huawei($pfxfile, $asn, $pfxstr, $pfxstr_v6, $pfxlen, $pfxlen_v6, $o_4, $o_6) { 579 | $pfxname = sprintf($pfxstr, $asn); 580 | $pfxname_v6 = sprintf($pfxstr_v6, $asn); 581 | global $cfg; 582 | 583 | printf("system-view\n"); 584 | 585 | if( $o_4 == 1 && $o_6 == 0 ) 586 | { 587 | printf("undo ip ip-prefix %s\n", $pfxname); 588 | } 589 | elseif( $o_6 == 1 && $o_4 == 0 ) 590 | { 591 | printf("undo ip ipv6-prefix %s\n", $pfxname_v6); 592 | } 593 | else 594 | { 595 | printf("undo ip ip-prefix %s\n", $pfxname); 596 | printf("undo ip ipv6-prefix %s\n", $pfxname_v6); 597 | } 598 | 599 | while (!feof($pfxfile)) { 600 | if (!($line = rtrim(fgets($pfxfile, 64)))) 601 | continue; 602 | 603 | $prefix = explode("/", rtrim($line)); 604 | 605 | if (_ip_version($line) == '4') 606 | { 607 | if( $prefix[1] > $pfxlen ) 608 | continue; 609 | 610 | printf("ip ip-prefix %s permit %s %d", $pfxname, $prefix[0], $prefix[1]); 611 | if ($prefix[0] == "0.0.0.0") { 612 | printf(" match-network"); 613 | } 614 | if ($prefix[1] < $pfxlen) 615 | printf(" le %s", $pfxlen); 616 | } 617 | 618 | elseif (_ip_version($line) == '6') 619 | { 620 | if( $prefix[1] > $pfxlen_v6 ) 621 | continue; 622 | 623 | printf("ip ipv6-prefix %s permit %s %d", $pfxname_v6, $prefix[0], $prefix[1]); 624 | if ($prefix[0] == "::") 625 | printf(" match-network"); 626 | 627 | if ($prefix[1] < $pfxlen_v6) 628 | printf(" le %s", $pfxlen_v6); 629 | } 630 | 631 | printf("\n"); 632 | } 633 | 634 | printf("return\n"); 635 | printf("save\n"); 636 | printf("y\n"); 637 | 638 | return 0; 639 | } 640 | 641 | ?> 642 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | IRRPT 2 | ====== 3 | 4 | ### Sponsored by TorIX - The Toronto Internet Exchange 5 | * Originally written by Richard A Steenbergen 6 | * Older versions can be found on sourceforge at [http://sourceforge.net/projects/irrpt/](http://sourceforge.net/projects/irrpt/) 7 | * IPv6 support added by Elisa Jasinska for version 2.0 8 | * Bug Fixes provided by Anna Claiborne for version 2.0 9 | 10 | 11 | Summary 12 | ------- 13 | 14 | A collection of tools which allow ISPs to easily track, manage, and 15 | utilize IPv4 and IPv6 BGP routing information stored in Internet Routing 16 | Registry (IRR) databases. Some of these tools include automated IRR data 17 | retrieval, update tracking via CVS, e-mail notifications, e-mail based 18 | notification for ISPs who still do human processing of routing information, 19 | and hooks for automatically deploying prefix-lists on routers. 20 | 21 | 22 | Purpose 23 | ------- 24 | 25 | Internet Routing Registry (IRR) services have existed in various forms for 26 | some time, yet many ISPs (particularly outside of the European/RIPE 27 | region) have still not adopted it. There are a variety of reasons given, 28 | but some of the most important ones include: 29 | 30 | 1. The system is overly complicated, and lacks sufficient examples. End 31 | users can not figure it out, which means another layer of support 32 | structure must be added, or proxy registration must be implemented. 33 | 2. A publicly accessable description of every import and export policy 34 | to every transit, peer, and customer, is difficult to maintain, and 35 | is not in the best business interests of many ISPs. 36 | 3. There are no existing tools which provides registration change 37 | tracking. Without this kind of tracking, there is not enough 38 | accountability for prefix registrations, and router configuration 39 | updates are difficult to manage. 40 | 41 | This tool takes a pragmatic approach, by focusing on the key elements and 42 | critical goal of the IRR, in order to provide simple and effective prefix- 43 | list management for the masses. To that end, IRR PowerTools aims to 44 | provide the following functionality: 45 | 46 | * Automated retrieval of prefixes registered behind an IRR Object. 47 | * Automatic exclusion of bogon or other configured undesirable routes. 48 | * Tracking and long-term recording of prefix changes through CVS. 49 | * Automatic aggregation to optimize data and reduce unnecessary changes. 50 | * E-mail updates, letting users know that their change was processed. 51 | * E-mail alerts to the ISP, letting them know of new routing changes. 52 | * Exporting of change data in e-mail form, for non-IRR using ISPs. 53 | * Router config generation, for easy automated config deployment. 54 | 55 | While many ISPs still rely on error-prone e-mail and human processing to 56 | handle prefix-lists, a few have developed systems similar to this one in 57 | order to automate their prefix-list management. Unfortunately each of 58 | these systems is proprietary and owned by the respective ISPs, leaving 59 | other networks with a choice between spending money and man-hours writing 60 | duplicating this work, or more often taking the path of least resistance 61 | and simply using e-mails and human processing. 62 | 63 | This project aims to provide to every network the same features that 64 | individual ISPs have been developing internally for years. By making it 65 | easier for every ISP to effectively utilize the IRR data, we hope to 66 | increase the popularity of the IRR as a prefix-list management tool, which 67 | will hopefully lead to more accurate data and a more effective system. 68 | 69 | 70 | Requirements 71 | ------------ 72 | 73 | * PHP >= 7.2.0 (http://www.php.net/downloads.php) 74 | * CVS Optional (http://www.gnu.org/software/cvs/) 75 | * CVSweb Optional (http://people.freebsd.org/~scop/cvsweb/) 76 | 77 | 78 | Installation 79 | ------------- 80 | 81 | Make sure you have the required packages as listed above installed. Clone 82 | the git respository in your desired install path via: 83 | https://github.com/6connect/irrpt.git 84 | Or, download the zip file and extract. 85 | Run 'php configure.php' in the base installation directory. 86 | 87 | To get started, you probably want to take a look at every file in 88 | the /conf directory. The most important information to change will be 89 | paths and company-specific information such as the name, ASN, and 90 | e-mail addresses. 91 | 92 | irrpt.conf - This is the master config file which contains global 93 | configuration information, the paths to our internal 94 | locations and external tools, and internal parameters. 95 | 96 | irrdb.conf - This is the second most important config file, 97 | containing a list of the ASNs and IRR Objects you wish 98 | to track. 99 | 100 | nag.conf - This file contains the settings for the nag process. 101 | 102 | nag.msg - This file contains the message that will be sent 103 | during the nag process. 104 | 105 | exclusions.conf - This file lists routes which can not be registered. A 106 | good example for this file would be known bogon routes, 107 | or routes in known unallocated space. 108 | 109 | The irrdb.conf file should certain a unique ASN, the IRR object (an 110 | AS-SET or AUT-NUM record) that you are interested in tracking, and a 111 | contact e-mail for change notification. You probably want to track your 112 | own ASN and AS-SET record here as well. 113 | 114 | A reasonable deployment would be to crontab the fetch process once or 115 | better yet twice a day. If you need to add a new customer outside of 116 | the normal fetch schedule, or if a customer needs an emergency 117 | prefix-list update, you can add the ASN/IRR Object to irrdb.conf and 118 | run a manual pull of just that ASN, with "./fetch ASN". 119 | 120 | After the updates are processed, you should receive a local copy of the 121 | e-mail. It is probably reasonable to keep a human being in the loop 122 | between prefix fetching and prefix deployment, to make certain that 123 | nothing "bad" or unintended is happening. After you are reasonably 124 | certain that the changes are ready for deployment, you can generate 125 | the router configs use the "pfxgen" tool. Optionally, you can nag 126 | any of your transit providers who still require e-mail updates using 127 | the nag process. 128 | 129 | If there is anything else that you can't figure out, it is probably 130 | either a bug or an oversight in the documentation. Send e-mail about 131 | either one, and I'll make certain it gets addressed in a future 132 | release. 133 | 134 | 135 | Documentation 136 | ------------- 137 | 138 | The operation of IRR PowerTools is broken down into the following distinct 139 | operations: 140 | 141 | ### irrpt_fetch 142 | 143 | $ bin/irrpt_fetch -h 144 | Usage: bin/irrpt_fetch [-h46qv] [-f file] [--nocvs] [object] 145 | 146 | Quiet mode: 147 | 148 | $ bin/irrpt_fetch -q 42 149 | ... 150 | 151 | Verbose mode: 152 | 153 | $ bin/irrpt_fetch -v 42 154 | ... 155 | 156 | irrpt_fetch uses the list of objects provided by irrdb.conf by 157 | default. To track additional objects, simply add them to the 158 | irrdb.conf file. To fetch objects for a specific AS, or specific 159 | object, provide this as an argument per the above example. 160 | 161 | This is the stage where you pull data for the objects that you are 162 | tracking off of the IRR, and store them locally. Inside the fetch 163 | process, the following steps are performed: 164 | 165 | 1. Query a IRR whois server for prefixes behind the IRR object. 166 | 2. Match prefixes (including more-specifics) against the Exclusions 167 | list, which contains prefixes that can not be registered. 168 | 3. Store the approved prefixes locally. 169 | 4. Run the prefixes through an aggregation tool to optimize them. 170 | 5. Track changes to both the raw and aggregated prefix lists via CVS. 171 | 6. When changes are detected, send out notification e-mails to 172 | customers and optionally a local copy to your operations staff, 173 | alerting everyone that the routing change has been successfully 174 | processed. 175 | 176 | ### irrpt_nag 177 | 178 | $ bin/irrpt_nag -h 179 | Usage: bin/irrpt_nag [-hp] [-c config] [-m message] 180 | 181 | Options: 182 | -p Preview mode (for diagnostic use). Print results to screen instead of 183 | e-mail. 184 | 185 | In this stage, any transit providers (or other interested parties) 186 | who still track prefix-list updates via e-mail rather than via IRR 187 | can be notified of the change. 188 | 189 | As more providers begin to use IRR, and rely on it as their primary 190 | prefix tracking tool, this should become less and less necessary. 191 | Unfortunately many of the largest ISPs still use human processing of 192 | prefix lists, so for the immediate future you will probably still be 193 | getting a lot of use out of this tool. 194 | 195 | ### irrpt_pfxgen 196 | 197 | $ bin/irrpt_pfxgen -h 198 | Usage: bin/irrpt_pfxgen [-h46] [-p pfxstr] [-p6 pfxstr_v6] [-l pfxlength] [-l6 pfxlength_v6] [-f format] 199 | pfxstr - The prefix-list name format string (default: CUSTOMER:%d) 200 | pfxstr_v6 - The prefix-list name format string (default: CUSTOMERv6:%d) 201 | pfxlength - The max length more-specific that will be allowed (default: 24) 202 | pfxlength_v6 - The max length more-specific that will be allowed for v6 (default: 48) 203 | format - The output format for a specific router type (default: cisco) 204 | Currently supported values are: 205 | cisco 206 | ciscoxr 207 | extreme 208 | foundry 209 | force10 210 | juniper 211 | edgeos 212 | huawei 213 | 214 | Examples: 215 | 216 | $ bin/irrpt_pfxgen -f cisco 42 217 | ... 218 | $ bin/irrpt_pfxgen -f juniper 42 219 | ... 220 | $ bin/irrpt_pfxgen -f extreme 42 221 | ... 222 | $ bin/irrpt_pfxgen -f force10 42 223 | ... 224 | $ bin/irrpt_pfxgen -f edgeos 42 225 | ... 226 | $ bin/irrpt_pfxgen -f huawei 42 227 | ... 228 | 229 | In this stage, actual router configurations are generated based on 230 | the aggregated data we have stored. Currently only the following 231 | formats are supported, but it should be trivial to add new ones: 232 | 233 | 1. Cisco/Foundry format (and anyone else with a similar CLI) 234 | 2. Juniper format 235 | 3. Extreme format 236 | 4. Force10 format 237 | 5. EdgeOS format (Vyos/Vyatta should also work) 238 | 6. Huawei format 239 | 7. Openbgpd format 240 | 8. CiscoXR / iosxr format 241 | 242 | These configs can then be deployed automatically using a variety of 243 | existing tools. Some of these tools include: 244 | 245 | * JUNOScript - http://www.juniper.net/support/junoscript/ 246 | * JunOS PyEZ - https://www.juniper.net/documentation/en_US/day-one-books/topics/topic-map/how-to-automatically-update-prefix-lists.html 247 | * Net::Telnet::Cisco - http://NetTelnetCisco.sourceforge.net/ 248 | * Oxidized - https://github.com/ytti/oxidized-script 249 | 250 | Networks may find it appropriate to have an external database in place 251 | which tracks customer BGP session data, so that you can search by a 252 | customer name or ASN to automatically find and update only the necessary 253 | routers and BGP sessions. This is also outside the scope of the current 254 | project. 255 | 256 | Juniper includes an example config pushing script in the JUNOSCript 257 | package which works well, despite being a little complicated and having 258 | something like 68 Perl dependancies. 259 | 260 | Cisco can be configured a number of ways, including various perl 261 | modules, expect scripts. Due to issues like CLI interactivity delays, 262 | many users find that simply copying config updates via tftp is an easy 263 | way to manage changes. 264 | 265 | Many other systems exist as well. 266 | 267 | ### irrpt_list_prefixes 268 | 269 | Show prefixes for a given AS or AS-SET, in unaggregated or aggregated form. 270 | 271 | $ bin/irrpt_list_prefixes -h 272 | Usage: bin/irrpt_list_prefixes [-h46va] 273 | 274 | Pull unaggregated prefixes: 275 | 276 | $ bin/irrpt_list_prefixes AS-PCH 277 | 2a01:8840:4::/48 278 | 2a01:8840:5::/48 279 | ... 280 | 281 | Pull aggregated prefixes: 282 | 283 | $ bin/irrpt_list_prefixes -a AS-PCH 284 | 2a01:8840:4:2020:2020:2020:2020:2020/47 285 | ... 286 | 287 | Verbose mode: 288 | 289 | $ bin/irrpt_list_prefixes -v -a AS-PCH 290 | ... 291 | - Aggregating routes - aggregating neighboring prefixes... 292 | * WARNING: Aggregating 2a01:8840:0004:0000:0000:0000:0000:0000/48 and 2a01:8840:0005:0000:0000:0000:0000:0000/48 into 2a01:8840:4:2020:2020:2020:2020:2020/47 293 | ... 294 | 295 | 296 | ### irrpt_list_ases 297 | 298 | Show AS numbers for a given AS-SET. 299 | 300 | $ bin/irrpt_list_ases -h 301 | Usage: bin/irrpt_list_ases [-h46] 302 | 303 | Example: 304 | 305 | $ bin/irrpt_list_ases AS-PCH 306 | AS-PCH 307 | { 308 | AS-RS 309 | { 310 | AS-CHEREDA-SM 311 | { 312 | AS-RS (dup) 313 | AS197058 314 | AS21312 315 | ... 316 | 317 | 318 | FAQ 319 | --- 320 | 321 | Q) Wouldn't this be more scalable if we stored our config and data in SQL? 322 | 323 | A) Maybe it would. However, the purpose of this project is to open up the 324 | world of automated IRR-based prefix-lists to every ISP. As such, it is 325 | designed to be as simple as possible, with the fewest complex external 326 | dependencies. A well organized network who maintains customer BGP info 327 | in an existing database should be able to easily export their database 328 | to our config format. It's the disorganized networks we really have to 329 | worry about. :) If you are dealing with so many prefixes that text 330 | files are too slow, email one of the maintainers. 331 | 332 | Q) Does this tool support RPSLng? 333 | 334 | A) Not right now. This may change in the future (see the TODO file). Feel 335 | free to let me know your strong interest (or lack thereof) on this issue. 336 | 337 | Q) Does this tool generate AS-PATH filters? 338 | 339 | A) No. Please note that this is not intended as an end-all and be-all 340 | RPSL tool, or a total replacement for a utility like IRRToolSet. For 341 | example, this tool does not try to parse aut-num import/export policies, 342 | try to map every relationship between ASNs, try to write your entire 343 | route-map for you. This is a pragmatic tool for helping ISPs easily do 344 | what 95% of them need to do, namely handle prefixes registered behind a 345 | specific IRR aut-num, route-set, or as-set object. Trying to support 346 | every possible item which you can document with RPSL is best left to 347 | academians and the pedantic. 348 | 349 | 350 | Long Term Goals 351 | --------------- 352 | 353 | We understand that this is only one step in the direction of making IRR 354 | truly and globally useful, but hopefully it will prove to be an important 355 | one. The next logical step towards improving the overall user experience 356 | would be to design an intuitive web interface for managing records in the 357 | IRR, so that BGP speaking end-users are not required to learn the 358 | complexities of RPSL in order to register their routes. 359 | 360 | Hopefully this tool will prove to be useful to a variety of ISPs, and will 361 | increase the adoption of IRR data over error-prone e-mail updates. When 362 | more ISPs can make use of this data, and when more end-users are able to 363 | easily make use of the system, there will hopefully be an increase in 364 | accurate and maintained data and a reduction in the need to pollute the 365 | IRR with proxy-registered junk. 366 | 367 | More in the TODO file. 368 | 369 | 370 | Thanks 371 | ------ 372 | 373 | Joe Abley for the very useful aggregate tool which was used in initial 374 | versions, Chris Morrow for nagging me into releasing this publicly, and all 375 | the folks in the ChangeLog who helped pick out the bugs. 376 | 377 | A special thanks to Jon Nistor for many many rounds of QA for version 2.0. 378 | 379 | Change Log 380 | ------- 381 | 382 | 2.1 - Jun 2021 (release pending) 383 | 384 | * Add: pfxgen added support for iosXR 385 | * Add: pfxgen added support for Ubiquity EdgeOS (@mikenowak) 386 | * Add: pfxgen added support for openbgpd 387 | * Add: pfxgen added support for Huawei (@miuvlad) 388 | * Enh: more help options 389 | * Enh: AS Object name is now fetched when run via the CLI by specifying a single AS number 390 | * Fix: undefined offset in aggreagte.inc 391 | * Fix: cosmetic issue on completed notices 392 | * Fix: remove Cisco prefix-list associated with proper address family 393 | * Fix: prefix lengths not properly checked (juniper, iosxr, f10, extreme) 394 | * Fix: Force10 pfxgen syntax issue 395 | * Fix: Email notifications comparing full routes and full agg file (@gawul) 396 | * Fix: iosXR pfxgen generates blank entry for prefixes not matching length (@bonev) 397 | * Fix: PHP 7.2.0 behaviour change using count(), fixed errors under Juniper pfxgen 398 | * Fix: Mask php errors for connection timeout, report proper host timeout. 399 | * Fix: reduced connection timeout to 15s, down from 30s. 400 | * Fix: typo in iosxr/ciscoxr (@Bierchermuesli) 401 | * Fix: last line of JunOS generation did not have a carriage return (@rlhennig) 402 | * Fix: Not having CVS no longer bails on initial setup. 403 | * Fix: v6 addresses are all now in compressed format when processing. 404 | * Fix: TCP reconnect back to whois server if connection drops (@tallwireless) 405 | * Fix: irrpt_list_ases did not include ASNs which had a single prefix 406 | * Fix: readme cleanup 407 | * Fix: Removed extra carriage return in concatenated files 408 | * Chg: Required PHP version bumped from 5.4 to 7.2 409 | 410 | 411 | 2.0 - Aug / Sep / Oct 2015 412 | 413 | Changes for version 2.0 by Anna Claiborne : 414 | 415 | * Tagging new version as 2.0 416 | * Removed system calls from configure.php 417 | * Updated configure.php print out to be more easily parsible/readable 418 | * Updated readme docs for version 2.0 419 | * Created configure.php for initial setup and cvs directory restore 420 | * Fixed Force10 prefix list syntax in pfxgen 421 | * Fixed bugs found in running with PHP 7 422 | * Removed extra and eroneous space from generated emails 423 | * Fixed irrpt_fetch to check file ownership before attempting permission 424 | changes 425 | * Removed calls to system to concatenate v4/v6 route files. Now performed by 426 | php function in utils.inc 427 | * Provided support to leave email in irrdb.conf blank if the user wishes no 428 | email updates for a particular as/object 429 | * Fixed support for separate (correct) v4 and v6 prefix list for Juniper config. 430 | * Added AS validation/checking for pfxgen 431 | 432 | Changes for version 2.0 by Elisa Jasinska : 433 | 434 | * Added v6 support to IRR query, to prefix exclusion via 435 | exclusions.conf, to aggregation and to the prefix generator 436 | * Implemented aggregate functionality and removed dependency on the 437 | external agregate tool 438 | * -4 and -6 switches for all command line tools 439 | * Renamed irrpt_eval and irrpt_explorer into irrpt_list_ases 440 | and irrpt_list_prefixes 441 | * Added -f option to provide location to irrdb.conf file 442 | * Added --nocvs option to omit cvs tracking 443 | * Bug fix for irrpt_list_ases with -6/-4 444 | * Improved as number vs as string handling in irrpt_fetch 445 | * Updated config for correct default cvs path 446 | * Improved ioverall as/as-set input format checking 447 | * Changed irrpt_list_prefixes to provide aggregated v6 routes in compressed 448 | form instead of expanded 449 | * Added command lines options for seperate v4 and v6 perfix list names 450 | * Added warning when 0 routes found for AS 451 | * Added v4/v6 command line switches to irrpt_list_ases 452 | * Better input validation for AS numbers and AS Sets as well as case sensitivity issues 453 | resolved 454 | 455 | 1.28 - June 8 2015 456 | 457 | * Making the latest version compatible with TORIX changes, such as: -q quiet 458 | mode, timezone support and memory limit support by Elisa Jasinska 459 | . 460 | 461 | 1.27 - Feb 8 2008 462 | 463 | * Redesigned (and simplified) the CVS diff code. The options are now 464 | "fulldiff" (basically the complete output of a normal CVS diff, that 465 | you could actually use to patch a file), "plusminus" (stripped down 466 | to only a list of prefix changes), and "english" (aka "Cogent Mode", 467 | for NOCs who can't quite figure out what the + and - symbols might 468 | mean). This was supposed to have been done back in 1.20, but this 469 | version actually makes it work. 470 | 471 | 1.26 - June 8 2007 472 | 473 | * Added support for Force10 prefix-list format. The style is JUST 474 | different enough from Cisco to be incompatible, you have to enter a 475 | hierarchy under the prefix-list declaration and then enter the permit 476 | lines seperately, rather than stating it all on one line. Thanks to 477 | Greg Hankins for pointing this out and providing the fix. 478 | 479 | * Added support for 32-bit (4-byte) ASNs. The #.# format (who's stupid 480 | idea was that anyways :P) was being detected as an IP address and the 481 | result of a route-set. Added an additional check to make it detect as 482 | an object to be be further queried, not that it will help since it 483 | seems the IRRd software (which is the only protocol we support, for 484 | various reasons) doesn't currently like importing this format either, 485 | but at least it won't be IRRPT's fault when it doesn't work. :) 486 | 487 | Thanks to: those wonderful euros who just had to actually go and try 488 | those new ASNs out ASAP, thus killing all the Farce10 and JUNOS 8.3R1 489 | boxes on the planet. Go go gadget incompatibility. :) 490 | 491 | 1.25 - May 27 2006 492 | 493 | * Added an option to automatically change uid/gid to a specified id if 494 | run as root. CVS will not let you run as root, so you should have a 495 | dedicated user anyways, but sometimes people forget. 496 | 497 | * Completely changed the way we process arguments, php getopt() sucks. 498 | 499 | * Fixed a plethoa of bugs in irrpt_nag. I don't know how you guys were 500 | even using this before, or why no one told me how broken this was 501 | before now. :) 502 | 503 | * Changed the default subject line of irrpt_nag, put the ASN info in 504 | the front for a quicker read on narrow terminals. 505 | 506 | 507 | 1.24 - December 29 2005 508 | 509 | * Added an "ASN list" file which records the ASNs behind each object. This 510 | may be used to implement some kind of AS-PATH filtering in the future I 511 | suppose, though it really isn't the right tool for the job. Talked into 512 | this evilness by Jon Nistor . 513 | 514 | * Fixed a really silly mistake in the processing of "english" style output. 515 | 516 | * Added a new tool "irrpt_explorer", which queries and displays the 517 | contents of an AS-SET in a hierarchal and recursive format. 518 | 519 | * Removed some unnecessary code in irrpt_eval. 520 | 521 | 1.23 - November 18 2005 522 | 523 | * Changed $_SERVER['SCRIPT_FILENAME'] references to __FILE__ to work around 524 | some portability issues with certain PHP builds (like apparently, RedHat). 525 | Pointed out by Joshua Sahala . 526 | 527 | * Fixed the -p flag (preview mode) on irrpt_nag. It works better if you 528 | actually have "p" in getopt(). Pointed out by Christian Malo 529 | . 530 | 531 | 1.22 - November 8 2005 532 | 533 | * Fixed irrpt_eval -a (aggregate) functionality. Pointed out by 534 | Jon Nistor . 535 | 536 | * Added support for ExtremeWare prefix generation. Submitted by Tom 537 | Hodgson. 538 | 539 | * A documentation tweak noting that PHP >= 4.3 is required. Submitted 540 | by Tom Hodgson. 541 | 542 | * Added a note regarding Debian's "aggregate" package to the README. 543 | 544 | * Got a little carried away getting rid of Nistor's strict errors. Rolling 545 | some things back to continue supporting php 4, easier solution is just 546 | not to turn on E_STRICT. 547 | 548 | * Added some example scripts for router configuration deployment. Updated 549 | documentation to reflect these changes. 550 | 551 | * Nailed down a few bugs with caching results that had incorrect array 552 | indexes following sort and unique. Extensive troubleshooting by Jon 553 | Nistor . 554 | 555 | * Added a quick optimization to only parse the exclusions file once (well 556 | most of the time at any rate). 557 | 558 | * A couple of minor bugfixes and documentation changes from the previous 559 | release. 560 | 561 | 562 | 1.20 - November 7 2005 563 | 564 | * Added support for handling a route-set object, and revised the as-set code 565 | to be a little more generic/graceful while handling it. Issue noted by 566 | Jon Nistor . 567 | 568 | * Added a new tool "irrpt_eval" which returns a simple plain-text list of 569 | routes from an arbitrary IRR object, using the irrpt query engine. This 570 | can be useful for diagnostics/quick lookups, and is similar to the 571 | IRRToolSet tool "peval". Requested by Aaron Weintraub. 572 | 573 | * Fixed some class definitions to comply with php strict mode. Contributed 574 | by Jon Nistor . 575 | 576 | * When an IRR query fails, the query that resulted in the failure is now 577 | displayed (verbose mode required). Nagged to death by Jon Nistor 578 | . 579 | 580 | * Added a config option for controlling the e-mail updates which are sent 581 | when a route change is detected: 582 | 583 | irrpt.conf: ['fetch']['emailonchange'] 584 | 585 | User can now specify whether to send emails for updated detected in the 586 | "full" (unaggregated) route file, the "aggregated" route file, "both" 587 | (default), or "none". Requested by Jon Nistor 588 | 589 | * Added an option for "english" language diff format. Apparently the Cogent 590 | NOC can't figure out what "+" and "-" means, so "english" mode changes 591 | the output to "Add" and "Remove" (and is the new default). There is 592 | also an option to continue using + and - ("plusminus") but to remove the 593 | full "diff" headers. The old behavior is retained under the setting 594 | "fulldiff". Requested by Adam Rothschild. 595 | 596 | irrpt.conf: ['diff']['output_format'] 597 | 598 | * Added a new flag "-p" to irrpt_nag, which enables preview mode. In 599 | preview mode, the contents of the email(s) which would be sent are 600 | output to stdout instead of being emailed. This allows you to 601 | double-check the e-mails before actually sending them. Requested by 602 | Adam Rothschild. 603 | 604 | * Changed the format of ['pfxgen']['default_pfxname'], and renamed it to 605 | default_pfxstr. It is now handled as a printf()-style format string, with 606 | one argument (the ASN). For example, to generate "CUSTOMER:1234" use the 607 | string: "CUSTOMER:%d". Requested by Pierfrancesco Caci. 608 | 609 | * Updated the distributed (but commented out) example exclusions.conf, just 610 | incase someone decides to uncomment it without obtaining the current 611 | exclusions list themselves. 612 | 613 | * A bunch of small misc. changes in formatting and alignment. Largely 614 | requested by Jon Nistor 615 | 616 | 617 | 1.10 - December 29 2004 618 | 619 | * Added an optional local cache for prefixes queried from an aut-num 620 | record, under the assumption that many networks will have as-set 621 | records which contain overlapping aut-num records. This will increase 622 | memory usage a bit, but results in significantly faster queries from 623 | the IRR whois servers for those with a large number of IRRDB entries. 624 | Cache is enabled by default. Suggested by Arnold Nipper. 625 | 626 | * Fixed a bug in the default CVS files which would cause an error 627 | message when fetch is run from a directory other than the default 628 | "/usr/local/irrpt". 629 | 630 | * Commented out the default bogon routes in the distributed exclusions 631 | config file, so users must choose to explicitly enable it. Hopefully 632 | this will prevent the blind use of potentially out of date bogon 633 | information, and avoid unnecessary whining on mailing lists every 634 | time a RIR is allocated a new /8. 635 | 636 | 637 | 1.00 - December 26 2004 638 | 639 | * Initial public release. 640 | --------------------------------------------------------------------------------