doc/developers/cpp_flows_sorting.md
The sorting process follows this path:
active_list.lua → getFlowsInfo() → NetworkInterface::getFlows() → NetworkInterface::sortFlows() → flow_search_walker()
scripts/lua/rest/v2/get/flow/active_list.lua)src/NetworkInterface.cpp)include/ntop_typedefs.h)include/Flow.h)Add new column enums in include/ntop_typedefs.h:
typedef enum {
// ... existing columns
column_score,
column_score_as_client,
column_score_as_server,
// NEW COLUMNS - Add here
column_cli_asn,
column_srv_asn,
column_transit_asn,
// ... rest of columns
} sortField;
If you need new data accessors, add them to include/Flow.h:
class Flow : public GenericHashEntry {
// new memers
u_int32_t srcAS, dstAS;
u_int32_t transitAS; // NEW FIELD
// new getters
u_int32_t getSrcAS() { return(srcAS); }
u_int32_t getDstAS() { return(dstAS); }
};
In scripts/lua/rest/v2/get/flow/active_list.lua, add mappings:
local mapping_column_lua_c = {
server = "column_server",
client = "column_client",
-- ... existing mappings
qoe = "column_qoe",
-- NEW MAPPINGS
cli_asn = "column_cli_asn",
srv_asn = "column_srv_asn",
transit_asn = "column_transit_asn"
}
In src/NetworkInterface.cpp, add cases to the flow_search_walker function:
static bool flow_search_walker(GenericHashEntry *h, void *user_data, bool *matched) {
Flow *f = (Flow*)h;
FlowSearcher *retriever = (FlowSearcher*)user_data;
// ... existing code
switch (retriever->sorter) {
// ... existing cases
// add new column cases
case column_cli_asn:
retriever->elems[retriever->actNumEntries++].numericValue = f->getSrcAS();
break;
case column_srv_asn:
retriever->elems[retriever->actNumEntries++].numericValue = f->getDstAS();
break;
case column_transit_asn:
retriever->elems[retriever->actNumEntries++].numericValue = f->getTransitAS();
break;
// ... other cases
}
}
In NetworkInterface::sortFlows(), add column string recognition:
int NetworkInterface::sortFlows(u_int32_t *begin_slot, bool walk_all,
bool a2zSortOrder, sortField field,
const char *sortColumn, /* ... */) {
// ... existing code
if (!strcmp(sortColumn, "column_duration"))
retriever->sorter = column_duration, sorter = numericSorter;
else if (!strcmp(sortColumn, "column_qoe"))
retriever->sorter = column_qoe, sorter = numericSorter;
// NEW SORT COLUMNS
else if (!strcmp(sortColumn, "column_cli_asn"))
retriever->sorter = column_cli_asn, sorter = numericSorter;
else if (!strcmp(sortColumn, "column_srv_asn"))
retriever->sorter = column_srv_asn, sorter = numericSorter;
else if (!strcmp(sortColumn, "column_transit_asn"))
retriever->sorter = column_transit_asn, sorter = numericSorter;
// ... rest of the function
}
The sorting order is controlled by p->a2zSortOrder():
// In NetworkInterface::getFlows()
if (p->a2zSortOrder()) {
// Ascending order (A to Z, 0 to 9)
for (int i = p->toSkip(), num = 0; i < (int)retriever.actNumEntries; i++) {
// Process sorted elements
}
} else {
// Descending order (Z to A, 9 to 0)
for (int i = (int)retriever.actNumEntries - 1 - p->toSkip(), num = 0; i >= 0; i--) {
// Process sorted elements in reverse
}
}
Choose the appropriate sorter type:
numericSorter: For numeric values (ASN, scores, durations, bytes)stringSorter: For string values (hostnames, protocols)ipSorter: For IP addressesThe flow_search_walker populates the retriever->elems array:
// For numeric values
retriever->elems[retriever->actNumEntries++].numericValue = someNumber;
// For string values
retriever->elems[retriever->actNumEntries++].stringValue = someString;
// For IP addresses
retriever->elems[retriever->actNumEntries++].ipValue = someIPAddress;
Following the commit example, here's how the Server ASN sorting was implemented:
column_srv_asn in ntop_typedefs.hsrv_asn = "column_srv_asn" in Luaf->getDstAS() as numeric value"column_srv_asn" string to enumcolumn_* for enums, *_asn/*_* for Lua keysinclude/ntop_typedefs.h - Column type definitionsinclude/Flow.h - Flow class and data accessorssrc/NetworkInterface.cpp - Core sorting logicscripts/lua/rest/v2/get/flow/active_list.lua - Frontend API mapping