diff --git a/src/Makefile.am b/src/Makefile.am index d609445f..13cdac98 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,7 +8,7 @@ COMMON_LDADD = -lsctp bin_PROGRAMS = hnbgw -hnbgw_SOURCES = hnbap_encoder.c hnbap_decoder.c rua_encoder.c rua_decoder.c ranap_common.c rua_common.c hnbap_common.c iu_helpers.c asn1helpers.c hnbgw.c hnbgw_hnbap.c hnbgw_rua.c hnbgw_ranap.c ranap_decoder.c ranap_encoder.c ranap_msg_factory.c +hnbgw_SOURCES = hnbap_encoder.c hnbap_decoder.c rua_encoder.c rua_decoder.c ranap_common.c rua_common.c hnbap_common.c iu_helpers.c asn1helpers.c hnbgw.c hnbgw_hnbap.c hnbgw_rua.c hnbgw_ranap.c ranap_decoder.c ranap_encoder.c ranap_msg_factory.c context_map.c hnbgw_LDADD = $(OSMOCORE_LIBS) $(OSMOVTY_LIBS) $(OSMOGSM_LIBS) $(ASN1C_LIBS) $(OSMOSIGTRAN_LIBS) $(COMMON_LDADD) hnbap/libosmo-asn1-hnbap.a rua/libosmo-asn1-rua.a ranap/libosmo-asn1-ranap.a BUILT_SOURCES = hnbap_decoder.c hnbap_encoder.c rua_decoder.c rua_encoder.c ranap_decoder.c ranap_encoder.c diff --git a/src/context_map.c b/src/context_map.c new file mode 100644 index 00000000..8fc48ba4 --- /dev/null +++ b/src/context_map.c @@ -0,0 +1,164 @@ +/* Mapper between RUA ContextID (24 bit, per HNB) and the SUA/SCCP + * Connection ID (32bit, per signalling link) */ + +/* (C) 2015 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/* an expired mapping is destroyed after 1..2 * EXPIRY_TIMER_SECS */ +#define EXPIRY_TIMER_SECS 23 + +#include + +#include "hnbgw.h" +#include "context_map.h" + +/* is a given SCCP USER SAP Connection ID in use for a given CN link? */ +static int cn_id_in_use(struct hnbgw_cnlink *cn, uint32_t id) +{ + struct hnbgw_context_map *map; + + llist_for_each_entry(map, &cn->map_list, cn_list) { + if (map->scu_conn_id == id) + return 1; + } + return 0; +} + +/* try to allocate a new SCCP User SAP Connection ID */ +static int alloc_cn_conn_id(struct hnbgw_cnlink *cn, uint32_t *id_out) +{ + uint32_t i; + uint32_t id; + + for (i = 0; i < 0xffffffff; i++) { + id = cn->next_conn_id++; + if (!cn_id_in_use(cn, id)) { + *id_out = id; + return 1; + } + } + return -1; +} + +/* Map from a HNB + ContextID to the SCCP-side Connection ID */ +struct hnbgw_context_map * +context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id, + struct hnbgw_cnlink *cn_if_new) +{ + struct hnbgw_context_map *map; + uint32_t new_scu_conn_id; + + llist_for_each_entry(map, &hnb->map_list, hnb_list) { + if (map->state != MAP_S_ACTIVE) + continue; + if (map->rua_ctx_id == rua_ctx_id) { + return map; + } + } + + /* FIXME: allocated CN side ID! */ + if (alloc_cn_conn_id(cn_if_new, &new_scu_conn_id) < 0) + return NULL; + + /* alloate a new map entry */ + map = talloc_zero(hnb, struct hnbgw_context_map); + map->state = MAP_S_NULL; + map->cn_link = cn_if_new; + map->hnb_ctx = hnb; + map->rua_ctx_id = rua_ctx_id; + + /* put it into both lists */ + llist_add_tail(&map->hnb_list, &hnb->map_list); + llist_add_tail(&map->cn_list, &cn_if_new->map_list); + map->state = MAP_S_ACTIVE; + + return map; +} + +/* Map from a CN + Connection ID to HNB + Context ID */ +struct hnbgw_context_map * +context_map_by_cn(struct hnbgw_cnlink *cn, uint32_t scu_conn_id) +{ + struct hnbgw_context_map *map; + + /* FIXME: allocated HNB side ID! */ + + llist_for_each_entry(map, &cn->map_list, cn_list) { + if (map->state != MAP_S_ACTIVE) + continue; + if (map->scu_conn_id == scu_conn_id) { + return map; + } + } + /* we don't allocate new mappings in the CN->HNB + * direction, as the RUA=SCCP=SUA connections are always + * established from HNB towards CN. */ + return NULL; +} + +void context_map_deactivate(struct hnbgw_context_map *map) +{ + /* set the state to reserved. We still show up in the list and + * avoid re-allocation of the context-id until we are cleaned up + * by the context_map garbage collector timer */ + + if (map->state != MAP_S_RESERVED2) + map->state = MAP_S_RESERVED1; +} + +static struct osmo_timer_list context_map_tmr; + +static void context_map_tmr_cb(void *data) +{ + struct hnb_gw *gw = data; + struct hnbgw_cnlink *cn; + + /* iterate over list of core network (links) */ + llist_for_each_entry(cn, &gw->cn_list, list) { + struct hnbgw_context_map *map; + + llist_for_each_entry(map, &cn->map_list, cn_list) { + switch (map->state) { + case MAP_S_RESERVED1: + /* first time we see this reserved + * entry: mark it for stage 2 */ + map->state = MAP_S_RESERVED2; + break; + case MAP_S_RESERVED2: + /* first time we see this reserved + * entry: remove it */ + map->state = MAP_S_NULL; + llist_del(&map->cn_list); + llist_del(&map->hnb_list); + talloc_free(map); + break; + default: + break; + } + } + } + /* re-schedule this timer */ + osmo_timer_schedule(&context_map_tmr, EXPIRY_TIMER_SECS, 0); +} + +int context_map_init(struct hnb_gw *gw) +{ + context_map_tmr.cb = context_map_tmr_cb; + context_map_tmr.data = gw; + osmo_timer_schedule(&context_map_tmr, EXPIRY_TIMER_SECS, 0); +} diff --git a/src/context_map.h b/src/context_map.h new file mode 100644 index 00000000..c1a4495b --- /dev/null +++ b/src/context_map.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +enum hnbgw_context_map_state { + MAP_S_NULL, + MAP_S_ACTIVE, /* currently active map */ + MAP_S_RESERVED1, /* just disconnected, still resrved */ + MAP_S_RESERVED2, /* still reserved */ +}; + +struct hnb_context; +struct hnbgw_cnlink; + +struct hnbgw_context_map { + /* entry in the per-CN list of mappings */ + struct llist_head cn_list; + /* entry in the per-HNB list of mappings */ + struct llist_head hnb_list; + /* pointer to HNB */ + struct hnb_context *hnb_ctx; + /* pointer to CN */ + struct hnbgw_cnlink *cn_link; + /* RUA contxt ID */ + uint32_t rua_ctx_id; + /* SCCP User SAP connection ID */ + uint32_t scu_conn_id; + + enum hnbgw_context_map_state state; +}; + + +struct hnbgw_context_map * +context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id, + struct hnbgw_cnlink *cn_if_new); + +struct hnbgw_context_map * +context_map_by_cn(struct hnbgw_cnlink *cn, uint32_t scu_conn_id); + +void context_map_deactivate(struct hnbgw_context_map *map); + +int context_map_init(struct hnb_gw *gw); diff --git a/src/hnbgw.c b/src/hnbgw.c index 0dbe37db..bc8be99b 100644 --- a/src/hnbgw.c +++ b/src/hnbgw.c @@ -53,6 +53,7 @@ #include "hnbgw.h" #include "hnbgw_hnbap.h" #include "hnbgw_rua.h" +#include "context_map.h" static void *tall_hnb_ctx; static void *tall_ue_ctx; @@ -210,6 +211,47 @@ static int hnb_write_cb(struct osmo_fd *fd, struct msgb *msg) return rc; } +struct hnb_context *hnb_context_alloc(struct hnb_gw *gw, int new_fd) +{ + struct hnb_context *ctx; + + ctx = talloc_zero(tall_hnb_ctx, struct hnb_context); + if (!ctx) + return NULL; + + ctx->gw = gw; + osmo_wqueue_init(&ctx->wqueue, 16); + ctx->wqueue.bfd.data = ctx; + ctx->wqueue.bfd.fd = new_fd; + ctx->wqueue.bfd.when = BSC_FD_READ; + ctx->wqueue.read_cb = hnb_read_cb; + ctx->wqueue.write_cb = hnb_write_cb; + osmo_fd_register(&ctx->wqueue.bfd); + + llist_add_tail(&ctx->list, &gw->hnb_list); +} + +void hnb_context_release(struct hnb_context *ctx) +{ + struct hnbgw_context_map *map, *map2; + + /* remove from the list of HNB contexts */ + llist_del(&ctx->list); + + /* deactivate all context maps */ + llist_for_each_entry_safe(map, map2, &ctx->map_list, hnb_list) { + /* remove it from list, as HNB context will soon be + * gone. Let's hope the seccond osmo_llist_del in the + * map garbage collector wors fine? */ + llist_del(&map->hnb_list); + context_map_deactivate(map); + } + /* FIXME: flush write queue items */ + osmo_fd_unregister(&ctx->wqueue.bfd); + + talloc_free(ctx); +} + /*! call-back when the listen FD has something to read */ static int listen_fd_cb(struct osmo_fd *fd, unsigned int what) { @@ -226,21 +268,10 @@ static int listen_fd_cb(struct osmo_fd *fd, unsigned int what) LOGP(DMAIN, LOGL_INFO, "SCTP Connection accept()ed\n"); - ctx = talloc_zero(tall_hnb_ctx, struct hnb_context); + ctx = hnb_context_alloc(gw, new_fd); if (!ctx) return -ENOMEM; - ctx->gw = gw; - osmo_wqueue_init(&ctx->wqueue, 16); - ctx->wqueue.bfd.data = ctx; - ctx->wqueue.bfd.fd = new_fd; - ctx->wqueue.bfd.when = BSC_FD_READ; - ctx->wqueue.read_cb = hnb_read_cb; - ctx->wqueue.write_cb = hnb_write_cb; - osmo_fd_register(&ctx->wqueue.bfd); - - llist_add_tail(&ctx->list, &gw->hnb_list); - return 0; } @@ -380,6 +411,8 @@ int main(int argc, char **argv) INIT_LLIST_HEAD(&g_hnb_gw.hnb_list); INIT_LLIST_HEAD(&g_hnb_gw.ue_list); + context_map_init(&g_hnb_gw); + rc = osmo_init_logging(&hnbgw_log_info); if (rc < 0) exit(1); diff --git a/src/hnbgw.h b/src/hnbgw.h index fede5eef..05094152 100644 --- a/src/hnbgw.h +++ b/src/hnbgw.h @@ -3,10 +3,13 @@ #include #include #include +#include +#include #define DEBUG #include + enum { DMAIN, DHNBAP, @@ -39,6 +42,37 @@ struct umts_cell_id { struct hnb_gw; +enum hnbgw_cnlink_state { + /* we have just been initialized or were disconnected */ + CNLINK_S_NULL, + /* establishment of the SUA/SCCP link is pending */ + CNLINK_S_EST_PEND, + /* establishment of the SUA/SCCP link was confirmed */ + CNLINK_S_EST_CONF, + /* we have esnt the RANAP RESET and wait for the ACK */ + CNLINK_S_EST_RST_TX_WAIT_ACK, + /* we have received the RANAP RESET ACK and are active */ + CNLINK_S_EST_ACTIVE, +}; + +struct hnbgw_cnlink { + struct llist_head list; + enum hnbgw_cnlink_state state; + struct hnb_gw *gw; + /* are we a PS connection (1) or CS (0) */ + int is_ps; + /* timer for re-transmitting the RANAP Reset */ + struct osmo_timer_list T_RafC; + /* reference to the SCCP User SAP by which we communicate */ + struct osmo_sua_link *sua_link; + struct osmo_sccp_addr local_addr; + struct osmo_sccp_addr remote_addr; + uint32_t next_conn_id; + + /* linked list of hnbgw_context_map */ + struct llist_head map_list; +}; + struct hnb_context { /*! Entry in HNB-global list of HNB */ struct llist_head list; @@ -55,6 +89,9 @@ struct hnb_context { uint16_t hnbap_stream; /*! SCTP stream ID for RUA */ uint16_t rua_stream; + + /* linked list of hnbgw_context_map */ + struct llist_head map_list; }; struct ue_context { @@ -80,6 +117,7 @@ struct hnb_gw { struct osmo_fd listen_fd; struct llist_head hnb_list; struct llist_head ue_list; + struct llist_head cn_list; uint32_t next_ue_ctx_id; }; @@ -89,3 +127,6 @@ struct ue_context *ue_context_by_id(uint32_t id); struct ue_context *ue_context_by_imsi(const char *imsi); struct ue_context *ue_context_alloc(struct hnb_context *hnb, const char *imsi); void ue_context_free(struct ue_context *ue); + +struct hnb_context *hnb_context_alloc(struct hnb_gw *gw, int new_fd); +void hnb_context_release(struct hnb_context *ctx);