From: Shivani Bhardwaj Date: Tue, 20 Feb 2024 16:22:38 +0000 (+0530) Subject: detect/port: create list of small port ranges X-Git-Tag: suricata-8.0.0-beta1~1678 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4ac2382f26e75d73b2ee23d7555cca616a8dfe92;p=thirdparty%2Fsuricata.git detect/port: create list of small port ranges Using the unique port points, create a list of small port ranges which contain the DetectPort objects and the designated SGHs found by finding the overlaps with the existing ports and copying the SGHs accordingly. Ticket 6792 Bug 6414 --- diff --git a/src/detect-engine-build.c b/src/detect-engine-build.c index a983e72e68..b9e283bcc4 100644 --- a/src/detect-engine-build.c +++ b/src/detect-engine-build.c @@ -1133,6 +1133,11 @@ int CreateGroupedPortListCmpCnt(DetectPort *a, DetectPort *b); #define RANGE_PORT 1 #define SINGLE_PORT 2 +typedef struct UniquePortPoint_ { + uint16_t port; /* value of the port */ + bool single; /* is the port single or part of a range */ +} UniquePortPoint; + /** * \brief Function to set unique port points. Consider all the ports * flattened out on one line, set the points that correspond @@ -1168,6 +1173,110 @@ static inline uint32_t SetUniquePortPoints( return size_list; } +/** + * \brief Function to set the *final* unique port points and save them + * for later use. The points are already sorted because of the way + * they have been retrieved and saved earlier for use at this point. + * + * \param unique_list List of the unique port points to be used + * \param size_unique_arr Number of unique port points + * \param final_arr List of the final unique port points to be created + */ +static inline void SetFinalUniquePortPoints( + const uint8_t *unique_list, const uint32_t size_unique_arr, UniquePortPoint *final_arr) +{ + for (uint32_t i = 0, j = 0; i < (UINT16_MAX + 1); i++) { + DEBUG_VALIDATE_BUG_ON(j > size_unique_arr); + if (unique_list[i] == RANGE_PORT) { + final_arr[j].port = (uint16_t)i; + final_arr[j++].single = false; + } else if (unique_list[i] == SINGLE_PORT) { + final_arr[j].port = (uint16_t)i; + final_arr[j++].single = true; + } + } +} + +/** + * \brief Function to create the list of ports with the smallest ranges + * by resolving overlaps and end point conditions. These contain the + * correct SGHs as well after going over the interval tree to find + * any range overlaps. + * + * \param de_ctx Detection Engine Context + * \param unique_list Final list of unique port points + * \param size_list Size of the unique_list + * \param it Pointer to the interval tree + * \param list Pointer to the list where final ports will be stored + * + * \return 0 on success, -1 otherwise + */ +static inline int CreatePortList(DetectEngineCtx *de_ctx, const uint8_t *unique_list, + const uint32_t size_list, SCPortIntervalTree *it, DetectPort **list) +{ + /* Only do the operations if there is at least one unique port */ + if (size_list == 0) + return 0; + UniquePortPoint *final_unique_points = + (UniquePortPoint *)SCCalloc(size_list, sizeof(UniquePortPoint)); + if (final_unique_points == NULL) + return -1; + SetFinalUniquePortPoints(unique_list, size_list, final_unique_points); + /* Handle edge case when there is just one unique port */ + if (size_list == 1) { + SCPortIntervalFindOverlappingRanges( + de_ctx, final_unique_points[0].port, final_unique_points[0].port, &it->tree, list); + } else { + UniquePortPoint *p1 = &final_unique_points[0]; + UniquePortPoint *p2 = &final_unique_points[1]; + uint16_t port = p1 ? p1->port : 0; // just for cppcheck + uint16_t port2 = p2->port; + for (uint32_t i = 1; i < size_list; i++) { + DEBUG_VALIDATE_BUG_ON(port > port2); + if ((p1 && p1->single) && p2->single) { + SCPortIntervalFindOverlappingRanges(de_ctx, port, port, &it->tree, list); + SCPortIntervalFindOverlappingRanges(de_ctx, port2, port2, &it->tree, list); + port = port2 + 1; + } else if (p1 && p1->single) { + SCPortIntervalFindOverlappingRanges(de_ctx, port, port, &it->tree, list); + port = port + 1; + } else if (p2->single) { + /* If port2 is boundary and less or equal to port + 1, create a range + * keeping the boundary away as it is single port */ + if ((port2 >= port + 1)) { + SCPortIntervalFindOverlappingRanges(de_ctx, port, port2 - 1, &it->tree, list); + } + /* Deal with port2 as it is a single port */ + SCPortIntervalFindOverlappingRanges(de_ctx, port2, port2, &it->tree, list); + port = port2 + 1; + } else { + if ((port2 > port + 1)) { + SCPortIntervalFindOverlappingRanges(de_ctx, port, port2 - 1, &it->tree, list); + port = port2; + } else { + SCPortIntervalFindOverlappingRanges(de_ctx, port, port2, &it->tree, list); + port = port2 + 1; + } + } + /* if the current port matches the p2->port, assign it to p1 so that + * there is a UniquePortPoint object to check other info like whether + * the port with this value is single */ + if (port == p2->port) { + p1 = p2; + } else { + p1 = NULL; + } + if (i + 1 < size_list) { + p2 = &final_unique_points[i + 1]; + port2 = p2->port; + } + } + } + /* final_unique_points array is no longer needed */ + SCFree(final_unique_points); + return 0; +} + static DetectPort *RulesGroupByPorts(DetectEngineCtx *de_ctx, uint8_t ipproto, uint32_t direction) { /* step 1: create a hash of 'DetectPort' objects based on all the @@ -1272,6 +1381,15 @@ static DetectPort *RulesGroupByPorts(DetectEngineCtx *de_ctx, uint8_t ipproto, u int r = DetectPortInsert(de_ctx, &list , tmp); BUG_ON(r == -1); } + /* Create a sorted list of ports in ascending order after resolving overlaps + * and corresponding SGHs */ + if (CreatePortList(de_ctx, unique_port_points, size_unique_port_arr, it, &list) < 0) + goto error; + + /* unique_port_points array is no longer needed */ + SCFree(unique_port_points); + + /* Port hashes are no longer needed */ DetectPortHashFree(de_ctx); SCLogDebug("rules analyzed");