diff --git a/lib/core/ogs-sockaddr.c b/lib/core/ogs-sockaddr.c index c54a1f590..61ad459db 100644 --- a/lib/core/ogs-sockaddr.c +++ b/lib/core/ogs-sockaddr.c @@ -530,6 +530,32 @@ bool ogs_sockaddr_is_equal_addr(const void *p, const void *q) return ogs_sockaddr_compare(a, b, false); } +bool ogs_sockaddr_check_any_match( + ogs_sockaddr_t *base, + ogs_sockaddr_t *list, const ogs_sockaddr_t *single, bool compare_port) +{ + ogs_sockaddr_t *p = NULL; + + while (list) { + p = base; + while (p) { + if (ogs_sockaddr_compare(p, list, compare_port) == true) + return true; + p = p->next; + } + list = list->next; + } + if (single) { + p = base; + while (p) { + if (ogs_sockaddr_compare(p, single, compare_port) == true) + return true; + p = p->next; + } + } + return false; +} + static int parse_network(ogs_ipsubnet_t *ipsub, const char *network) { /* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */ diff --git a/lib/core/ogs-sockaddr.h b/lib/core/ogs-sockaddr.h index c4550878c..144255926 100644 --- a/lib/core/ogs-sockaddr.h +++ b/lib/core/ogs-sockaddr.h @@ -113,6 +113,10 @@ socklen_t ogs_sockaddr_len(const void *sa); bool ogs_sockaddr_is_equal(const void *p, const void *q); bool ogs_sockaddr_is_equal_addr(const void *p, const void *q); +bool ogs_sockaddr_check_any_match( + ogs_sockaddr_t *base, + ogs_sockaddr_t *list, const ogs_sockaddr_t *single, bool compare_port); + int ogs_ipsubnet(ogs_ipsubnet_t *ipsub, const char *ipstr, const char *mask_or_numbits); diff --git a/lib/pfcp/context.c b/lib/pfcp/context.c index 001274ee8..f84fbec44 100644 --- a/lib/pfcp/context.c +++ b/lib/pfcp/context.c @@ -864,42 +864,12 @@ int ogs_pfcp_context_parse_config(const char *local, const char *remote) return OGS_OK; } -/*-------------------------------------------------------------------------- - * check_any_match(): - * If any node in "list" or the single "single" matches base, return 1 - *-------------------------------------------------------------------------- - */ -static bool check_any_match(ogs_sockaddr_t *base, - ogs_sockaddr_t *list, - const ogs_sockaddr_t *single) -{ - ogs_sockaddr_t *p = NULL; - - while (list) { - p = base; - while (p) { - if (ogs_sockaddr_is_equal_addr(p, list)) - return true; - p = p->next; - } - list = list->next; - } - if (single) { - p = base; - while (p) { - if (ogs_sockaddr_is_equal_addr(p, single)) - return true; - p = p->next; - } - } - return false; -} - -/*-------------------------------------------------------------------------- - * ogs_pfcp_node_new(): - * Create node with config_addr, copy config_addr into node->addr_list - *-------------------------------------------------------------------------- - */ +/****************************************************************************** + * ogs_pfcp_node_new() + * - Initialize node_id.type to OGS_PFCP_NODE_ID_UNKNOWN + * - So the node can later be updated with a real Node ID via + * ogs_pfcp_node_merge() once we learn it from PFCP messages. + ******************************************************************************/ ogs_pfcp_node_t *ogs_pfcp_node_new(ogs_sockaddr_t *config_addr) { int rv; @@ -926,6 +896,12 @@ ogs_pfcp_node_t *ogs_pfcp_node_new(ogs_sockaddr_t *config_addr) } } + /* + * Initialize node->node_id to UNKNOWN, meaning "no Node ID assigned yet". + */ + memset(&node->node_id, 0, sizeof(node->node_id)); + node->node_id.type = OGS_PFCP_NODE_ID_UNKNOWN; + ogs_list_init(&node->local_list); ogs_list_init(&node->remote_list); @@ -948,12 +924,11 @@ void ogs_pfcp_node_free(ogs_pfcp_node_t *node) ogs_pool_free(&ogs_pfcp_node_pool, node); } -/*-------------------------------------------------------------------------- - * ogs_pfcp_node_add(): - * Create a new node (with config_addr=NULL), set node_id/from, - * then merge => finally add to list - *-------------------------------------------------------------------------- - */ +/****************************************************************************** + * ogs_pfcp_node_add() + * - Create a new PFCP node, then call ogs_pfcp_node_merge() to handle + * IPv4/IPv6 or FQDN logic. + ******************************************************************************/ ogs_pfcp_node_t *ogs_pfcp_node_add(ogs_list_t *list, ogs_pfcp_node_id_t *node_id, ogs_sockaddr_t *from) { @@ -962,6 +937,18 @@ ogs_pfcp_node_t *ogs_pfcp_node_add(ogs_list_t *list, ogs_assert(list); ogs_assert(node_id && from); + /* + * We only handle IPv4, IPv6, and FQDN types here. If the incoming + * node_id has any other type, we treat it as invalid. This ensures + * we do not merge a node with an unsupported PFCP Node ID. + */ + if (node_id->type != OGS_PFCP_NODE_ID_IPV4 && + node_id->type != OGS_PFCP_NODE_ID_IPV6 && + node_id->type != OGS_PFCP_NODE_ID_FQDN) { + ogs_error("Invalid PFCP Node Type = %d", node_id->type); + return NULL; + } + /* Create node with no config_addr initially */ node = ogs_pfcp_node_new(NULL); if (!node) { @@ -971,10 +958,11 @@ ogs_pfcp_node_t *ogs_pfcp_node_add(ogs_list_t *list, return NULL; } - /* Store node_id and from_addr */ + /* Set node->node_id, reset last_dns_refresh. */ memcpy(&node->node_id, node_id, sizeof(node->node_id)); + node->last_dns_refresh = 0; - /* Merge them => fill node->addr_list if conditions are met */ + /* Merge addresses => fill node->addr_list if conditions are met */ if (ogs_pfcp_node_merge(node, node_id, from) != OGS_OK) { ogs_error("ogs_pfcp_node_merge() failed node_id [%s] from [%s]", ogs_pfcp_node_id_to_string_static(node_id), @@ -988,75 +976,137 @@ ogs_pfcp_node_t *ogs_pfcp_node_add(ogs_list_t *list, return node; } -/*-------------------------------------------------------------------------- - * ogs_pfcp_node_find(): - * Find a node in the list whose "addr_list" matches node_id or from_addr - *-------------------------------------------------------------------------- - */ +/****************************************************************************** + * ogs_pfcp_node_find() + * - No DNS logic here. Merely finds a node by node_id (if provided) and + * checks if 'from' address is in node->addr_list. + ******************************************************************************/ ogs_pfcp_node_t *ogs_pfcp_node_find(ogs_list_t *list, ogs_pfcp_node_id_t *node_id, ogs_sockaddr_t *from) { ogs_pfcp_node_t *cur; - ogs_sockaddr_t *nid_list = NULL; - int found = 0; ogs_assert(list); ogs_assert(node_id || from); - if (node_id) - nid_list = ogs_pfcp_node_id_to_addrinfo(node_id); - ogs_list_for_each(list, cur) { - if (check_any_match(cur->addr_list, nid_list, from)) { - found = 1; - break; + /* + * If the node currently has a known Node ID (not UNKNOWN) + * and the caller provided a node_id to match, then compare them. + * If they do not match, skip this node. This allows config-based nodes + * (with an UNKNOWN node_id) to be found by IP address alone, + * while nodes with a definite ID must match the incoming node_id. + */ + if (cur->node_id.type != OGS_PFCP_NODE_ID_UNKNOWN && node_id) { + if (!ogs_pfcp_node_id_compare(&cur->node_id, node_id)) + continue; + } + if (!from) + return cur; + + /* Check if 'from' is in cur->addr_list. */ + if (ogs_sockaddr_check_any_match(cur->addr_list, NULL, + from, /* compare_port= */ true)) { + return cur; } } - if (nid_list) - ogs_freeaddrinfo(nid_list); - - if (found) - return cur; + /* No match found. */ return NULL; } -/*-------------------------------------------------------------------------- +/****************************************************************************** * ogs_pfcp_node_merge(): - * Merge logic: addr_list + node_id + from_addr - *-------------------------------------------------------------------------- - * - If node->addr_list is empty, copy node->config_addr into addr_list first - * - Convert node_id to addresses, then check from_addr - * - If addr_list is empty => merge everything - * - If addr_list not empty => merge only if there's a matching IP - */ + * - If node_id changes to FQDN, we check last_dns_refresh. + * => If 0, do an immediate DNS resolution (first time). + * => If >= 300 seconds passed, do a periodic refresh. + * => Otherwise, skip. + * - If node_id changes to IPv4/IPv6, convert IP addresses immediately. + * - Merge the 'from' address into addr_list if provided. + ******************************************************************************/ int ogs_pfcp_node_merge(ogs_pfcp_node_t *node, ogs_pfcp_node_id_t *node_id, ogs_sockaddr_t *from) { - ogs_sockaddr_t *nid_list = NULL; ogs_sockaddr_t single; + ogs_sockaddr_t *tmp_list = NULL; ogs_assert(node); ogs_assert(node_id || from); - /* Convert node_id to a list of addresses */ if (node_id) { - nid_list = ogs_pfcp_node_id_to_addrinfo(node_id); - if (!nid_list) { - ogs_error("ogs_pfcp_node_id_to_addrinfo() failed [%d]", - node_id->type); + /* + * We only handle IPv4, IPv6, and FQDN types here. If the incoming + * node_id has any other type, we treat it as invalid. This ensures + * we do not merge a node with an unsupported PFCP Node ID. + */ + if (node_id->type != OGS_PFCP_NODE_ID_IPV4 && + node_id->type != OGS_PFCP_NODE_ID_IPV6 && + node_id->type != OGS_PFCP_NODE_ID_FQDN) { + ogs_error("Invalid PFCP Node Type = %d", node_id->type); return OGS_ERROR; } - ogs_merge_addrinfo(&node->addr_list, nid_list); - ogs_freeaddrinfo(nid_list); + /* Check if node_id is different from node->node_id. */ + if (!ogs_pfcp_node_id_compare(&node->node_id, node_id)) { + /* Update the node's ID and reset the refresh timestamp. */ + memcpy(&node->node_id, node_id, sizeof(node->node_id)); + node->last_dns_refresh = 0; + } + + /* If FQDN, do a DNS lookup (immediate or periodic). */ + if (node->node_id.type == OGS_PFCP_NODE_ID_FQDN) { +/* + * We perform a DNS resolution if 'last_dns_refresh' is zero, which means + * this FQDN node has never been resolved yet (first-time resolution), or if + * at least 300 seconds have passed since the last refresh. Without checking + * '== 0', a newly created FQDN node might skip resolution if 'now' is less + * than the 300-second threshold. + */ + ogs_time_t now = ogs_time_now(); + +/* For 300-second refresh interval in microseconds. */ +#define OGS_PFCP_NODE_DNS_REFRESH_INTERVAL \ + ((ogs_time_t)(300) * OGS_USEC_PER_SEC) + if (node->last_dns_refresh == 0 || + (now - node->last_dns_refresh) >= + OGS_PFCP_NODE_DNS_REFRESH_INTERVAL) { + + tmp_list = ogs_pfcp_node_id_to_addrinfo(&node->node_id); + if (!tmp_list) { + ogs_error("DNS resolution failed for FQDN [%s]", + node->node_id.fqdn); + return OGS_ERROR; + } + + ogs_freeaddrinfo(node->addr_list); + node->addr_list = tmp_list; + node->last_dns_refresh = now; + tmp_list = NULL; + } + } + /* If IPv4/IPv6, convert immediately. */ + else if (node->node_id.type == OGS_PFCP_NODE_ID_IPV4 || + node->node_id.type == OGS_PFCP_NODE_ID_IPV6) { + tmp_list = ogs_pfcp_node_id_to_addrinfo(&node->node_id); + if (!tmp_list) { + ogs_error("Failed to convert node ID to address info"); + return OGS_ERROR; + } + ogs_merge_addrinfo(&node->addr_list, tmp_list); + ogs_freeaddrinfo(tmp_list); + tmp_list = NULL; + } + else { + /* Not IPv4, IPv6, or FQDN => invalid node type. */ + ogs_error("Invalid Node ID type [%d]", node->node_id.type); + return OGS_ERROR; + } } - /* "from" as single item */ + /* Merge 'from' into addr_list if provided. */ if (from) { memcpy(&single, from, sizeof(single)); single.next = NULL; - ogs_merge_addrinfo(&node->addr_list, &single); } @@ -1082,22 +1132,33 @@ void ogs_pfcp_node_remove_all(ogs_list_t *list) ogs_pfcp_node_remove(list, node); } -/* Function to compare two node IDs */ -int ogs_pfcp_node_id_compare( +/****************************************************************************** + * Compare two node IDs for equality. Returns true if they match, else false. + ******************************************************************************/ +bool ogs_pfcp_node_id_compare( const ogs_pfcp_node_id_t *id1, const ogs_pfcp_node_id_t *id2) { - if (id1->type != id2->type) { + if (id1->type != id2->type) return false; /* Types do not match */ - } switch (id1->type) { case OGS_PFCP_NODE_ID_IPV4: - return (id1->addr == id2->addr); + if (id1->addr != id2->addr) return false; + return true; + case OGS_PFCP_NODE_ID_IPV6: - return (memcmp(id1->addr6, id2->addr6, OGS_IPV6_LEN) == 0); + if (memcmp(id1->addr6, id2->addr6, OGS_IPV6_LEN) != 0) + return false; + return true; + case OGS_PFCP_NODE_ID_FQDN: - return (strcmp(id1->fqdn, id2->fqdn) == 0); + if (strcmp(id1->fqdn, id2->fqdn) != 0) + return false; + return true; + default: + ogs_error("Unexpected Node Type [%d]", id1->type); + ogs_abort(); return false; /* Unknown types do not match */ } } diff --git a/lib/pfcp/context.h b/lib/pfcp/context.h index 219644fcf..f18c8bf31 100644 --- a/lib/pfcp/context.h +++ b/lib/pfcp/context.h @@ -84,17 +84,20 @@ typedef struct ogs_pfcp_context_s { typedef struct ogs_pfcp_node_s { ogs_lnode_t lnode; /* A node of list_t */ - ogs_sockaddr_t *config_addr; /* Configured addresses */ + ogs_sockaddr_t *config_addr; /* Configured addresses */ ogs_pfcp_node_id_t node_id; /* PFCP node ID */ /* List of addresses:: final merged address list */ - ogs_sockaddr_t *addr_list; + ogs_sockaddr_t *addr_list; /* * Iterator for round-robin sendto operations. * Points to the current address in the round-robin sequence. */ - ogs_sockaddr_t *current_addr; + ogs_sockaddr_t *current_addr; + + /* Timestamp of last DNS refresh for FQDN nodes. */ + ogs_time_t last_dns_refresh; ogs_list_t local_list; ogs_list_t remote_list; @@ -417,7 +420,7 @@ int ogs_pfcp_node_merge(ogs_pfcp_node_t *node, ogs_pfcp_node_id_t *node_id, ogs_sockaddr_t *from); void ogs_pfcp_node_remove(ogs_list_t *list, ogs_pfcp_node_t *node); void ogs_pfcp_node_remove_all(ogs_list_t *list); -int ogs_pfcp_node_id_compare( +bool ogs_pfcp_node_id_compare( const ogs_pfcp_node_id_t *id1, const ogs_pfcp_node_id_t *id2); ogs_gtpu_resource_t *ogs_pfcp_find_gtpu_resource(ogs_list_t *list, diff --git a/lib/pfcp/types.h b/lib/pfcp/types.h index 433382f5d..cbe9df7de 100644 --- a/lib/pfcp/types.h +++ b/lib/pfcp/types.h @@ -550,9 +550,17 @@ typedef struct ogs_pfcp_outer_header_removal_s { uint8_t gtpu_extheader_deletion; } ogs_pfcp_outer_header_removal_t; +/****************************************************************************** + * PFCP Node ID structure + ******************************************************************************/ #define OGS_PFCP_NODE_ID_IPV4 0 #define OGS_PFCP_NODE_ID_IPV6 1 #define OGS_PFCP_NODE_ID_FQDN 2 + +/****************************************************************************** + * Add this line to define the UNKNOWN type. We use '3' since 0,1,2 are taken. + ******************************************************************************/ +#define OGS_PFCP_NODE_ID_UNKNOWN 0xf typedef struct ogs_pfcp_node_id_s { ED2(uint8_t spare:4;, uint8_t type:4;)