Files
ipam/ipam.cc
ADAM David Alan Martin fd17f4efab Add in the IMP-NET addresses.
I'll merge master into this, periodically?
2025-05-05 12:13:08 -04:00

315 lines
7.8 KiB
C++

#include <cstddef>
#include <iostream>
#include <exception>
#include <boost/lexical_cast.hpp>
#include <boost/asio/ip/network_v6.hpp>
#include <Alepha/IOStreams/Streamable.h>
#include <Alepha/IOStreams/String.h>
namespace asio= boost::asio;
namespace ip= asio::ip;
using Alepha::IOStreams::Str;
template< typename= Alepha::Capabilities< Alepha::IOStreams::Streamable > >
struct Allocation_core;
using Allocation= Allocation_core<>;
struct IP_Address : ip::network_v6
{
using ip::network_v6::network_v6;
IP_Address( const network_v6 &n ) : ip::network_v6( n ) {}
friend std::istream &
operator >> ( std::istream &is, IP_Address &a )
{
std::string s;
is >> s;
std::cerr << "Network string: " << s << std::endl;
if( not s.contains( '/' ) )
{
throw std::runtime_error{ "No subnet mask detected. Did you forget a `/`?" };
}
a= ip::make_network_v6( s );
std::cerr << "Parsed into: `" << a << std::endl;
return is;
}
};
template< typename >
struct Allocation_core
{
IP_Address network;
std::string owner;
std::string name;
std::string purpose;
std::string country;
std::string region;
std::string city;
std::string postal;
std::vector< Allocation > subAllocations() const;
std::vector< Allocation > available() const;
};
std::vector< Allocation > spaces;
template<>
inline std::vector< Allocation >
Allocation::subAllocations() const
{
std::vector< Allocation > rv;
for( const auto &alloc: spaces )
{
if( alloc.network.is_subnet_of( network ) ) rv.push_back( alloc );
}
return rv;
}
struct Command
{
std::string name;
std::string help;
std::function< void ( std::vector< std::string > ) > run;
};
std::map< std::string, Command > commands;
void
add_command( Command command )
{
commands.insert( { command.name, command } );
}
int
main()
try
{
spaces.push_back( { ip::make_network_v6( "2000::/3" ), "IANA", "Global Routing - v6", "The initial global routing table for v6" } );
spaces.push_back( { ip::make_network_v6( "2600::/8" ), "ARIN", "ARIN v6 RIR Allocation", "Large ARIN block.", "US" } );
spaces.push_back( { ip::make_network_v6( "2602:f6a8::/36" ), "IMP-NET", "Imperial Network", "Main Imperial Network Allocation.", "US", "US-PA", "Bryn Athyn", "19009" } );
spaces.push_back( { ip::make_network_v6( "2602:f6a8::/45" ), "IMP-NET", "Imperial Network", "North Philly Region", "US", "US-PA", "Philadelphia" } );
spaces.push_back( { ip::make_network_v6( "2602:f6a8:8::/46" ), "IMP-NET", "Imperial Network", "Miami Region", "US", "US-FL", "Miami", } );
spaces.push_back( { ip::make_network_v6( "2602:f6a8:20::/44" ), "IMP-NET", "Imperial Network", "Long Island Region", "US", "US-NY", "Yaphank", "11780", } );
add_command
(
{
"help",
"List all commands",
[]( ... )
{
for( const auto &[ name, command ]: commands )
{
std::cout << name << " - " << command.help << std::endl;
}
}
}
);
add_command
(
{
"quit",
"Exits the IP Address Manager",
[]( ... )
{
::exit( EXIT_SUCCESS );
}
}
);
add_command
(
{
"describe",
"Describe a network range",
[]( const std::vector< std::string > &args )
{
if( args.empty() ) throw std::runtime_error{ "Requires one argument." };
if( args.size() != 1 ) throw std::runtime_error{ "Too many arguments." };
const auto &network= boost::lexical_cast< IP_Address >( args.at( 0 ) );
std::cout << "Parsed: `" << network << "` -- it has a " << network.prefix_length()
<< " bit mask." << std::endl;
const Allocation *p= nullptr;
std::optional< IP_Address > found;
auto better= [&]( const auto &where )
{
return not found.has_value() or where.is_subnet_of( found.value() );
};
for( const auto &space: spaces )
{
const auto &[ where, owner, name, purpose, country, state, city, postal ]= space;
std::cerr << "Checking " << network << " against: " << where;
if( network == where )
{
std::cerr << " -- Exact match!" << std::endl;
found= where;
p= &space;
break;
}
if( network.is_subnet_of( where ) and better( where ) )
{
std::cerr << " -- Matched." << std::endl;
found= where;
p = &space;
}
else std::cerr << " -- No match." << std::endl;
}
std::cout << network << " was best matched by " << found.value() << std::endl;
const auto &[ _, owner, name, purpose, country, state, city, postal ]= *p;
std::cout << network << " is owned by " << owner << std::endl;
std::cout << "It is called `" << name << "`" << std::endl;
std::cout << "It is used for '" << purpose << "'" << std::endl;
}
}
);
add_command
(
{
"subnets",
"Describe a network range's subnets",
[]( const std::vector< std::string > &args )
{
if( args.empty() ) throw std::runtime_error{ "Requires one argument." };
if( args.size() != 1 ) throw std::runtime_error{ "Too many arguments." };
const auto &network= boost::lexical_cast< IP_Address >( args.at( 0 ) );
std::cout << "Parsed: `" << network << "` -- it has a " << network.prefix_length()
<< " bit mask." << std::endl;
const Allocation *p= nullptr;
std::optional< IP_Address > found;
auto better= [&]( const auto &where )
{
return not found.has_value() or where.is_subnet_of( found.value() );
};
for( const auto &space: spaces )
{
const auto &[ where, owner, name, purpose, country, state, city, postal ]= space;
if( network.is_subnet_of( where ) and better( where ) )
{
found= where;
p = &space;
}
}
std::cout << network << " was best matched by " << found.value() << std::endl;
const auto &[ _, owner, name, purpose, country, state, city, postal ]= *p;
std::cout << network << " is owned by " << owner << std::endl;
std::cout << "It is called `" << name << "`" << std::endl;
std::cout << "It is used for '" << purpose << "'" << std::endl;
for( const auto &sub: p->subAllocations() )
{
std::cout << sub.network << ": " << owner << std::endl;
}
}
}
);
add_command
(
{
"geofeed",
"Describe a network's various physical locations.",
[]( const std::vector< std::string > &args )
{
if( args.empty() ) throw std::runtime_error{ "Requires one argument." };
if( args.size() != 1 ) throw std::runtime_error{ "Too many arguments." };
const auto &network= boost::lexical_cast< IP_Address >( args.at( 0 ) );
std::cout << "Parsed: `" << network << "` -- it has a " << network.prefix_length()
<< " bit mask." << std::endl;
const Allocation *p= nullptr;
std::optional< IP_Address > found;
auto better= [&]( const auto &where )
{
return not found.has_value() or where.is_subnet_of( found.value() );
};
for( const auto &space: spaces )
{
const auto &[ where, owner, name, purpose, country, state, city, postal ]= space;
if( network.is_subnet_of( where ) and better( where ) )
{
found= where;
p = &space;
}
}
const auto &[ _, owner, name, purpose, country, state, city, postal ]= *p;
for( const auto &sub: p->subAllocations() )
{
std::cout << sub.network << "," << sub.country << "," << sub.region
<< "," << sub.city << "," << sub.postal
<< std::endl;
}
}
}
);
while( not std::cin.eof() )
try
{
std::string line;
getline( std::cin, line );
const auto parsed= Alepha::split( line, ' ' );
const auto &command= parsed.at( 0 );
auto args= parsed;
args.erase( begin( args ) );
if( command.empty() ) continue;
if( not commands.contains( command ) )
{
throw std::runtime_error{ Str << "No such command `" << command << '`' };
}
commands.at( command ).run( args );
}
catch( const std::exception &e )
{
std::cerr << "ERROR: " << e.what() << std::endl;
}
return EXIT_SUCCESS;
}
catch( const std::exception &ex )
{
std::cerr << "FATAL ERROR: " << ex.what() << std::endl;
return EXIT_FAILURE;
}