diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8063cca --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +CXXFLAGS+= -std=c++23 +LDLIBS+= -lalepha +LDFLAGS+= -L Alepha/build-debug -Wl,-rpath,/home/user/proj/ipam/Alepha/build-debug +CPPFLAGS+= -I . + +all: test + +test: ipam + ./ipam diff --git a/README b/README new file mode 100644 index 0000000..c2b304f --- /dev/null +++ b/README @@ -0,0 +1,2 @@ +Basic text-interface IPAM program + diff --git a/ipam.cc b/ipam.cc new file mode 100644 index 0000000..e0b34ce --- /dev/null +++ b/ipam.cc @@ -0,0 +1,155 @@ +#include + +#include +#include + +#include +#include + +#include + +namespace asio= boost::asio; +namespace ip= asio::ip; + +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; + a= ip::make_network_v6( s ); + + return is; + } +}; + +template< typename > +struct Allocation_core +{ + ip::network_v6 network; + + std::string owner; + std::string name; + std::string purpose; + + 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" } ); + + add_command + ( + { + "help", + "List all commands", + []( ... ) + { + for( const auto &[ name, command ]: commands ) + { + std::cout << name << " - " << command.help << std::endl; + } + } + } + ); + + add_command + ( + { + "describe", + "Describe a network range", + []( const std::vector< std::string > &args ) + { + if( args.size() != 1 ) throw std::runtime_error{ "Too many arguments." }; + + const auto &network= boost::lexical_cast< IP_Address >( args.at( 0 ) ); + + const Allocation *p= nullptr; + + for( const auto &space: spaces ) + { + const auto &[ where, owner, name, purpose ]= space; + if( where == network ) + { + p = &space; + break; + } + } + const auto &[ _, owner, name, purpose ]= *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; + } + } + ); + + + + while( true ) + { + 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 ) ); + + commands.at( command ).run( args ); + } + + return EXIT_SUCCESS; +} +catch( const std::exception &ex ) +{ + std::cerr << "Error: " << ex.what() << std::endl; + return EXIT_FAILURE; +}