pespin has submitted this change. ( https://gerrit.osmocom.org/c/libosmo-sigtran/+/38636?usp=email )
Change subject: sigtran: Make osmo_ss7_route struct and several APIs private ......................................................................
sigtran: Make osmo_ss7_route struct and several APIs private
The struct is in general managed internally by the library, only a few APIs which are in use by some external users (osmo-hnbgw and osmo-bsc vty) are left public.
Change-Id: I8a86966cb6a5361687987a2e9acd79c4ab3d97c4 --- M TODO-RELEASE M include/osmocom/sigtran/osmo_ss7.h M src/Makefile.am M src/osmo_ss7.c M src/osmo_ss7_as.c M src/osmo_ss7_hmrt.c A src/osmo_ss7_route.c M src/osmo_ss7_route_table.c M src/osmo_ss7_vty.c M src/sccp_scrc.c M src/sccp_user.c M src/ss7_internal.h A src/ss7_route.h M src/xua_as_fsm.c M src/xua_rkm.c M tests/ss7/ss7_test.c 16 files changed, 409 insertions(+), 345 deletions(-)
Approvals: osmith: Looks good to me, but someone else must approve laforge: Looks good to me, but someone else must approve pespin: Looks good to me, approved Jenkins Builder: Verified
diff --git a/TODO-RELEASE b/TODO-RELEASE index 06d4f53..06ae923 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -9,4 +9,5 @@ #library what description / commit summary line libosmo-sigtran API change struct osmo_ss7_instance has new member 'secondary_pc' libosmo-sigtran add API osmo_sccp_{release,return,reset,error,refusal}_cause_name(s), osmo_sua_sccp_cause_name -libosmo-sigtran add API osmo_ss7_route_get_dest_as() \ No newline at end of file +libosmo-sigtran add API osmo_ss7_route_get_dest_as() +libosmo-sigtran Make private osmo_ss7_route diff --git a/include/osmocom/sigtran/osmo_ss7.h b/include/osmocom/sigtran/osmo_ss7.h index 3b5aa4f..002117a 100644 --- a/include/osmocom/sigtran/osmo_ss7.h +++ b/include/osmocom/sigtran/osmo_ss7.h @@ -181,37 +181,10 @@ * SS7 Routes ***********************************************************************/
-struct osmo_ss7_route { - /*! member in \ref osmo_ss7_route_table.routes */ - struct llist_head list; - /*! \ref osmo_ss7_route_table to which we belong */ - struct osmo_ss7_route_table *rtable; - - struct { - /*! pointer to linkset (destination) of route */ - struct osmo_ss7_linkset *linkset; - /*! pointer to Application Server */ - struct osmo_ss7_as *as; - } dest; - - struct { - /* FIXME: presence? */ - uint32_t pc; - uint32_t mask; - /*! human-specified linkset name */ - char *linkset_name; - /*! lower priority is higher */ - uint32_t priority; - uint8_t qos_class; - } cfg; -}; +struct osmo_ss7_route;
struct osmo_ss7_route * osmo_ss7_route_lookup(struct osmo_ss7_instance *inst, uint32_t dpc); -struct osmo_ss7_route * -osmo_ss7_route_create(struct osmo_ss7_route_table *rtbl, uint32_t dpc, - uint32_t mask, const char *linkset_name); -void osmo_ss7_route_destroy(struct osmo_ss7_route *rt); const char *osmo_ss7_route_print(const struct osmo_ss7_route *rt); const char *osmo_ss7_route_name(struct osmo_ss7_route *rt, bool list_asps); struct osmo_ss7_as * diff --git a/src/Makefile.am b/src/Makefile.am index 5dddb05..af6bae8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,6 +5,7 @@ noinst_HEADERS = \ sccp_internal.h \ ss7_internal.h \ + ss7_route.h \ ss7_route_table.h \ xua_asp_fsm.h \ xua_as_fsm.h \ @@ -38,6 +39,7 @@ osmo_ss7_hmrt.c \ osmo_ss7_vty.c \ osmo_ss7_xua_srv.c \ + osmo_ss7_route.c \ osmo_ss7_route_table.c \ sccp2sua.c \ sccp_helpers.c \ diff --git a/src/osmo_ss7.c b/src/osmo_ss7.c index 95e58a7..37b50da 100644 --- a/src/osmo_ss7.c +++ b/src/osmo_ss7.c @@ -51,6 +51,7 @@ #include "sccp_internal.h" #include "xua_internal.h" #include "ss7_internal.h" +#include "ss7_route.h" #include "ss7_route_table.h" #include "xua_asp_fsm.h" #include "xua_as_fsm.h" @@ -508,7 +509,7 @@ /* find any routes pointing to this AS and remove them */ llist_for_each_entry_safe(rt, rt2, &lset->inst->rtable_system->routes, list) { if (rt->dest.linkset == lset) - osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt); }
for (i = 0; i < ARRAY_SIZE(lset->links); i++) { @@ -612,287 +613,6 @@ }
/*********************************************************************** - * SS7 Routes - ***********************************************************************/ - -/*! \brief Allocate a route entry - * \param[in] rtbl Routing Table where the route belongs - * \param[in] pc Point Code of the destination of the route - * \param[in] mask Mask of the destination Point Code \ref pc - * \returns Allocated route (not yet inserted into its rtbl), NULL on error - * - * The returned route has no linkset associated yet, user *must* associate it - * using API ss7_route_set_linkset() before inserting the route into its - * routing table. - * - * Fields priority and qos_class may be set *before* inserting the route into - * its routing table: - * - A default priority of 0 is configured on the route. - * - A default qos-class of 0 is configured on the route. - * - * Use API ss7_route_insert() to insert the route into its routing table. - * - * The route entry allocated with this API can be destroyed/freed at any point using API - * osmo_ss7_route_destroy(), regardless of it being already inserted or not in - * its routing table. - */ -struct osmo_ss7_route * -ss7_route_alloc(struct osmo_ss7_route_table *rtbl, uint32_t pc, uint32_t mask) -{ - struct osmo_ss7_route *rt; - - OSMO_ASSERT(ss7_initialized); - - rt = talloc_zero(rtbl, struct osmo_ss7_route); - if (!rt) - return NULL; - - /* Mark it as not being inserted yet in rtbl */ - INIT_LLIST_HEAD(&rt->list); - rt->rtable = rtbl; - /* truncate mask to maximum. Let's avoid callers specifying arbitrary large - * masks to ensure we don't fail duplicate detection with longer mask lengths */ - rt->cfg.mask = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, mask); - rt->cfg.pc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, pc); - rt->cfg.priority = OSMO_SS7_ROUTE_PRIO_DEFAULT; - return rt; -} - -/*! \brief Check whether route has already been inserted into its routing table. - * \returns true if already inserted, false if not. - */ -static bool ss7_route_inserted(const struct osmo_ss7_route *rt) -{ - return !llist_empty(&rt->list); -} - -/*! \brief Set linkset on route entry - * \param[in] rt Route to be configured - * \param[in] linkset_name string name of the linkset to be used - * \returns 0 on success, negative on error. - */ -int -ss7_route_set_linkset(struct osmo_ss7_route *rt, const char *linkset_name) -{ - struct osmo_ss7_linkset *lset; - struct osmo_ss7_as *as = NULL; - struct osmo_ss7_route_table *rtbl = rt->rtable; - - if (rt->cfg.linkset_name) { - LOGSS7(rtbl->inst, LOGL_ERROR, "Attempt setting linkset on route already configured!\n"); - return -EBUSY; - } - - if (ss7_route_inserted(rt)) { - LOGSS7(rtbl->inst, LOGL_ERROR, "Attempt setting linkset on route already in the routing table!\n"); - return -EALREADY; - } - - lset = osmo_ss7_linkset_find_by_name(rtbl->inst, linkset_name); - if (!lset) { - as = osmo_ss7_as_find_by_name(rtbl->inst, linkset_name); - if (!as) - return -ENODEV; - } - - rt->cfg.linkset_name = talloc_strdup(rt, linkset_name); - if (lset) { - rt->dest.linkset = lset; - LOGSS7(rtbl->inst, LOGL_INFO, "Creating route: pc=%u=%s mask=0x%x via linkset '%s'\n", - rt->cfg.pc, osmo_ss7_pointcode_print(rtbl->inst, rt->cfg.pc), - rt->cfg.mask, lset->cfg.name); - } else { - rt->dest.as = as; - LOGSS7(rtbl->inst, LOGL_INFO, "Creating route: pc=%u=%s mask=0x%x via AS '%s'\n", - rt->cfg.pc, osmo_ss7_pointcode_print(rtbl->inst, rt->cfg.pc), - rt->cfg.mask, as->cfg.name); - } - return 0; -} - -/*! \brief Find a SS7 route for given destination point code in given SS7 */ -struct osmo_ss7_route * -osmo_ss7_route_lookup(struct osmo_ss7_instance *inst, uint32_t dpc) -{ - OSMO_ASSERT(ss7_initialized); - return ss7_route_table_find_route_by_dpc(inst->rtable_system, dpc); -} - -/* insert the route in the ordered list of routes. The list is sorted by - * mask length, so that the more specific (longer mask) routes are - * first, while the less specific routes with shorter masks are last. - * Within the same mask length, the routes are ordered by priority. - * Hence, the first matching route in a linear iteration is the most - * specific match. */ -static void route_insert_sorted(struct osmo_ss7_route_table *rtbl, - struct osmo_ss7_route *cmp) -{ - struct osmo_ss7_route *rt; - - llist_for_each_entry(rt, &rtbl->routes, list) { - if (rt->cfg.mask == cmp->cfg.mask && - rt->cfg.priority > cmp->cfg.priority) { - /* insert before the current entry */ - llist_add(&cmp->list, rt->list.prev); - return; - } - if (rt->cfg.mask < cmp->cfg.mask) { - /* insert before the current entry */ - llist_add(&cmp->list, rt->list.prev); - return; - } - } - /* not added, i.e. no smaller mask length and priority found: we are the - * smallest mask and priority and thus should go last */ - llist_add_tail(&cmp->list, &rtbl->routes); -} - -/*! \brief Insert route into its routing table - * \param[in] rt Route to be inserted into its routing table - * \returns 0 on success, negative on error - * - * A route is only really used once it has been inserted into its routing table. - */ -int -ss7_route_insert(struct osmo_ss7_route *rt) -{ - struct osmo_ss7_route *prev_rt; - struct osmo_ss7_route_table *rtbl = rt->rtable; - - if (ss7_route_inserted(rt)) { - LOGSS7(rtbl->inst, LOGL_ERROR, "Attempt insert of route already in the routing table!\n"); - return -EALREADY; - } - - if (!rt->cfg.linkset_name) { - LOGSS7(rtbl->inst, LOGL_ERROR, "Attempt insert of route with unset linkset!\n"); - return -EINVAL; - } - - /* check for duplicates */ - prev_rt = ss7_route_table_find_route_by_dpc_mask(rtbl, rt->cfg.pc, rt->cfg.mask); - if (prev_rt && !strcmp(prev_rt->cfg.linkset_name, rt->cfg.linkset_name)) { - LOGSS7(rtbl->inst, LOGL_ERROR, - "Refusing to create route with existing linkset name: pc=%u=%s mask=0x%x via linkset/AS '%s'\n", - rt->cfg.pc, osmo_ss7_pointcode_print(rtbl->inst, rt->cfg.pc), - rt->cfg.mask, rt->cfg.linkset_name); - return -EADDRINUSE; - } - - route_insert_sorted(rtbl, rt); - return 0; -} - -/*! \brief Create a new route in the given routing table - * \param[in] rtbl Routing Table in which the route is to be created - * \param[in] pc Point Code of the destination of the route - * \param[in] mask Mask of the destination Point Code \ref pc - * \param[in] linkset_name string name of the linkset to be used - * \returns callee-allocated + initialized route, NULL on error - * - * The route allocated and returned by this API is already inserted into the - * routing table, with priority and qos-class set to 0. - * If you plan to use different values for priority and qos-class, avoid using - * this API and use ss7_route_alloc() + ss7_route_set_linkset() + - * ss7_route_insert() instead. - */ -struct osmo_ss7_route * -osmo_ss7_route_create(struct osmo_ss7_route_table *rtbl, uint32_t pc, - uint32_t mask, const char *linkset_name) -{ - struct osmo_ss7_route *rt; - int rc; - - rt = ss7_route_alloc(rtbl, pc, mask); - if (!rt) - return NULL; - - if (ss7_route_set_linkset(rt, linkset_name) < 0) { - talloc_free(rt); - return NULL; - } - - rc = ss7_route_insert(rt); - /* Keep old behavior, return already existing route: */ - if (rc == -EADDRINUSE) { - talloc_free(rt); - return ss7_route_table_find_route_by_dpc_mask(rtbl, rt->cfg.pc, rt->cfg.mask); - } - - return rt; -} - -/*! \brief Destroy a given SS7 route */ -void osmo_ss7_route_destroy(struct osmo_ss7_route *rt) -{ - OSMO_ASSERT(ss7_initialized); - - if (!rt) - return; - - if (ss7_route_inserted(rt)) { - struct osmo_ss7_instance *inst = rt->rtable->inst; - LOGSS7(inst, LOGL_INFO, - "Destroying route: pc=%u=%s mask=0x%x via linkset/ASP '%s'\n", - rt->cfg.pc, osmo_ss7_pointcode_print(inst, rt->cfg.pc), - rt->cfg.mask, rt->cfg.linkset_name); - llist_del(&rt->list); - } - talloc_free(rt); -} - -/* count number of consecutive leading (MSB) bits that are '1' */ -static unsigned int count_leading_one_bits(uint32_t inp, unsigned int nbits) -{ - unsigned int i; - - for (i = 0; i < nbits; i++) { - if (!(inp & (1 << (nbits-1-i)))) - return i; - } - return i; -} - -/* determine the mask length in number of bits; negative if non-consecutive mask */ -static int u32_masklen(uint32_t mask, unsigned int nbits) -{ - unsigned int i; - unsigned int leading_one_bits = count_leading_one_bits(mask, nbits); - - /* are there any bits set after the initial bits? */ - for (i = leading_one_bits; i < nbits; i++) { - if (mask & (1 << (nbits-1-i))) - return -1; /* not a simple prefix mask */ - } - return leading_one_bits; -} - -const char *osmo_ss7_route_print(const struct osmo_ss7_route *rt) -{ - const struct osmo_ss7_instance *inst = rt->rtable->inst; - unsigned int pc_width = osmo_ss7_pc_width(&inst->cfg.pc_fmt); - static char buf[64]; - int rc = u32_masklen(rt->cfg.mask, pc_width); - - if (rc < 0) - snprintf(buf, sizeof(buf), "%s/%s", osmo_ss7_pointcode_print(inst, rt->cfg.pc), - osmo_ss7_pointcode_print2(inst, rt->cfg.mask)); - else - snprintf(buf, sizeof(buf), "%s/%u", osmo_ss7_pointcode_print(inst, rt->cfg.pc), rc); - return buf; -} - -/*! \brief Get destination AS of route - * \param[in] rt Route entry holding the AS destination - * \returns pointer to Application Server on success; NULL if rt doesn't route - * to an AS (i.e. routes to a linkset). */ -struct osmo_ss7_as * -osmo_ss7_route_get_dest_as(struct osmo_ss7_route *rt) -{ - return rt->dest.as; -} - -/*********************************************************************** * SS7 Application Server ***********************************************************************/
diff --git a/src/osmo_ss7_as.c b/src/osmo_ss7_as.c index 87e7e04..cc61b12 100644 --- a/src/osmo_ss7_as.c +++ b/src/osmo_ss7_as.c @@ -33,6 +33,7 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/logging.h>
+#include "ss7_route.h" #include "ss7_route_table.h" #include "ss7_internal.h" #include "xua_as_fsm.h" @@ -166,7 +167,7 @@ /* find any routes pointing to this AS and remove them */ llist_for_each_entry_safe(rt, rt2, &as->inst->rtable_system->routes, list) { if (rt->dest.as == as) - osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt); }
as->inst = NULL; diff --git a/src/osmo_ss7_hmrt.c b/src/osmo_ss7_hmrt.c index 57d7388..55e5776 100644 --- a/src/osmo_ss7_hmrt.c +++ b/src/osmo_ss7_hmrt.c @@ -35,6 +35,7 @@ #include <osmocom/sigtran/protocol/m3ua.h>
#include "xua_internal.h" +#include "ss7_route.h" #include "ss7_route_table.h" #include "ss7_internal.h"
diff --git a/src/osmo_ss7_route.c b/src/osmo_ss7_route.c new file mode 100644 index 0000000..b5b2d93 --- /dev/null +++ b/src/osmo_ss7_route.c @@ -0,0 +1,312 @@ +/* (C) 2015-2017 by Harald Welte laforge@gnumonks.org + * (C) 2023-2024 by sysmocom s.f.m.c. GmbH info@sysmocom.de + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <errno.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/logging.h> +#include <osmocom/sigtran/mtp_sap.h> +#include <osmocom/sigtran/osmo_ss7.h> + +#include "ss7_route.h" +#include "ss7_route_table.h" +#include "ss7_internal.h" + +/*********************************************************************** + * SS7 Routes + ***********************************************************************/ + +/*! \brief Allocate a route entry + * \param[in] rtbl Routing Table where the route belongs + * \param[in] pc Point Code of the destination of the route + * \param[in] mask Mask of the destination Point Code \ref pc + * \returns Allocated route (not yet inserted into its rtbl), NULL on error + * + * The returned route has no linkset associated yet, user *must* associate it + * using API ss7_route_set_linkset() before inserting the route into its + * routing table. + * + * Fields priority and qos_class may be set *before* inserting the route into + * its routing table: + * - A default priority of 0 is configured on the route. + * - A default qos-class of 0 is configured on the route. + * + * Use API ss7_route_insert() to insert the route into its routing table. + * + * The route entry allocated with this API can be destroyed/freed at any point using API + * ss7_route_destroy(), regardless of it being already inserted or not in + * its routing table. + */ +struct osmo_ss7_route * +ss7_route_alloc(struct osmo_ss7_route_table *rtbl, uint32_t pc, uint32_t mask) +{ + struct osmo_ss7_route *rt; + + OSMO_ASSERT(ss7_initialized); + + rt = talloc_zero(rtbl, struct osmo_ss7_route); + if (!rt) + return NULL; + + /* Mark it as not being inserted yet in rtbl */ + INIT_LLIST_HEAD(&rt->list); + rt->rtable = rtbl; + /* truncate mask to maximum. Let's avoid callers specifying arbitrary large + * masks to ensure we don't fail duplicate detection with longer mask lengths */ + rt->cfg.mask = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, mask); + rt->cfg.pc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, pc); + rt->cfg.priority = OSMO_SS7_ROUTE_PRIO_DEFAULT; + return rt; +} + +/*! \brief Check whether route has already been inserted into its routing table. + * \returns true if already inserted, false if not. + */ +static bool ss7_route_inserted(const struct osmo_ss7_route *rt) +{ + return !llist_empty(&rt->list); +} + +/*! \brief Set linkset on route entry + * \param[in] rt Route to be configured + * \param[in] linkset_name string name of the linkset to be used + * \returns 0 on success, negative on error. + */ +int +ss7_route_set_linkset(struct osmo_ss7_route *rt, const char *linkset_name) +{ + struct osmo_ss7_linkset *lset; + struct osmo_ss7_as *as = NULL; + struct osmo_ss7_route_table *rtbl = rt->rtable; + + if (rt->cfg.linkset_name) { + LOGSS7(rtbl->inst, LOGL_ERROR, "Attempt setting linkset on route already configured!\n"); + return -EBUSY; + } + + if (ss7_route_inserted(rt)) { + LOGSS7(rtbl->inst, LOGL_ERROR, "Attempt setting linkset on route already in the routing table!\n"); + return -EALREADY; + } + + lset = osmo_ss7_linkset_find_by_name(rtbl->inst, linkset_name); + if (!lset) { + as = osmo_ss7_as_find_by_name(rtbl->inst, linkset_name); + if (!as) + return -ENODEV; + } + + rt->cfg.linkset_name = talloc_strdup(rt, linkset_name); + if (lset) { + rt->dest.linkset = lset; + LOGSS7(rtbl->inst, LOGL_INFO, "Creating route: pc=%u=%s mask=0x%x via linkset '%s'\n", + rt->cfg.pc, osmo_ss7_pointcode_print(rtbl->inst, rt->cfg.pc), + rt->cfg.mask, lset->cfg.name); + } else { + rt->dest.as = as; + LOGSS7(rtbl->inst, LOGL_INFO, "Creating route: pc=%u=%s mask=0x%x via AS '%s'\n", + rt->cfg.pc, osmo_ss7_pointcode_print(rtbl->inst, rt->cfg.pc), + rt->cfg.mask, as->cfg.name); + } + return 0; +} + +/* insert the route in the ordered list of routes. The list is sorted by + * mask length, so that the more specific (longer mask) routes are + * first, while the less specific routes with shorter masks are last. + * Within the same mask length, the routes are ordered by priority. + * Hence, the first matching route in a linear iteration is the most + * specific match. */ +static void route_insert_sorted(struct osmo_ss7_route_table *rtbl, + struct osmo_ss7_route *cmp) +{ + struct osmo_ss7_route *rt; + + llist_for_each_entry(rt, &rtbl->routes, list) { + if (rt->cfg.mask == cmp->cfg.mask && + rt->cfg.priority > cmp->cfg.priority) { + /* insert before the current entry */ + llist_add(&cmp->list, rt->list.prev); + return; + } + if (rt->cfg.mask < cmp->cfg.mask) { + /* insert before the current entry */ + llist_add(&cmp->list, rt->list.prev); + return; + } + } + /* not added, i.e. no smaller mask length and priority found: we are the + * smallest mask and priority and thus should go last */ + llist_add_tail(&cmp->list, &rtbl->routes); +} + +/*! \brief Insert route into its routing table + * \param[in] rt Route to be inserted into its routing table + * \returns 0 on success, negative on error + * + * A route is only really used once it has been inserted into its routing table. + */ +int +ss7_route_insert(struct osmo_ss7_route *rt) +{ + struct osmo_ss7_route *prev_rt; + struct osmo_ss7_route_table *rtbl = rt->rtable; + + if (ss7_route_inserted(rt)) { + LOGSS7(rtbl->inst, LOGL_ERROR, "Attempt insert of route already in the routing table!\n"); + return -EALREADY; + } + + if (!rt->cfg.linkset_name) { + LOGSS7(rtbl->inst, LOGL_ERROR, "Attempt insert of route with unset linkset!\n"); + return -EINVAL; + } + + /* check for duplicates */ + prev_rt = ss7_route_table_find_route_by_dpc_mask(rtbl, rt->cfg.pc, rt->cfg.mask); + if (prev_rt && !strcmp(prev_rt->cfg.linkset_name, rt->cfg.linkset_name)) { + LOGSS7(rtbl->inst, LOGL_ERROR, + "Refusing to create route with existing linkset name: pc=%u=%s mask=0x%x via linkset/AS '%s'\n", + rt->cfg.pc, osmo_ss7_pointcode_print(rtbl->inst, rt->cfg.pc), + rt->cfg.mask, rt->cfg.linkset_name); + return -EADDRINUSE; + } + + route_insert_sorted(rtbl, rt); + return 0; +} + +/*! \brief Create a new route in the given routing table + * \param[in] rtbl Routing Table in which the route is to be created + * \param[in] pc Point Code of the destination of the route + * \param[in] mask Mask of the destination Point Code \ref pc + * \param[in] linkset_name string name of the linkset to be used + * \returns callee-allocated + initialized route, NULL on error + * + * The route allocated and returned by this API is already inserted into the + * routing table, with priority and qos-class set to 0. + * If you plan to use different values for priority and qos-class, avoid using + * this API and use ss7_route_alloc() + ss7_route_set_linkset() + + * ss7_route_insert() instead. + */ +struct osmo_ss7_route * +ss7_route_create(struct osmo_ss7_route_table *rtbl, uint32_t pc, + uint32_t mask, const char *linkset_name) +{ + struct osmo_ss7_route *rt; + int rc; + + rt = ss7_route_alloc(rtbl, pc, mask); + if (!rt) + return NULL; + + if (ss7_route_set_linkset(rt, linkset_name) < 0) { + talloc_free(rt); + return NULL; + } + + rc = ss7_route_insert(rt); + /* Keep old behavior, return already existing route: */ + if (rc == -EADDRINUSE) { + talloc_free(rt); + return ss7_route_table_find_route_by_dpc_mask(rtbl, rt->cfg.pc, rt->cfg.mask); + } + + return rt; +} + +/*! \brief Destroy a given SS7 route */ +void ss7_route_destroy(struct osmo_ss7_route *rt) +{ + OSMO_ASSERT(ss7_initialized); + + if (!rt) + return; + + if (ss7_route_inserted(rt)) { + struct osmo_ss7_instance *inst = rt->rtable->inst; + LOGSS7(inst, LOGL_INFO, + "Destroying route: pc=%u=%s mask=0x%x via linkset/ASP '%s'\n", + rt->cfg.pc, osmo_ss7_pointcode_print(inst, rt->cfg.pc), + rt->cfg.mask, rt->cfg.linkset_name); + llist_del(&rt->list); + } + talloc_free(rt); +} + +/* count number of consecutive leading (MSB) bits that are '1' */ +static unsigned int count_leading_one_bits(uint32_t inp, unsigned int nbits) +{ + unsigned int i; + + for (i = 0; i < nbits; i++) { + if (!(inp & (1 << (nbits-1-i)))) + return i; + } + return i; +} + +/* determine the mask length in number of bits; negative if non-consecutive mask */ +static int u32_masklen(uint32_t mask, unsigned int nbits) +{ + unsigned int i; + unsigned int leading_one_bits = count_leading_one_bits(mask, nbits); + + /* are there any bits set after the initial bits? */ + for (i = leading_one_bits; i < nbits; i++) { + if (mask & (1 << (nbits-1-i))) + return -1; /* not a simple prefix mask */ + } + return leading_one_bits; +} + +const char *osmo_ss7_route_print(const struct osmo_ss7_route *rt) +{ + const struct osmo_ss7_instance *inst = rt->rtable->inst; + unsigned int pc_width = osmo_ss7_pc_width(&inst->cfg.pc_fmt); + static char buf[64]; + int rc = u32_masklen(rt->cfg.mask, pc_width); + + if (rc < 0) + snprintf(buf, sizeof(buf), "%s/%s", osmo_ss7_pointcode_print(inst, rt->cfg.pc), + osmo_ss7_pointcode_print2(inst, rt->cfg.mask)); + else + snprintf(buf, sizeof(buf), "%s/%u", osmo_ss7_pointcode_print(inst, rt->cfg.pc), rc); + return buf; +} + +/*! \brief Find a SS7 route for given destination point code in given SS7 */ +struct osmo_ss7_route * +osmo_ss7_route_lookup(struct osmo_ss7_instance *inst, uint32_t dpc) +{ + OSMO_ASSERT(ss7_initialized); + return ss7_route_table_find_route_by_dpc(inst->rtable_system, dpc); +} + +/*! \brief Get destination AS of route + * \param[in] rt Route entry holding the AS destination + * \returns pointer to Application Server on success; NULL if rt doesn't route + * to an AS (i.e. routes to a linkset). */ +struct osmo_ss7_as * +osmo_ss7_route_get_dest_as(struct osmo_ss7_route *rt) +{ + return rt->dest.as; +} diff --git a/src/osmo_ss7_route_table.c b/src/osmo_ss7_route_table.c index 2939bf4..4ab4594 100644 --- a/src/osmo_ss7_route_table.c +++ b/src/osmo_ss7_route_table.c @@ -24,6 +24,7 @@ #include <osmocom/sigtran/mtp_sap.h> #include <osmocom/sigtran/osmo_ss7.h>
+#include "ss7_route.h" #include "ss7_route_table.h" #include "ss7_internal.h"
diff --git a/src/osmo_ss7_vty.c b/src/osmo_ss7_vty.c index aa8c9bc..6c9db5a 100644 --- a/src/osmo_ss7_vty.c +++ b/src/osmo_ss7_vty.c @@ -46,6 +46,7 @@ #include "xua_internal.h" #include <osmocom/sigtran/sccp_sap.h> #include "sccp_internal.h" +#include "ss7_route.h" #include "ss7_route_table.h" #include "ss7_internal.h"
@@ -443,7 +444,7 @@ return CMD_SUCCESS;
destroy_warning: - osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt); return CMD_WARNING; }
@@ -477,7 +478,7 @@ return CMD_WARNING; }
- osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt); return CMD_SUCCESS; }
@@ -2025,7 +2026,7 @@ if (cs7_role == CS7_ROLE_ASP) { rt = ss7_route_table_find_route_by_dpc_mask(as->inst->rtable_system, rkey->pc, 0xffffff); if (rt) - osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt); }
rkey->pc = pc; @@ -2036,7 +2037,7 @@
/* automatically add new route (see also comment above) */ if (cs7_role == CS7_ROLE_ASP) { - if (!osmo_ss7_route_create(as->inst->rtable_system, rkey->pc, 0xffffff, as->cfg.name)) { + if (!ss7_route_create(as->inst->rtable_system, rkey->pc, 0xffffff, as->cfg.name)) { vty_out(vty, "Cannot create route (pc=%s, linkset=%s) to AS %s", dpc, as->cfg.name, VTY_NEWLINE); return CMD_WARNING; } diff --git a/src/sccp_scrc.c b/src/sccp_scrc.c index be23c21..5b541d3 100644 --- a/src/sccp_scrc.c +++ b/src/sccp_scrc.c @@ -33,8 +33,10 @@ #include <osmocom/sigtran/protocol/mtp.h>
#include "sccp_internal.h" +#include "ss7_route.h" #include "xua_internal.h"
+ /*********************************************************************** * Helper Functions ***********************************************************************/ diff --git a/src/sccp_user.c b/src/sccp_user.c index 277ca5f..6edbfd1 100644 --- a/src/sccp_user.c +++ b/src/sccp_user.c @@ -39,6 +39,7 @@
#include "sccp_internal.h" #include "xua_internal.h" +#include "ss7_route.h" #include "ss7_route_table.h" #include "ss7_internal.h"
@@ -589,7 +590,7 @@ rt = ss7_route_table_find_route_by_dpc_mask(ss7->rtable_system, 0, 0); if (!rt) { LOGP(DLSCCP, LOGL_NOTICE, "%s: Creating default route\n", name); - rt = osmo_ss7_route_create(ss7->rtable_system, 0, 0, + rt = ss7_route_create(ss7->rtable_system, 0, 0, as->cfg.name); if (!rt) goto out_as; @@ -711,7 +712,7 @@ osmo_ss7_asp_destroy(asp); out_rt: if (rt_created) - osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt); out_as: if (as_created) osmo_ss7_as_destroy(as); @@ -838,7 +839,7 @@ goto out_strings;
/* route only selected PC to the client */ - rt = osmo_ss7_route_create(ss7->rtable_system, pc, 0xffff, as_name); + rt = ss7_route_create(ss7->rtable_system, pc, 0xffff, as_name); if (!rt) goto out_as;
@@ -868,7 +869,7 @@ out_asp: osmo_ss7_asp_destroy(asp); out_rt: - osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt); out_as: osmo_ss7_as_destroy(as); out_strings: diff --git a/src/ss7_internal.h b/src/ss7_internal.h index 61fe20e..dcc2ef1 100644 --- a/src/ss7_internal.h +++ b/src/ss7_internal.h @@ -50,13 +50,3 @@ SS7_ASP_CTR_PKT_RX_UNKNOWN, SS7_ASP_CTR_PKT_TX_TOTAL, }; - -/*********************************************************************** - * SS7 Routes - ***********************************************************************/ -#define OSMO_SS7_ROUTE_PRIO_DEFAULT 5 - -struct osmo_ss7_route * -ss7_route_alloc(struct osmo_ss7_route_table *rtbl, uint32_t pc, uint32_t mask); -int ss7_route_set_linkset(struct osmo_ss7_route *rt, const char *linkset_name); -int ss7_route_insert(struct osmo_ss7_route *rt); diff --git a/src/ss7_route.h b/src/ss7_route.h new file mode 100644 index 0000000..7a29b7b --- /dev/null +++ b/src/ss7_route.h @@ -0,0 +1,56 @@ +#pragma once + +#include <stdint.h> +#include <osmocom/core/linuxlist.h> + +/*********************************************************************** + * SS7 Routes + ***********************************************************************/ + +struct osmo_ss7_instance; +struct osmo_ss7_route_table; +struct osmo_ss7_linkset; +struct osmo_ss7_as; + +#define OSMO_SS7_ROUTE_PRIO_DEFAULT 5 + +struct osmo_ss7_route { + /*! member in \ref osmo_ss7_route_table.routes */ + struct llist_head list; + /*! \ref osmo_ss7_route_table to which we belong */ + struct osmo_ss7_route_table *rtable; + + struct { + /*! pointer to linkset (destination) of route */ + struct osmo_ss7_linkset *linkset; + /*! pointer to Application Server */ + struct osmo_ss7_as *as; + } dest; + + struct { + /* FIXME: presence? */ + uint32_t pc; + uint32_t mask; + /*! human-specified linkset name */ + char *linkset_name; + /*! lower priority is higher */ + uint32_t priority; + uint8_t qos_class; + } cfg; +}; + +struct osmo_ss7_route * +ss7_route_alloc(struct osmo_ss7_route_table *rtbl, uint32_t pc, uint32_t mask); +struct osmo_ss7_route * +ss7_route_create(struct osmo_ss7_route_table *rtbl, uint32_t dpc, + uint32_t mask, const char *linkset_name); +void ss7_route_destroy(struct osmo_ss7_route *rt); + +struct osmo_ss7_route * +ss7_route_find_dpc(struct osmo_ss7_route_table *rtbl, uint32_t dpc); +struct osmo_ss7_route * +ss7_route_find_dpc_mask(struct osmo_ss7_route_table *rtbl, uint32_t dpc, + uint32_t mask); + +int ss7_route_set_linkset(struct osmo_ss7_route *rt, const char *linkset_name); +int ss7_route_insert(struct osmo_ss7_route *rt); diff --git a/src/xua_as_fsm.c b/src/xua_as_fsm.c index 2083ca0..dff0d06 100644 --- a/src/xua_as_fsm.c +++ b/src/xua_as_fsm.c @@ -23,6 +23,7 @@ #include <osmocom/sigtran/protocol/sua.h> #include <osmocom/sigtran/protocol/m3ua.h>
+#include "ss7_route.h" #include "ss7_route_table.h" #include "xua_asp_fsm.h" #include "xua_as_fsm.h" @@ -236,7 +237,7 @@
/* As opposed to M3UA, there is no RKM and we have to implicitly * automatically add a route once an IPA connection has come up */ - if (osmo_ss7_route_create(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff, as->cfg.name)) + if (ss7_route_create(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff, as->cfg.name)) xafp->ipa_route_created = true; }
@@ -269,7 +270,7 @@ return; }
- osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt); xafp->ipa_route_created = false; }
diff --git a/src/xua_rkm.c b/src/xua_rkm.c index 774cf3a..38fb903 100644 --- a/src/xua_rkm.c +++ b/src/xua_rkm.c @@ -28,6 +28,7 @@ #include <osmocom/sigtran/osmo_ss7.h> #include <osmocom/sigtran/protocol/m3ua.h>
+#include "ss7_route.h" #include "ss7_route_table.h" #include "xua_internal.h" #include "xua_as_fsm.h" @@ -260,7 +261,7 @@ as->cfg.routing_key.context = rctx;
/* add route for that routing key */ - rt = osmo_ss7_route_create(as->inst->rtable_system, dpc, 0xFFFFFF, namebuf); + rt = ss7_route_create(as->inst->rtable_system, dpc, 0xFFFFFF, namebuf); if (!rt) { LOGPASP(asp, DLSS7, LOGL_ERROR, "RKM: Cannot insert route for DPC %s / as %s\n", osmo_ss7_pointcode_print(asp->inst, dpc), namebuf); @@ -271,7 +272,7 @@
/* append to list of newly assigned as */ if (*nas_idx >= max_nas_idx) { - osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt); osmo_ss7_as_destroy(as); LOGPASP(asp, DLSS7, LOGL_ERROR, "RKM: not enough room for newly assigned AS (max %u AS)\n", max_nas_idx+1); @@ -392,7 +393,7 @@ * route and destroy the AS */ if (as->rkm_dyn_allocated) { /* remove route + AS definition */ - osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt); osmo_ss7_as_destroy(as); } /* report success */ diff --git a/tests/ss7/ss7_test.c b/tests/ss7/ss7_test.c index 5f00f78..79f6912 100644 --- a/tests/ss7/ss7_test.c +++ b/tests/ss7/ss7_test.c @@ -1,3 +1,4 @@ +#include "../src/ss7_route.h" #include "../src/ss7_route_table.h" #include "../src/xua_internal.h" #include "../src/xua_asp_fsm.h" @@ -162,14 +163,14 @@
/* route with full mask */ OSMO_ASSERT(ss7_route_table_find_route_by_dpc(rtbl, 12) == NULL); - rt = osmo_ss7_route_create(rtbl, 12, 0xffff, "a"); + rt = ss7_route_create(rtbl, 12, 0xffff, "a"); printf("route with full mask: %s\n", osmo_ss7_route_print(rt)); OSMO_ASSERT(rt); OSMO_ASSERT(ss7_route_table_find_route_by_dpc(rtbl, 12) == rt); - osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt);
/* route with partial mask */ - rt = osmo_ss7_route_create(rtbl, 8, 0xfff8, "a"); + rt = ss7_route_create(rtbl, 8, 0xfff8, "a"); printf("route with partial mask: %s\n", osmo_ss7_route_print(rt)); OSMO_ASSERT(ss7_route_table_find_route_by_dpc(rtbl, 8) == rt); OSMO_ASSERT(ss7_route_table_find_route_by_dpc(rtbl, 9) == rt); @@ -178,23 +179,23 @@ OSMO_ASSERT(ss7_route_table_find_route_by_dpc(rtbl, 16) == NULL); /* insert more specific route for 12, must have higher priority * than existing one */ - rt12 = osmo_ss7_route_create(rtbl, 12, 0xffff, "b"); + rt12 = ss7_route_create(rtbl, 12, 0xffff, "b"); OSMO_ASSERT(ss7_route_table_find_route_by_dpc(rtbl, 12) == rt12); OSMO_ASSERT(ss7_route_table_find_route_by_dpc(rtbl, 15) == rt); OSMO_ASSERT(ss7_route_table_find_route_by_dpc(rtbl, 16) == NULL); /* add a default route, which should have lowest precedence */ - rtdef = osmo_ss7_route_create(rtbl, 0, 0, "a"); + rtdef = ss7_route_create(rtbl, 0, 0, "a"); OSMO_ASSERT(ss7_route_table_find_route_by_dpc(rtbl, 12) == rt12); OSMO_ASSERT(ss7_route_table_find_route_by_dpc(rtbl, 15) == rt); OSMO_ASSERT(ss7_route_table_find_route_by_dpc(rtbl, 16) == rtdef);
- osmo_ss7_route_destroy(rtdef); - osmo_ss7_route_destroy(rt12); - osmo_ss7_route_destroy(rt); + ss7_route_destroy(rtdef); + ss7_route_destroy(rt12); + ss7_route_destroy(rt);
- rt = osmo_ss7_route_create(rtbl, 8, 0xfff9, "a"); + rt = ss7_route_create(rtbl, 8, 0xfff9, "a"); printf("route with non-consecutive mask: %s\n", osmo_ss7_route_print(rt)); - osmo_ss7_route_destroy(rt); + ss7_route_destroy(rt);
osmo_ss7_linkset_destroy(lset_a); osmo_ss7_linkset_destroy(lset_b);