diff --git a/src/callouts.cc b/src/callouts.cc index 12f6285fe23a16111e970dbda6d2437aa7f604b1..fc7d6c990be7d4808efd126f67a5eb78d48e5e11 100644 --- a/src/callouts.cc +++ b/src/callouts.cc @@ -6,26 +6,93 @@ #include <hooks/hooks.h> #include <pgsql/pgsql_connection.h> +#include <algorithm> #include <cstdlib> #include <string> +#include <tuple> #include "globals.h" #include "logger.h" +static std::tuple<int, std::string, std::string, std::string> parseOption82( + const isc::dhcp::Pkt4Ptr &query4_ptr) { + /* Get switch hostname and port name from DHCP option 82 + * https://www.cisco.com/c/en/us/td/docs/switches/lan/catalyst4500/12-2/15-02SG/configuration/guide/config/dhcp.html#57094 + */ + auto option82{query4_ptr->getOption(82)}; + if (!option82) { + LOG_FATAL(schmatrix_logger, SCHMATRIX_DHCP_OPTION_82_ERROR); + return std::make_tuple(1, nullptr, nullptr, nullptr); + } + + auto circuit_id{option82->getOption(1)->getData()}; + auto remote_id{option82->getOption(2)->getData()}; + + // Check suboption ID types + if (circuit_id[0] != 0 || remote_id[0] != 1) { + LOG_FATAL(schmatrix_logger, SCHMATRIX_DHCP_OPTION_82_ERROR); + return std::make_tuple(1, nullptr, nullptr, nullptr); + } + + std::string port_id{std::to_string(circuit_id[4]) + "/" + + std::to_string(circuit_id[5])}; + std::string switch_name{remote_id.begin() + 2, remote_id.end()}; + auto switch_id{g_switch_data.at(switch_name)}; + + return std::make_tuple(1, std::move(switch_name), std::move(port_id), + std::move(switch_id)); +} + extern "C" { // Check IP conflict int pkt4_receive(isc::hooks::CalloutHandle &handle) { isc::dhcp::Pkt4Ptr query4_ptr; handle.getArgument("query4", query4_ptr); - const std::string &mac_address{query4_ptr->getHWAddr()->toText(false)}; - const char *values[]{mac_address.c_str()}; - isc::db::PgSqlResult r(PQexecPrepared(*g_pg_sql_connection, "ip_conflict", 1, + // Skip non MUEB devices check + auto hwaddr_ptr = query4_ptr->getHWAddr(); + if (hwaddr_ptr->hwaddr_[0] != 0x54 || hwaddr_ptr->hwaddr_[1] != 0x10 || + hwaddr_ptr->hwaddr_[2] != 0xEC) { + return 0; + } + + const auto [result, switch_name, port_id, switch_id] = + parseOption82(query4_ptr); + const std::string &mac_address{hwaddr_ptr->toText(false)}; + const char *values[2] = {port_id.c_str(), switch_id.c_str()}; + + // Get room id + isc::db::PgSqlResult r(PQexecPrepared(*g_pg_sql_connection, "mueb_in_room", 2, values, nullptr, nullptr, 0)); - /* Drop packet - * MUEB with IP conflict needs manual fix + // Handle incorrect room + if (r.getRows() < 0) { + LOG_ERROR(schmatrix_logger, SCHMATRIX_UNKNOWN_ROOM) + .arg(mac_address) + .arg(switch_name) + .arg(port_id); + return 1; + } + + const auto room_id{PQgetvalue(r, 0, 0)}; + values[0] = mac_address.c_str(); + values[1] = room_id; + + // Drop packet if multiple MUEBs(excluding current) is in the same room + isc::db::PgSqlResult r2(PQexecPrepared(*g_pg_sql_connection, + "mueb_count_in_room", 2, values, + nullptr, nullptr, 0)); + if (r.getRows() > 0 && std::atoi(PQgetvalue(r, 0, 0)) > 0) { + LOG_WARN(schmatrix_logger, SCHMATRIX_MULTIPLE_MUEB).arg(room_id); + handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP); + return 0; + } + + isc::db::PgSqlResult r3(PQexecPrepared(*g_pg_sql_connection, "ip_conflict", 1, + values, nullptr, nullptr, 0)); + /* Drop packet when a MUEB has IP conflict + * Needs manual fix */ - if (r.getRows() > 0 && atoi(PQgetvalue(r, 0, 0)) > 0) { + if (r.getRows() > 0 && std::atoi(PQgetvalue(r, 0, 0)) > 0) { handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP); } @@ -60,23 +127,11 @@ int lease4_select(isc::hooks::CalloutHandle &handle) { // Critical part begin, it's not safe to lease IP yet handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_SKIP); - // Get switch hostname and port name from DHCP option 82 - // https://www.cisco.com/c/en/us/td/docs/switches/lan/catalyst4500/12-2/15-02SG/configuration/guide/config/dhcp.html#57094 - auto option82{query4_ptr->getOption(82)}; - auto circuit_id{option82->getOption(1)->getData()}; - auto remote_id{option82->getOption(2)->getData()}; - - // Check suboption ID types - if (circuit_id[0] != 0 || remote_id[0] != 1) { - LOG_FATAL(schmatrix_logger, SCHMATRIX_DHCP_OPTION_82_ERROR); + auto [result, switch_name, port_id, switch_id] = parseOption82(query4_ptr); + if (result != 0) { return 1; } - std::string port_id{std::to_string(circuit_id[4]) + "/" + - std::to_string(circuit_id[5])}; - std::string switch_name{remote_id.begin() + 2, remote_id.end()}; - auto switch_id{g_switch_data.at(switch_name)}; - // Check for IP override const char *values[3] = {mac_address.c_str()}; isc::db::PgSqlResult r(PQexecPrepared(*g_pg_sql_connection, "ip_override", 1, diff --git a/src/framework_functions.cc b/src/framework_functions.cc index 386c905fa2185efd188e3fb2f77bc5a4862d2a72..1f7d7e7e7e670723d3199ebd10962caf819dcf69 100644 --- a/src/framework_functions.cc +++ b/src/framework_functions.cc @@ -42,15 +42,26 @@ int load(isc::hooks::LibraryHandle& handle) { } // Store prepared statements - std::array<isc::db::PgSqlTaggedStatement, 6> statements{ + std::array<isc::db::PgSqlTaggedStatement, 8> statements{ {{1, {isc::db::OID_TEXT}, "ip_conflict", "select ip_conflict::int from mueb where mac_address = $1::macaddr"}, + {2, + {isc::db::OID_TEXT, isc::db::OID_TEXT}, + "mueb_in_room", + "select room_id from port p join room r using(room_id) where p.port_id " + "= $1 and p.switch_id = $2::inet"}, + {2, + {isc::db::OID_TEXT, isc::db::OID_TEXT}, + "mueb_count_in_room", + "select count(*) from port p join mueb m using(port_id, switch_id) " + "where m.mac_address::macaddr != $1 and p.room_id = $2"}, {1, {isc::db::OID_TEXT}, "ip_override", - "select ip_override from mueb where mac_address = $1::macaddr and ip_override " + "select ip_override from mueb where mac_address = $1::macaddr and " + "ip_override " "is not null"}, {2, {isc::db::OID_TEXT, isc::db::OID_TEXT}, @@ -65,8 +76,10 @@ int load(isc::hooks::LibraryHandle& handle) { {3, {isc::db::OID_TEXT, isc::db::OID_TEXT, isc::db::OID_TEXT}, "insert_mueb", - "insert into mueb (mac_address) values ($1::macaddr) on conflict (mac_address) do update set " - "port_id = $2, switch_id = $3::inet where mueb.mac_address = $1::macaddr"}, + "insert into mueb (mac_address) values ($1::macaddr) on conflict " + "(mac_address) do update set " + "port_id = $2, switch_id = $3::inet where mueb.mac_address = " + "$1::macaddr"}, {1, {isc::db::OID_TEXT}, "set_ip_conflict", diff --git a/src/messages.cc b/src/messages.cc index 79375a2dc640cd8677d91203bf99265483fb9441..15908e9e024d44f14dc49ffd2c77f588742f3568 100644 --- a/src/messages.cc +++ b/src/messages.cc @@ -1,4 +1,4 @@ -// File created from messages.mes on Sun Aug 08 2021 19:54 +// File created from messages.mes on Thu Sep 02 2021 20:36 #include <cstddef> #include <log/message_types.h> @@ -10,6 +10,7 @@ extern const isc::log::MessageID SCHMATRIX_DHCP_STATE = "SCHMATRIX_DHCP_STATE"; extern const isc::log::MessageID SCHMATRIX_IP_CONFLICT = "SCHMATRIX_IP_CONFLICT"; extern const isc::log::MessageID SCHMATRIX_IP_OVERRIDDEN = "SCHMATRIX_IP_OVERRIDDEN"; extern const isc::log::MessageID SCHMATRIX_MISSING_PARAMETERS = "SCHMATRIX_MISSING_PARAMETERS"; +extern const isc::log::MessageID SCHMATRIX_MULTIPLE_MUEB = "SCHMATRIX_MULTIPLE_MUEB"; extern const isc::log::MessageID SCHMATRIX_NOT_MUEB = "SCHMATRIX_NOT_MUEB"; extern const isc::log::MessageID SCHMATRIX_OPEN_DATABASE = "SCHMATRIX_OPEN_DATABASE"; extern const isc::log::MessageID SCHMATRIX_QUERIED_IP = "SCHMATRIX_QUERIED_IP"; @@ -24,6 +25,7 @@ const char* values[] = { "SCHMATRIX_IP_CONFLICT", "Could not lease IP: %1 to MUEB: %2 because of IP conflict", "SCHMATRIX_IP_OVERRIDDEN", "MUEB: %1 IP's overridden to: %2", "SCHMATRIX_MISSING_PARAMETERS", "Not all parameters are provided", + "SCHMATRIX_MULTIPLE_MUEB", "Multiple MUEBs in the same room %1", "SCHMATRIX_NOT_MUEB", "Device with MAC: %1 is not a MUEB", "SCHMATRIX_OPEN_DATABASE", "Opened database successfully", "SCHMATRIX_QUERIED_IP", "Queried IP is: %1", diff --git a/src/messages.h b/src/messages.h index 0f763981a49a0c0e4aaab30230944af97559e028..c7113e9344b7cb40be115e3e08aa6ac30eddb035 100644 --- a/src/messages.h +++ b/src/messages.h @@ -1,4 +1,4 @@ -// File created from messages.mes on Sun Aug 08 2021 19:54 +// File created from messages.mes on Thu Sep 02 2021 20:36 #ifndef MESSAGES_H #define MESSAGES_H @@ -11,6 +11,7 @@ extern const isc::log::MessageID SCHMATRIX_DHCP_STATE; extern const isc::log::MessageID SCHMATRIX_IP_CONFLICT; extern const isc::log::MessageID SCHMATRIX_IP_OVERRIDDEN; extern const isc::log::MessageID SCHMATRIX_MISSING_PARAMETERS; +extern const isc::log::MessageID SCHMATRIX_MULTIPLE_MUEB; extern const isc::log::MessageID SCHMATRIX_NOT_MUEB; extern const isc::log::MessageID SCHMATRIX_OPEN_DATABASE; extern const isc::log::MessageID SCHMATRIX_QUERIED_IP; diff --git a/src/messages.mes b/src/messages.mes index 7ff3eb3ff8df69dcd09b687fbbf1bbea799cb920..d1e174f1af8cd49f602b4fd43e8f651a0f763bab 100644 --- a/src/messages.mes +++ b/src/messages.mes @@ -17,3 +17,5 @@ % SCHMATRIX_IP_OVERRIDDEN MUEB: %1 IP's overridden to: %2 % SCHMATRIX_DHCP_OPTION_82_ERROR Invalid DHCP option 82 + +% SCHMATRIX_MULTIPLE_MUEB Multiple MUEBs in the same room %1