diff options
author | Thorsten Alteholz <debian@alteholz.de> | 2018-07-16 12:34:42 +0200 |
---|---|---|
committer | Thorsten Alteholz <debian@alteholz.de> | 2018-07-16 12:34:42 +0200 |
commit | 0bdaae23af07b9d02fc8cf0e56a1165a5b5987c0 (patch) | |
tree | f5c147f62bc857f126ffebb0b68b9c847c64adc4 | |
parent | 1dc1480e7f024f8807f2d28a031bf9fc2040a401 (diff) |
Import Upstream version 0.3.0
-rwxr-xr-x | asn1/utils/asn1tostruct.py | 9 | ||||
-rw-r--r-- | configure.ac | 49 | ||||
-rwxr-xr-x | contrib/jenkins.sh | 9 | ||||
-rw-r--r-- | include/osmocom/iuh/context_map.h | 5 | ||||
-rw-r--r-- | include/osmocom/iuh/hnbgw.h | 17 | ||||
-rw-r--r-- | include/osmocom/ranap/iu_helpers.h | 6 | ||||
-rw-r--r-- | src/Makefile.am | 15 | ||||
-rw-r--r-- | src/context_map.c | 16 | ||||
-rw-r--r-- | src/hnbgw.c | 154 | ||||
-rw-r--r-- | src/hnbgw_cn.c | 62 | ||||
-rw-r--r-- | src/hnbgw_hnbap.c | 89 | ||||
-rw-r--r-- | src/hnbgw_ranap.c | 52 | ||||
-rw-r--r-- | src/hnbgw_rua.c | 39 | ||||
-rw-r--r-- | src/hnbgw_vty.c | 120 | ||||
-rw-r--r-- | src/iu_client.c | 227 | ||||
-rw-r--r-- | src/iu_helpers.c | 53 | ||||
-rw-r--r-- | src/ranap_common.c | 4 | ||||
-rw-r--r-- | src/ranap_common_cn.c | 14 | ||||
-rw-r--r-- | src/ranap_msg_factory.c | 62 | ||||
-rw-r--r-- | src/tests/Makefile.am | 2 | ||||
-rw-r--r-- | src/tests/hnb-test.c | 10 | ||||
-rw-r--r-- | src/tests/test-helpers.c | 4 | ||||
-rw-r--r-- | src/tests/test-hnbap.c | 4 | ||||
-rw-r--r-- | src/tests/test-ranap.c | 2 | ||||
-rw-r--r-- | src/tests/test_common.c | 25 | ||||
-rw-r--r-- | src/tests/test_common.h | 1 |
26 files changed, 774 insertions, 276 deletions
diff --git a/asn1/utils/asn1tostruct.py b/asn1/utils/asn1tostruct.py index 8364c27..0bba9dd 100755 --- a/asn1/utils/asn1tostruct.py +++ b/asn1/utils/asn1tostruct.py @@ -311,7 +311,12 @@ for key in iesDefs: f.write(" memset(%s, 0, sizeof(%s_t));\n" % (lowerFirstCamelWord(re.sub('-', '_', key)), prefix + re.sub('-', '_', key))) f.write(" %s_DEBUG(\"Decoding message %s (%%s:%%d)\\n\", __FILE__, __LINE__);\n\n" % (fileprefix.upper(), prefix + re.sub('-', '_', keyName))) - f.write(" ANY_to_type_aper(any_p, &asn_DEF_%s, (void**)&%s_p);\n\n" % (asn1cStruct, asn1cStructfirstlower)) + f.write(" tempDecoded = ANY_to_type_aper(any_p, &asn_DEF_%s, (void**)&%s_p);\n\n" % (asn1cStruct, asn1cStructfirstlower)) + f.write(" if (tempDecoded < 0 || %s_p == NULL) {\n" % (asn1cStructfirstlower)) + f.write(" %s_DEBUG(\"Decoding of message %s failed\\n\");\n" % (fileprefix.upper(), prefix + re.sub('-', '_', keyName))) + f.write(" return -1;\n") + f.write(" }\n\n") + f.write(" for (i = 0; i < %s_p->%slist.count; i++) {\n" % (asn1cStructfirstlower, iesaccess)) f.write(" %sIE_t *ie_p;\n" % (prefix)) f.write(" ie_p = %s_p->%slist.array[i];\n" % (asn1cStructfirstlower, iesaccess)) @@ -384,6 +389,8 @@ for key in iesDefs: iename = re.sub('id-', '', ie[0]) ienameunderscore = lowerFirstCamelWord(re.sub('-', '_', iename)) f.write(" ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_%s, &%s->%s);\n" % (ietypeunderscore, lowerFirstCamelWord(re.sub('-', '_', key)), ienameunderscore)) + + f.write(" return 0;\n") f.write("}\n\n") for key in iesDefs: diff --git a/configure.ac b/configure.ac index ff0501a..bb99dfb 100644 --- a/configure.ac +++ b/configure.ac @@ -33,18 +33,55 @@ if test "x$PKG_CONFIG_INSTALLED" = "xno"; then fi PKG_PROG_PKG_CONFIG([0.20]) -PKG_CHECK_MODULES(OSMOCORE, libosmocore >= 0.10.0) -PKG_CHECK_MODULES(OSMOGSM, libosmogsm >= 0.10.0) -PKG_CHECK_MODULES(OSMOVTY, libosmovty >= 0.10.0) -PKG_CHECK_MODULES(OSMONETIF, libosmo-netif >= 0.1.0) -PKG_CHECK_MODULES(OSMOSIGTRAN, libosmo-sigtran >= 0.8.0) -PKG_CHECK_MODULES(ASN1C, libasn1c >= 0.9.28) +PKG_CHECK_MODULES(OSMOCORE, libosmocore >= 0.11.0) +PKG_CHECK_MODULES(OSMOGSM, libosmogsm >= 0.11.0) +PKG_CHECK_MODULES(OSMOVTY, libosmovty >= 0.11.0) +PKG_CHECK_MODULES(OSMOCTRL, libosmoctrl >= 0.11.0) +PKG_CHECK_MODULES(OSMONETIF, libosmo-netif >= 0.2.0) +PKG_CHECK_MODULES(OSMOSIGTRAN, libosmo-sigtran >= 0.9.0) +PKG_CHECK_MODULES(ASN1C, libasn1c >= 0.9.30) AC_CONFIG_MACRO_DIR([m4]) dnl checks for header files AC_HEADER_STDC +AC_ARG_ENABLE(sanitize, + [AS_HELP_STRING( + [--enable-sanitize], + [Compile with address sanitizer enabled], + )], + [sanitize=$enableval], [sanitize="no"]) +if test x"$sanitize" = x"yes" +then + CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined" + CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" +fi + +AC_ARG_ENABLE(werror, + [AS_HELP_STRING( + [--enable-werror], + [Turn all compiler warnings into errors, with exceptions: + a) deprecation (allow upstream to mark deprecation without breaking builds); + b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) + ] + )], + [werror=$enableval], [werror="no"]) +if test x"$werror" = x"yes" +then + WERROR_FLAGS="-Werror" + WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" + WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" + CFLAGS="$CFLAGS $WERROR_FLAGS" + CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" +fi + +CFLAGS="$CFLAGS -Wall" +CPPFLAGS="$CPPFLAGS -Wall" + +AC_MSG_RESULT([CFLAGS="$CFLAGS"]) +AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) + AC_OUTPUT( libosmo-ranap.pc src/Makefile diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh index cda0e2c..617aeac 100755 --- a/contrib/jenkins.sh +++ b/contrib/jenkins.sh @@ -7,15 +7,16 @@ deps="$base/deps" inst="$deps/install" export deps inst +osmo-clean-workspace.sh + mkdir "$deps" || true -rm -rf "$inst" verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]") export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" export LD_LIBRARY_PATH="$inst/lib" -osmo-build-dep.sh libosmocore +osmo-build-dep.sh libosmocore "" --disable-doxygen osmo-build-dep.sh libosmo-abis osmo-build-dep.sh libosmo-netif osmo-build-dep.sh libosmo-sccp @@ -33,7 +34,7 @@ echo set -x autoreconf --install --force -./configure +./configure --enable-sanitize # Verify that checked-in asn1 code is identical to regenerated asn1 code PATH="$inst/bin:$PATH" $MAKE $PARALLEL_MAKE -C src regen @@ -54,3 +55,5 @@ $MAKE check \ || cat-testlogs.sh $MAKE distcheck \ || cat-testlogs.sh + +osmo-clean-workspace.sh diff --git a/include/osmocom/iuh/context_map.h b/include/osmocom/iuh/context_map.h index 8d957d6..6279b91 100644 --- a/include/osmocom/iuh/context_map.h +++ b/include/osmocom/iuh/context_map.h @@ -8,8 +8,13 @@ enum hnbgw_context_map_state { MAP_S_ACTIVE, /* currently active map */ MAP_S_RESERVED1, /* just disconnected, still resrved */ MAP_S_RESERVED2, /* still reserved */ + MAP_S_NUM_STATES /* Number of states, keep this at the end */ }; +extern const struct value_string hnbgw_context_map_state_names[]; +static inline const char *hnbgw_context_map_state_name(enum hnbgw_context_map_state val) +{ return get_value_string(hnbgw_context_map_state_names, val); } + struct hnb_context; struct hnbgw_cnlink; diff --git a/include/osmocom/iuh/hnbgw.h b/include/osmocom/iuh/hnbgw.h index 58bdab4..b79bcc1 100644 --- a/include/osmocom/iuh/hnbgw.h +++ b/include/osmocom/iuh/hnbgw.h @@ -6,7 +6,7 @@ #include <osmocom/core/timer.h> #include <osmocom/sigtran/sccp_sap.h> #include <osmocom/sigtran/osmo_ss7.h> - +#include <osmocom/ctrl/control_if.h> #define DEBUG #include <osmocom/core/logging.h> @@ -18,6 +18,10 @@ enum { DRANAP, }; +enum hnb_ctrl_node { + CTRL_NODE_HNB = _LAST_CTRL_NODE, + _LAST_CTRL_NODE_HNB +}; #define HNBGW_LOCAL_IP_DEFAULT "0.0.0.0" /* TODO: CS and PS now both connect to OsmoSTP, i.e. that's always going to be the same address. Drop the @@ -93,6 +97,10 @@ struct hnb_context { /*! SCTP stream ID for RUA */ uint16_t rua_stream; + /*! True if a HNB-REGISTER-REQ from this HNB has been accepted. Note that + * this entire data structure is freed if the HNB sends HNB-DE-REGISTER-REQ. */ + bool hnb_registered; + /* linked list of hnbgw_context_map */ struct llist_head map_list; }; @@ -129,7 +137,7 @@ struct hnb_gw { struct llist_head ue_list; /* next availble UE Context ID */ uint32_t next_ue_ctx_id; - + struct ctrl_handle *ctrl; /* currently active CN links for CS and PS */ struct { struct osmo_sccp_instance *client; @@ -142,6 +150,9 @@ struct hnb_gw { extern void *talloc_asn1_ctx; +struct hnb_context *hnb_context_by_id(struct hnb_gw *gw, uint32_t cid); +unsigned hnb_contexts(const struct hnb_gw *gw); + struct ue_context *ue_context_by_id(struct hnb_gw *gw, uint32_t id); struct ue_context *ue_context_by_imsi(struct hnb_gw *gw, const char *imsi); struct ue_context *ue_context_by_tmsi(struct hnb_gw *gw, uint32_t tmsi); @@ -150,7 +161,7 @@ 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, struct osmo_stream_srv_link *link, int new_fd); -void hnb_context_release(struct hnb_context *ctx); +void hnb_context_release(struct hnb_context *ctx, bool destroy_conn); void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx); int hnbgw_vty_go_parent(struct vty *vty); diff --git a/include/osmocom/ranap/iu_helpers.h b/include/osmocom/ranap/iu_helpers.h index 109b6da..9d801be 100644 --- a/include/osmocom/ranap/iu_helpers.h +++ b/include/osmocom/ranap/iu_helpers.h @@ -3,5 +3,11 @@ #include <stdint.h> #include <sys/types.h> +#include <osmocom/ranap/RANAP_IuTransportAssociation.h> +#include <osmocom/ranap/RANAP_TransportLayerAddress.h> + int ranap_bcd_decode(char *out, size_t out_len, const uint8_t *in, size_t in_len); int ranap_imsi_encode(uint8_t *out, size_t out_len, const char *in); +int ranap_transp_assoc_decode(uint16_t *port, const RANAP_IuTransportAssociation_t *transp_assoc); +int ranap_transp_layer_addr_decode(char *addr, unsigned int addr_len, + const RANAP_TransportLayerAddress_t *trasp_layer_addr); diff --git a/src/Makefile.am b/src/Makefile.am index d815394..7802384 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,18 +42,17 @@ gen_ranap.stamp: $(ASN1_ROOT)/ranap/RANAP-PDU-Contents.asn $(ASN1TOSTRUCT) ranap_decoder.c ranap_encoder.c: gen_ranap.stamp -AM_CFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include \ - $(OSMOCORE_CFLAGS) $(OSMOVTY_CFLAGS) $(OSMOGSM_CFLAGS) \ +AM_CFLAGS = -Wall -I$(top_srcdir)/include -I$(top_builddir)/include \ + $(OSMOCORE_CFLAGS) $(OSMOVTY_CFLAGS) $(OSMOCTRL_CFLAGS) $(OSMOGSM_CFLAGS) \ $(OSMONETIF_CFLAGS) $(ASN1C_CFLAGS) $(OSMOSIGTRAN_CFLAGS) -COMMON_LDADD = -lsctp # build the shared RANAP library # -RANAP_LIBVERSION=1:0:0 +RANAP_LIBVERSION=2:0:0 lib_LTLIBRARIES = libosmo-ranap.la libosmo_ranap_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(RANAP_LIBVERSION) -libosmo_ranap_la_LIBADD = $(OSMOCORE_LIBS) $(OSMOGSM_LIBS) $(OSMOSIGTRAN_LIBS) \ - $(ASN1C_LIBS) $(COMMON_LDADD) ranap/libosmo-asn1-ranap.la +libosmo_ranap_la_LIBADD = $(OSMOCORE_LIBS) $(OSMOGSM_LIBS) $(OSMOVTY_LIBS) $(OSMOSIGTRAN_LIBS) \ + $(ASN1C_LIBS) ranap/libosmo-asn1-ranap.la libosmo_ranap_la_SOURCES = ranap_common.c ranap_encoder.c ranap_decoder.c ranap_msg_factory.c iu_helpers.c \ ranap_common_cn.c iu_client.c iu_client_vty.c @@ -68,8 +67,8 @@ osmo_hnbgw_SOURCES = hnbap_encoder.c hnbap_decoder.c hnbap_common.c \ hnbgw_vty.c \ context_map.c hnbgw_cn.c -osmo_hnbgw_LDADD = $(OSMOCORE_LIBS) $(OSMOVTY_LIBS) $(OSMOGSM_LIBS) \ - $(ASN1C_LIBS) $(OSMOSIGTRAN_LIBS) $(COMMON_LDADD) \ +osmo_hnbgw_LDADD = $(OSMOCORE_LIBS) $(OSMOGSM_LIBS) $(OSMOVTY_LIBS) $(OSMOCTRL_LIBS) \ + $(ASN1C_LIBS) $(OSMOSIGTRAN_LIBS) \ $(OSMONETIF_LIBS) \ hnbap/libosmo-asn1-hnbap.a rua/libosmo-asn1-rua.a \ libosmo-ranap.la diff --git a/src/context_map.c b/src/context_map.c index 0960cb9..dc555bf 100644 --- a/src/context_map.c +++ b/src/context_map.c @@ -27,6 +27,14 @@ #include <osmocom/iuh/hnbgw.h> #include <osmocom/iuh/context_map.h> +const struct value_string hnbgw_context_map_state_names[] = { + {MAP_S_NULL , "not-initialized"}, + {MAP_S_ACTIVE , "active"}, + {MAP_S_RESERVED1, "inactive-reserved"}, + {MAP_S_RESERVED2, "inactive-discard"}, + {0, NULL} +}; + /* 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) { @@ -138,10 +146,10 @@ static void context_map_tmr_cb(void *data) { struct hnb_gw *gw = data; struct hnbgw_cnlink *cn = gw->sccp.cnlink; - struct hnbgw_context_map *map; + struct hnbgw_context_map *map, *next_map; DEBUGP(DMAIN, "Running context mapper garbage collection\n"); - llist_for_each_entry(map, &cn->map_list, cn_list) { + llist_for_each_entry_safe(map, next_map, &cn->map_list, cn_list) { switch (map->state) { case MAP_S_RESERVED1: /* first time we see this reserved @@ -149,7 +157,7 @@ static void context_map_tmr_cb(void *data) map->state = MAP_S_RESERVED2; break; case MAP_S_RESERVED2: - /* first time we see this reserved + /* second time we see this reserved * entry: remove it */ map->state = MAP_S_NULL; llist_del(&map->cn_list); @@ -169,4 +177,6 @@ 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); + + return 0; } diff --git a/src/hnbgw.c b/src/hnbgw.c index c9fdd53..94d8fb9 100644 --- a/src/hnbgw.c +++ b/src/hnbgw.c @@ -41,12 +41,19 @@ #include <osmocom/core/socket.h> #include <osmocom/core/msgb.h> #include <osmocom/core/write_queue.h> - +#include <osmocom/ctrl/control_if.h> +#include <osmocom/ctrl/control_cmd.h> +#include <osmocom/ctrl/control_vty.h> +#include <osmocom/ctrl/ports.h> #include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/logging.h> +#include <osmocom/vty/command.h> +#include <osmocom/vty/ports.h> #include <osmocom/netif/stream.h> +#include <osmocom/ranap/ranap_common.h> + #include <osmocom/sigtran/protocol/m3ua.h> #include <osmocom/sigtran/sccp_sap.h> @@ -69,8 +76,6 @@ void *talloc_asn1_ctx; static struct hnb_gw *g_hnb_gw; -static int listen_fd_cb(struct osmo_fd *fd, unsigned int what); - static struct hnb_gw *hnb_gw_create(void *ctx) { struct hnb_gw *gw = talloc_zero(ctx, struct hnb_gw); @@ -88,6 +93,30 @@ static struct hnb_gw *hnb_gw_create(void *ctx) return gw; } +struct hnb_context *hnb_context_by_id(struct hnb_gw *gw, uint32_t cid) +{ + struct hnb_context *hnb; + + llist_for_each_entry(hnb, &gw->hnb_list, list) { + if (hnb->id.cid == cid) + return hnb; + } + + return NULL; +} + +unsigned hnb_contexts(const struct hnb_gw *gw) +{ + unsigned num_ctx = 0; + struct hnb_context *hnb; + + llist_for_each_entry(hnb, &gw->hnb_list, list) { + num_ctx++; + } + + return num_ctx; +} + struct ue_context *ue_context_by_id(struct hnb_gw *gw, uint32_t id) { struct ue_context *ue; @@ -175,13 +204,19 @@ void ue_context_free(struct ue_context *ue) } static int hnb_close_cb(struct osmo_stream_srv *conn) { + struct hnb_context *hnb = osmo_stream_srv_get_data(conn); + + /* This connection is about to be closed. Destroy the HNB context now. */ + if (hnb) + hnb_context_release(hnb, false); + + return 0; } static int hnb_read_cb(struct osmo_stream_srv *conn) { struct hnb_context *hnb = osmo_stream_srv_get_data(conn); struct msgb *msg = msgb_alloc(IUH_MSGB_SIZE, "Iuh rx"); - int flags = 0; int rc; if (!msg) @@ -199,10 +234,10 @@ static int hnb_read_cb(struct osmo_stream_srv *conn) } else if (rc < 0) { LOGP(DMAIN, LOGL_ERROR, "Error during sctp_recvmsg()\n"); /* FIXME: clean up after disappeared HNB */ - hnb_context_release(hnb); + hnb_context_release(hnb, true); goto out; } else if (rc == 0) { - hnb_context_release(hnb); + hnb_context_release(hnb, true); rc = -1; goto out; @@ -222,12 +257,12 @@ static int hnb_read_cb(struct osmo_stream_srv *conn) case IUH_PPI_SABP: case IUH_PPI_RNA: case IUH_PPI_PUA: - LOGP(DMAIN, LOGL_ERROR, "Unimplemented SCTP PPID=%u received\n", + LOGP(DMAIN, LOGL_ERROR, "Unimplemented SCTP PPID=%lu received\n", msgb_sctp_ppid(msg)); rc = 0; break; default: - LOGP(DMAIN, LOGL_ERROR, "Unknown SCTP PPID=%u received\n", + LOGP(DMAIN, LOGL_ERROR, "Unknown SCTP PPID=%lu received\n", msgb_sctp_ppid(msg)); rc = 0; break; @@ -238,21 +273,6 @@ out: return rc; } -static int hnb_write_cb(struct osmo_fd *fd, struct msgb *msg) -{ - struct hnb_context *ctx = fd->data; - struct sctp_sndrcvinfo sinfo = { - .sinfo_ppid = htonl(msgb_sctp_ppid(msg)), - .sinfo_stream = ctx->hnbap_stream, - }; - int rc; - - rc = sctp_send(fd->fd, msgb_data(msg), msgb_length(msg), - &sinfo, 0); - /* we don't need to msgb_free(), write_queue does this for us */ - return rc; -} - struct hnb_context *hnb_context_alloc(struct hnb_gw *gw, struct osmo_stream_srv_link *link, int new_fd) { struct hnb_context *ctx; @@ -274,7 +294,7 @@ struct hnb_context *hnb_context_alloc(struct hnb_gw *gw, struct osmo_stream_srv_ return ctx; } -void hnb_context_release(struct hnb_context *ctx) +void hnb_context_release(struct hnb_context *ctx, bool destroy_conn) { struct hnbgw_context_map *map, *map2; @@ -291,7 +311,9 @@ void hnb_context_release(struct hnb_context *ctx) context_map_deactivate(map); } ue_context_free_by_hnb(ctx->gw, ctx); - osmo_stream_srv_destroy(ctx->conn); + + if (destroy_conn) + osmo_stream_srv_destroy(ctx->conn); talloc_free(ctx); } @@ -339,7 +361,7 @@ static const struct log_info hnbgw_log_info = { static struct vty_app_info vty_info = { .name = "OsmoHNBGW", - .version = "0", + .version = PACKAGE_VERSION, .go_parent_cb = hnbgw_vty_go_parent, }; @@ -433,11 +455,66 @@ static void handle_options(int argc, char **argv) } } +CTRL_CMD_DEFINE_RO(hnb_info, "info"); +static int get_hnb_info(struct ctrl_cmd *cmd, void *data) +{ + struct hnb_context *hnb = data; + + cmd->reply = talloc_strdup(cmd, hnb->identity_info); + + return CTRL_CMD_REPLY; +} + +CTRL_CMD_DEFINE_RO(hnbs, "num-hnb"); +static int get_hnbs(struct ctrl_cmd *cmd, void *data) +{ + cmd->reply = talloc_asprintf(cmd, "%u", hnb_contexts(data)); + + return CTRL_CMD_REPLY; +} + +int hnb_ctrl_cmds_install() +{ + int rc = 0; + + rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_hnbs); + rc |= ctrl_cmd_install(CTRL_NODE_HNB, &cmd_hnb_info); + + return rc; +} + +static int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i) +{ + const char *token = vector_slot(vline, *i); + struct hnb_context *hnb; + long num; + + switch (*node_type) { + case CTRL_NODE_ROOT: + if (strcmp(token, "hnb") != 0) + return 0; + + (*i)++; + + if (!ctrl_parse_get_num(vline, *i, &num)) + return -ERANGE; + + hnb = hnb_context_by_id(data, num); + if (!hnb) + return -ENODEV; + + *node_data = hnb; + *node_type = CTRL_NODE_HNB; + break; + default: + return 0; + } + + return 1; +} int main(int argc, char **argv) { - struct osmo_sccp_user *sccp_user; - struct osmo_sccp_link *sua_link; struct osmo_stream_srv_link *srv; int rc; @@ -486,21 +563,36 @@ int main(int argc, char **argv) log_set_log_level(osmo_stderr_target, hnbgw_cmdline_config.log_level); - rc = telnet_init_dynif(NULL, g_hnb_gw, vty_get_bind_addr(), 2323); + rc = telnet_init_dynif(tall_hnb_ctx, g_hnb_gw, vty_get_bind_addr(), OSMO_VTY_PORT_HNBGW); if (rc < 0) { perror("Error binding VTY port"); exit(1); } + g_hnb_gw->ctrl = ctrl_interface_setup_dynip2(g_hnb_gw, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_HNBGW, + hnb_ctrl_node_lookup, _LAST_CTRL_NODE_HNB); + if (!g_hnb_gw->ctrl) { + LOGP(DMAIN, LOGL_ERROR, "Failed to create CTRL interface on %s:%u\n", + ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_HNBGW); + exit(1); + } else { + rc = hnb_ctrl_cmds_install(); + if (rc) { + LOGP(DMAIN, LOGL_ERROR, "Failed to install CTRL interface commands\n"); + return 2; + } + } + ranap_set_log_area(DRANAP); - rc = hnbgw_cnlink_init(g_hnb_gw, - "127.0.0.1", M3UA_PORT, "127.0.0.5" /* FIXME: configurable */); + rc = hnbgw_cnlink_init(g_hnb_gw, "127.0.0.1", M3UA_PORT, NULL); if (rc < 0) { LOGP(DMAIN, LOGL_ERROR, "Failed to initialize SCCP link to CN\n"); exit(1); } + LOGP(DHNBAP, LOGL_NOTICE, "Using RNC-Id %u\n", g_hnb_gw->config.rnc_id); + OSMO_ASSERT(g_hnb_gw->config.iuh_local_ip); LOGP(DMAIN, LOGL_NOTICE, "Listening for Iuh at %s %d\n", g_hnb_gw->config.iuh_local_ip, diff --git a/src/hnbgw_cn.c b/src/hnbgw_cn.c index 3b828b4..ede00c8 100644 --- a/src/hnbgw_cn.c +++ b/src/hnbgw_cn.c @@ -19,6 +19,7 @@ */ #include <arpa/inet.h> +#include <errno.h> #include <osmocom/core/msgb.h> #include <osmocom/core/utils.h> @@ -38,13 +39,12 @@ * Outbound RANAP RESET to CN ***********************************************************************/ -int hnbgw_cnlink_change_state(struct hnbgw_cnlink *cnlink, enum hnbgw_cnlink_state state); +void hnbgw_cnlink_change_state(struct hnbgw_cnlink *cnlink, enum hnbgw_cnlink_state state); static int transmit_rst(struct hnb_gw *gw, RANAP_CN_DomainIndicator_t domain, struct osmo_sccp_addr *remote_addr) { struct msgb *msg; - struct msgb *msgprim; RANAP_Cause_t cause = { .present = RANAP_Cause_PR_transmissionNetwork, .choice. transmissionNetwork = RANAP_CauseTransmissionNetwork_signalling_transport_resource_failure, @@ -71,7 +71,7 @@ static void cnlink_trafc_cb(void *data) } /* change the state of a CN Link */ -int hnbgw_cnlink_change_state(struct hnbgw_cnlink *cnlink, enum hnbgw_cnlink_state state) +void hnbgw_cnlink_change_state(struct hnbgw_cnlink *cnlink, enum hnbgw_cnlink_state state) { switch (state) { case CNLINK_S_NULL: @@ -127,9 +127,11 @@ static int cn_ranap_rx_paging_cmd(struct hnbgw_cnlink *cnlink, struct hnb_gw *gw = cnlink->gw; struct hnb_context *hnb; RANAP_PagingIEs_t ies; - int rc = 0; + int rc; rc = ranap_decode_pagingies(&ies, &imsg->value); + if (rc < 0) + return rc; /* FIXME: determine which HNBs to send this Paging command, * rather than broadcasting to all HNBs */ @@ -145,8 +147,6 @@ static int cn_ranap_rx_initiating_msg(struct hnbgw_cnlink *cnlink, RANAP_InitiatingMessage_t *imsg, const uint8_t *data, unsigned int len) { - int rc; - switch (imsg->procedureCode) { case RANAP_ProcedureCode_id_Reset: return cn_ranap_rx_reset_cmd(cnlink, imsg); @@ -161,11 +161,11 @@ static int cn_ranap_rx_initiating_msg(struct hnbgw_cnlink *cnlink, case RANAP_ProcedureCode_id_DirectInformationTransfer: case RANAP_ProcedureCode_id_UplinkInformationExchange: LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP " - "Procedure %u from CN, ignoring\n", imsg->procedureCode); + "Procedure %ld from CN, ignoring\n", imsg->procedureCode); break; default: LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP " - "Procedure %u from CN, ignoring\n", imsg->procedureCode); + "Procedure %ld from CN, ignoring\n", imsg->procedureCode); break; } return 0; @@ -174,8 +174,6 @@ static int cn_ranap_rx_initiating_msg(struct hnbgw_cnlink *cnlink, static int cn_ranap_rx_successful_msg(struct hnbgw_cnlink *cnlink, RANAP_SuccessfulOutcome_t *omsg) { - int rc; - switch (omsg->procedureCode) { case RANAP_ProcedureCode_id_Reset: /* Reset acknowledge */ return cn_ranap_rx_reset_ack(cnlink, omsg); @@ -184,11 +182,11 @@ static int cn_ranap_rx_successful_msg(struct hnbgw_cnlink *cnlink, case RANAP_ProcedureCode_id_DirectInformationTransfer: case RANAP_ProcedureCode_id_UplinkInformationExchange: LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP " - "Procedure %u from CN, ignoring\n", omsg->procedureCode); + "Procedure %ld from CN, ignoring\n", omsg->procedureCode); break; default: LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP " - "Procedure %u from CN, ignoring\n", omsg->procedureCode); + "Procedure %ld from CN, ignoring\n", omsg->procedureCode); break; } return 0; @@ -210,14 +208,18 @@ static int _cn_ranap_rx(struct hnbgw_cnlink *cnlink, RANAP_RANAP_PDU_t *pdu, break; case RANAP_RANAP_PDU_PR_unsuccessfulOutcome: LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP " - "unsuccessful outcome procedure %u from CN, ignoring\n", + "unsuccessful outcome procedure %ld from CN, ignoring\n", pdu->choice.unsuccessfulOutcome.procedureCode); + rc = -ENOTSUP; break; default: LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP " "presence %u from CN, ignoring\n", pdu->present); + rc = -EINVAL; break; } + + return rc; } static int handle_cn_ranap(struct hnbgw_cnlink *cnlink, const uint8_t *data, @@ -357,7 +359,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx) struct osmo_sccp_user *scu = ctx; struct hnbgw_cnlink *cnlink; struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph; - int rc; + int rc = 0; LOGP(DMAIN, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph)); @@ -389,7 +391,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx) case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION): rc = handle_cn_disc_ind(cnlink, &prim->u.disconnect, oph); break; - defualt: + default: LOGP(DMAIN, LOGL_ERROR, "Received unknown prim %u from SCCP USER SAP\n", OSMO_PRIM_HDR(oph)); @@ -398,7 +400,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx) msgb_free(oph->msg); - return 0; + return rc; } static bool addr_has_pc_and_ssn(const struct osmo_sccp_addr *addr) @@ -411,13 +413,16 @@ static bool addr_has_pc_and_ssn(const struct osmo_sccp_addr *addr) } static int resolve_addr_name(struct osmo_sccp_addr *dest, struct osmo_ss7_instance **ss7, - const char *addr_name, const char *label) + const char *addr_name, const char *label, + uint32_t default_pc) { struct osmo_ss7_instance *ss7_tmp; if (!addr_name) { - LOGP(DMAIN, LOGL_ERROR, "Missing config: %s remote-addr\n", label); - return -1; + osmo_sccp_make_addr_pc_ssn(dest, default_pc, OSMO_SCCP_SSN_RANAP); + LOGP(DMAIN, LOGL_INFO, "%s remote addr not configured, using default: %s\n", label, + osmo_sccp_addr_name(*ss7, dest)); + return 0; } ss7_tmp = osmo_sccp_addr_by_name(dest, addr_name); @@ -453,19 +458,25 @@ int hnbgw_cnlink_init(struct hnb_gw *gw, const char *stp_host, uint16_t stp_port struct hnbgw_cnlink *cnlink; struct osmo_ss7_instance *ss7; uint32_t local_pc; - int rc; OSMO_ASSERT(!gw->sccp.client); OSMO_ASSERT(!gw->sccp.cnlink); ss7 = NULL; if (resolve_addr_name(&gw->sccp.iucs_remote_addr, &ss7, - gw->config.iucs_remote_addr_name, "IuCS")) + gw->config.iucs_remote_addr_name, "IuCS", (23 << 3) + 1)) return -1; if (resolve_addr_name(&gw->sccp.iups_remote_addr, &ss7, - gw->config.iups_remote_addr_name, "IuPS")) + gw->config.iups_remote_addr_name, "IuPS", (23 << 3) + 4)) return -1; + if (!ss7) { + LOGP(DRANAP, LOGL_NOTICE, "No cs7 instance configured for IuCS nor IuPS," + " creating default instance\n"); + ss7 = osmo_ss7_instance_find_or_create(gw, 0); + ss7->cfg.primary_pc = (23 << 3) + 5; + } + if (!osmo_ss7_pc_is_valid(ss7->cfg.primary_pc)) { LOGP(DMAIN, LOGL_ERROR, "IuCS/IuPS uplink cannot be setup: CS7 instance %d has no point-code set\n", ss7->cfg.id); @@ -474,8 +485,6 @@ int hnbgw_cnlink_init(struct hnb_gw *gw, const char *stp_host, uint16_t stp_port local_pc = ss7->cfg.primary_pc; osmo_sccp_make_addr_pc_ssn(&gw->sccp.local_addr, local_pc, OSMO_SCCP_SSN_RANAP); - - LOGP(DRANAP, LOGL_NOTICE, "M3UA uplink to STP: %s %u\n", stp_host, stp_port); LOGP(DRANAP, LOGL_NOTICE, "Local SCCP addr: %s\n", osmo_sccp_addr_name(ss7, &gw->sccp.local_addr)); gw->sccp.client = osmo_sccp_simple_client_on_ss7_id(gw, ss7->cfg.id, "OsmoHNBGW", @@ -500,6 +509,11 @@ int hnbgw_cnlink_init(struct hnb_gw *gw, const char *stp_host, uint16_t stp_port return -1; } + LOGP(DRANAP, LOGL_NOTICE, "Remote SCCP addr: IuCS: %s\n", + osmo_sccp_addr_name(ss7, &gw->sccp.iucs_remote_addr)); + LOGP(DRANAP, LOGL_NOTICE, "Remote SCCP addr: IuPS: %s\n", + osmo_sccp_addr_name(ss7, &gw->sccp.iups_remote_addr)); + /* In sccp_sap_up() we expect the cnlink in the user's priv. */ osmo_sccp_user_set_priv(cnlink->sccp_user, cnlink); diff --git a/src/hnbgw_hnbap.c b/src/hnbgw_hnbap.c index 0473482..acc5aff 100644 --- a/src/hnbgw_hnbap.c +++ b/src/hnbgw_hnbap.c @@ -20,6 +20,7 @@ #include <osmocom/core/msgb.h> #include <osmocom/core/utils.h> +#include <osmocom/core/socket.h> #include <osmocom/gsm/gsm48.h> #include <osmocom/netif/stream.h> @@ -48,6 +49,45 @@ static int hnbgw_hnbap_tx(struct hnb_context *ctx, struct msgb *msg) return 0; } +static int hnbgw_tx_hnb_register_rej(struct hnb_context *ctx) +{ + HNBRegisterReject_t reject_out; + HNBRegisterRejectIEs_t reject; + struct msgb *msg; + int rc; + + reject.presenceMask = 0, + reject.cause.present = Cause_PR_radioNetwork; + reject.cause.choice.radioNetwork = CauseRadioNetwork_unspecified; + + /* encode the Information Elements */ + memset(&reject_out, 0, sizeof(reject_out)); + rc = hnbap_encode_hnbregisterrejecties(&reject_out, &reject); + if (rc < 0) { + LOGP(DHNBAP, LOGL_ERROR, "Failure to encode HNB-REGISTER-REJECT to %s: rc=%d\n", + ctx->identity_info, rc); + return rc; + } + + /* generate a successfull outcome PDU */ + msg = hnbap_generate_unsuccessful_outcome(ProcedureCode_id_HNBRegister, + Criticality_reject, + &asn_DEF_HNBRegisterReject, + &reject_out); + + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBRegisterReject, &reject_out); + + rc = hnbgw_hnbap_tx(ctx, msg); + if (rc == 0) { + /* Tell libosmo-netif to destroy this connection when it is done + * sending our HNB-REGISTER-REJECT response. */ + osmo_stream_srv_set_flush_and_destroy(ctx->conn); + } else { + /* The message was not queued. Destroy the connection right away. */ + hnb_context_release(ctx, true); + } +} + static int hnbgw_tx_hnb_register_acc(struct hnb_context *ctx) { HNBRegisterAccept_t accept_out; @@ -63,6 +103,8 @@ static int hnbgw_tx_hnb_register_acc(struct hnb_context *ctx) memset(&accept_out, 0, sizeof(accept_out)); rc = hnbap_encode_hnbregisteraccepties(&accept_out, &accept); if (rc < 0) { + LOGP(DHNBAP, LOGL_ERROR, "Failure to encode HNB-REGISTER-ACCEPT to %s: rc=%d\n", + ctx->identity_info, rc); return rc; } @@ -355,23 +397,27 @@ static int hnbgw_rx_hnb_deregister(struct hnb_context *ctx, ANY_t *in) if (rc < 0) return rc; - DEBUGP(DHNBAP, "HNB-DE-REGISTER cause=%ld\n", - ies.cause); + DEBUGP(DHNBAP, "HNB-DE-REGISTER cause=%s\n", + hnbap_cause_str(&ies.cause)); hnbap_free_hnbde_registeries(&ies); - hnb_context_release(ctx); + hnb_context_release(ctx, true); return 0; } static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in) { + struct hnb_context *hnb; HNBRegisterRequestIEs_t ies; int rc; rc = hnbap_decode_hnbregisterrequesties(&ies, in); - if (rc < 0) + if (rc < 0) { + LOGP(DHNBAP, LOGL_ERROR, "Failure to decode HNB-REGISTER-REQ from %s: rc=%d\n", + ctx->identity_info, rc); return rc; + } /* copy all identity parameters from the message to ctx */ asn1_strncpy(ctx->identity_info, &ies.hnB_Identity.hNB_Identity_Info, @@ -380,8 +426,21 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in) ctx->id.sac = asn1str_to_u16(&ies.sac); ctx->id.rac = asn1str_to_u8(&ies.rac); ctx->id.cid = asn1bitstr_to_u28(&ies.cellIdentity); - //ctx->id.mcc FIXME - //ctx->id.mnc FIXME + gsm48_mcc_mnc_from_bcd(ies.plmNidentity.buf, &ctx->id.mcc, &ctx->id.mnc); + + llist_for_each_entry(hnb, &ctx->gw->hnb_list, list) { + if (hnb->hnb_registered && ctx != hnb && memcmp(&ctx->id, &hnb->id, sizeof(ctx->id)) == 0) { + struct osmo_fd *ofd = osmo_stream_srv_get_ofd(ctx->conn); + char *name = osmo_sock_get_name(ctx, ofd->fd); + LOGP(DHNBAP, LOGL_ERROR, "rejecting HNB-REGISTER-REQ with duplicate cell identity " + "MCC=%u,MNC=%u,LAC=%u,RAC=%u,SAC=%u,CID=%u from %s\n", + ctx->id.mcc, ctx->id.mnc, ctx->id.lac, ctx->id.rac, ctx->id.sac, ctx->id.cid, name); + talloc_free(name); + return hnbgw_tx_hnb_register_rej(ctx); + } + } + + ctx->hnb_registered = true; DEBUGP(DHNBAP, "HNB-REGISTER-REQ from %s\n", ctx->identity_info); @@ -457,7 +516,7 @@ static int hnbgw_rx_ue_deregister(struct hnb_context *ctx, ANY_t *in) ctxid = asn1bitstr_to_u24(&ies.context_ID); - DEBUGP(DHNBAP, "UE-DE-REGISTER context=%ld cause=%s\n", + DEBUGP(DHNBAP, "UE-DE-REGISTER context=%u cause=%s\n", ctxid, hnbap_cause_str(&ies.cause)); ue = ue_context_by_id(ctx->gw, ctxid); @@ -486,7 +545,7 @@ static int hnbgw_rx_err_ind(struct hnb_context *hnb, ANY_t *in) static int hnbgw_rx_initiating_msg(struct hnb_context *hnb, InitiatingMessage_t *imsg) { - int rc; + int rc = 0; switch (imsg->procedureCode) { case ProcedureCode_id_HNBRegister: /* 8.2 */ @@ -517,16 +576,24 @@ static int hnbgw_rx_initiating_msg(struct hnb_context *hnb, InitiatingMessage_t imsg->procedureCode); break; } + + return rc; } static int hnbgw_rx_successful_outcome_msg(struct hnb_context *hnb, SuccessfulOutcome_t *msg) { - + /* We don't care much about HNBAP */ + return 0; } static int hnbgw_rx_unsuccessful_outcome_msg(struct hnb_context *hnb, UnsuccessfulOutcome_t *msg) { - + /* We don't care much about HNBAP */ + LOGP(DHNBAP, LOGL_ERROR, "Received Unsuccessful Outcome, procedureCode %ld, criticality %ld," + " from '%s', cell mcc %u mnc %u lac %u rac %u sac %u cid %u\n", + msg->procedureCode, msg->criticality, hnb->identity_info, + hnb->id.mcc, hnb->id.mnc, hnb->id.lac, hnb->id.rac, hnb->id.sac, hnb->id.cid); + return 0; } @@ -581,5 +648,5 @@ int hnbgw_hnbap_rx(struct hnb_context *hnb, struct msgb *msg) int hnbgw_hnbap_init(void) { - + return 0; } diff --git a/src/hnbgw_ranap.c b/src/hnbgw_ranap.c index 7fd6d0a..6822752 100644 --- a/src/hnbgw_ranap.c +++ b/src/hnbgw_ranap.c @@ -77,7 +77,7 @@ static int ranap_rx_init_reset(struct hnb_context *hnb, ANY_t *in) static int ranap_rx_error_ind(struct hnb_context *hnb, ANY_t *in) { RANAP_ErrorIndicationIEs_t ies; - int rc, is_ps = 0; + int rc; rc = ranap_decode_errorindicationies(&ies, in); if (rc < 0) @@ -92,35 +92,9 @@ static int ranap_rx_error_ind(struct hnb_context *hnb, ANY_t *in) return 0; } -static int ranap_rx_dt(struct hnb_context *hnb, ANY_t *in) -{ - RANAP_DirectTransferIEs_t ies; - int sapi = 0; - int rc; - - rc = ranap_decode_directtransferies(&ies, in); - if (rc < 0) - return rc; - - if (ies.presenceMask & DIRECTTRANSFERIES_RANAP_SAPI_PRESENT) - sapi = ies.sapi; - - if (ies.presenceMask & DIRECTTRANSFERIES_RANAP_LAI_PRESENT) { - /* FIXME: Update LAI associated with UE */ - } - - if (ies.presenceMask & DIRECTTRANSFERIES_RANAP_RAC_PRESENT) { - /* FIXME: Update RAC associated with UE */ - } - - DEBUGP(DRANAP, "DirectTransfer: %s\n", - osmo_hexdump(ies.nas_pdu.buf, ies.nas_pdu.size)); - /* FIXME: hand NAS PDU into MSC */ -} - static int ranap_rx_initiating_msg(struct hnb_context *hnb, RANAP_InitiatingMessage_t *imsg) { - int rc; + int rc = 0; /* according tot the spec, we can primarily receive Overload, * Reset, Reset ACK, Error Indication, reset Resource, Reset @@ -144,19 +118,19 @@ static int ranap_rx_initiating_msg(struct hnb_context *hnb, RANAP_InitiatingMess case RANAP_ProcedureCode_id_DirectInformationTransfer: case RANAP_ProcedureCode_id_UplinkInformationExchange: LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP " - "Procedure %u from HNB, ignoring\n", imsg->procedureCode); + "Procedure %lu from HNB, ignoring\n", imsg->procedureCode); break; default: LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP " - "Procedure %u from HNB, ignoring\n", imsg->procedureCode); + "Procedure %lu from HNB, ignoring\n", imsg->procedureCode); break; } + + return rc; } static int ranap_rx_successful_msg(struct hnb_context *hnb, RANAP_SuccessfulOutcome_t *imsg) { - int rc; - /* according tot the spec, we can primarily receive Overload, * Reset, Reset ACK, Error Indication, reset Resource, Reset * Resurce Acknowledge as connecitonless RANAP. There are some @@ -172,20 +146,22 @@ static int ranap_rx_successful_msg(struct hnb_context *hnb, RANAP_SuccessfulOutc case RANAP_ProcedureCode_id_DirectInformationTransfer: case RANAP_ProcedureCode_id_UplinkInformationExchange: LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP " - "Procedure %u from HNB, ignoring\n", imsg->procedureCode); + "Procedure %lu from HNB, ignoring\n", imsg->procedureCode); break; default: LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP " - "Procedure %u from HNB, ignoring\n", imsg->procedureCode); + "Procedure %lu from HNB, ignoring\n", imsg->procedureCode); break; } + + return 0; } static int _hnbgw_ranap_rx(struct hnb_context *hnb, RANAP_RANAP_PDU_t *pdu) { - int rc; + int rc = 0; switch (pdu->present) { case RANAP_RANAP_PDU_PR_initiatingMessage: @@ -196,7 +172,7 @@ static int _hnbgw_ranap_rx(struct hnb_context *hnb, RANAP_RANAP_PDU_t *pdu) break; case RANAP_RANAP_PDU_PR_unsuccessfulOutcome: LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP " - "unsuccessful outcome procedure %u from HNB, ignoring\n", + "unsuccessful outcome procedure %lu from HNB, ignoring\n", pdu->choice.unsuccessfulOutcome.procedureCode); break; default: @@ -204,6 +180,8 @@ static int _hnbgw_ranap_rx(struct hnb_context *hnb, RANAP_RANAP_PDU_t *pdu) "presence %u from HNB, ignoring\n", pdu->present); break; } + + return rc; } @@ -228,5 +206,5 @@ int hnbgw_ranap_rx(struct msgb *msg, uint8_t *data, size_t len) int hnbgw_ranap_init(void) { - + return 0; } diff --git a/src/hnbgw_rua.c b/src/hnbgw_rua.c index 95979f5..40d1d94 100644 --- a/src/hnbgw_rua.c +++ b/src/hnbgw_rua.c @@ -24,6 +24,7 @@ #include <osmocom/netif/stream.h> #include <osmocom/sigtran/sccp_sap.h> +#include <osmocom/sigtran/sccp_helpers.h> #include <unistd.h> #include <errno.h> @@ -180,10 +181,11 @@ static int rua_to_scu(struct hnb_context *hnb, { struct msgb *msg; struct osmo_scu_prim *prim; - struct hnbgw_context_map *map; + struct hnbgw_context_map *map = NULL; struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink; struct osmo_sccp_addr *remote_addr; bool is_ps; + bool release_context_map = false; int rc; switch (cN_DomainIndicator) { @@ -196,7 +198,7 @@ static int rua_to_scu(struct hnb_context *hnb, is_ps = true; break; default: - LOGP(DRUA, LOGL_ERROR, "Unsupported Domain %u\n", + LOGP(DRUA, LOGL_ERROR, "Unsupported Domain %ld\n", cN_DomainIndicator); return -1; } @@ -211,13 +213,21 @@ static int rua_to_scu(struct hnb_context *hnb, prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim)); osmo_prim_init(&prim->oph, SCCP_SAP_USER, type, PRIM_OP_REQUEST, msg); - map = context_map_alloc_by_hnb(hnb, context_id, is_ps, cn); - OSMO_ASSERT(map); - - DEBUGP(DRUA, "rua_to_scu() %s to %s, rua_ctx_id %u scu_conn_id %u\n", - cn_domain_indicator_to_str(cN_DomainIndicator), - osmo_sccp_addr_dump(remote_addr), - map->rua_ctx_id, map->scu_conn_id); + switch (type) { + case OSMO_SCU_PRIM_N_UNITDATA: + DEBUGP(DRUA, "rua_to_scu() %s to %s, rua_ctx_id %u (unitdata, no scu_conn_id)\n", + cn_domain_indicator_to_str(cN_DomainIndicator), + osmo_sccp_addr_dump(remote_addr), + context_id); + break; + default: + map = context_map_alloc_by_hnb(hnb, context_id, is_ps, cn); + OSMO_ASSERT(map); + DEBUGP(DRUA, "rua_to_scu() %s to %s, rua_ctx_id %u scu_conn_id %u\n", + cn_domain_indicator_to_str(cN_DomainIndicator), + osmo_sccp_addr_dump(remote_addr), + map->rua_ctx_id, map->scu_conn_id); + } /* add primitive header */ switch (type) { @@ -238,6 +248,7 @@ static int rua_to_scu(struct hnb_context *hnb, case OSMO_SCU_PRIM_N_DISCONNECT: prim->u.disconnect.conn_id = map->scu_conn_id; prim->u.disconnect.cause = cause; + release_context_map = true; break; case OSMO_SCU_PRIM_N_UNITDATA: prim->u.unitdata.called_addr = *remote_addr; @@ -260,6 +271,9 @@ static int rua_to_scu(struct hnb_context *hnb, rc = osmo_sccp_user_sap_down(cn->sccp_user, &prim->oph); + if (map && release_context_map) + context_map_deactivate(map); + return rc; } @@ -347,7 +361,7 @@ static int rua_rx_init_connect(struct msgb *msg, ANY_t *in) rc = rua_to_scu(hnb, ies.cN_DomainIndicator, OSMO_SCU_PRIM_N_CONNECT, context_id, 0, ies.ranaP_Message.buf, ies.ranaP_Message.size); - /* FIXME: what to do with the asn1c-allocated memory */ + rua_free_connecties(&ies); return rc; @@ -382,7 +396,6 @@ static int rua_rx_init_disconnect(struct msgb *msg, ANY_t *in) OSMO_SCU_PRIM_N_DISCONNECT, context_id, scu_cause, ranap_data, ranap_len); - /* FIXME: what to do with the asn1c-allocated memory */ rua_free_disconnecties(&ies); return rc; @@ -409,7 +422,6 @@ static int rua_rx_init_dt(struct msgb *msg, ANY_t *in) context_id, 0, ies.ranaP_Message.buf, ies.ranaP_Message.size); - /* FIXME: what to do with the asn1c-allocated memory */ rua_free_directtransferies(&ies); return rc; @@ -418,7 +430,6 @@ static int rua_rx_init_dt(struct msgb *msg, ANY_t *in) static int rua_rx_init_udt(struct msgb *msg, ANY_t *in) { RUA_ConnectionlessTransferIEs_t ies; - RUA_CN_DomainIndicator_t domain; int rc; rc = rua_decode_connectionlesstransferies(&ies, in); @@ -483,7 +494,7 @@ static int rua_rx_initiating_msg(struct msgb *msg, RUA_InitiatingMessage_t *imsg rc = 0; break; default: - LOGP(DRUA, LOGL_NOTICE, "Unknown RUA Procedure %u\n", + LOGP(DRUA, LOGL_NOTICE, "Unknown RUA Procedure %lu\n", imsg->procedureCode); rc = -1; } diff --git a/src/hnbgw_vty.c b/src/hnbgw_vty.c index ddea578..859cd31 100644 --- a/src/hnbgw_vty.c +++ b/src/hnbgw_vty.c @@ -18,6 +18,9 @@ * */ +#include <string.h> + +#include <osmocom/core/socket.h> #include <osmocom/vty/command.h> #include <osmocom/iuh/vty.h> @@ -25,6 +28,8 @@ #include <osmocom/iuh/hnbgw.h> #include <osmocom/iuh/context_map.h> #include <osmocom/sigtran/protocol/sua.h> +#include <osmocom/sigtran/sccp_helpers.h> +#include <osmocom/netif/stream.h> static void *tall_hnb_ctx = NULL; static struct hnb_gw *g_hnb_gw = NULL; @@ -106,20 +111,88 @@ int hnbgw_vty_go_parent(struct vty *vty) return vty->node; } +DEFUN(show_cnlink, show_cnlink_cmd, "show cnlink", + SHOW_STR "Display information on core network link\n") +{ + struct osmo_ss7_route *rt; + struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(g_hnb_gw->sccp.client); + int i; +#define GUARD(STR) \ + STR ? STR : "", \ + STR ? ":" : "" + + vty_out(vty, "IuCS: %s <->", + osmo_sccp_user_name(g_hnb_gw->sccp.cnlink->sccp_user)); + vty_out(vty, " %s%s%s%s", + GUARD(g_hnb_gw->config.iucs_remote_addr_name), + osmo_sccp_inst_addr_name(g_hnb_gw->sccp.client, &g_hnb_gw->sccp.iucs_remote_addr), + VTY_NEWLINE); + + rt = osmo_ss7_route_lookup(ss7, g_hnb_gw->sccp.iucs_remote_addr.pc); + vty_out(vty, " SS7 route: %s%s", osmo_ss7_route_name(rt, true), VTY_NEWLINE); + + vty_out(vty, "IuPS: %s <->", + osmo_sccp_user_name(g_hnb_gw->sccp.cnlink->sccp_user)); + vty_out(vty, " %s%s%s%s", + GUARD(g_hnb_gw->config.iups_remote_addr_name), + osmo_sccp_inst_addr_name(g_hnb_gw->sccp.client, &g_hnb_gw->sccp.iups_remote_addr), + VTY_NEWLINE); + + rt = osmo_ss7_route_lookup(ss7, g_hnb_gw->sccp.iups_remote_addr.pc); + vty_out(vty, " SS7 route: %s%s", osmo_ss7_route_name(rt, true), VTY_NEWLINE); + +#undef GUARD + return CMD_SUCCESS; +} + +static void vty_out_ofd_addr(struct vty *vty, struct osmo_fd *ofd) +{ + char *name; + if (!ofd || ofd->fd < 0 + || !(name = osmo_sock_get_name(vty, ofd->fd))) { + vty_out(vty, "(no addr)"); + return; + } + vty_out(vty, "%s", name); + talloc_free(name); +} + +static void vty_dump_hnb_info__map_states(struct vty *vty, const char *name, unsigned int count, + unsigned int state_count[]) +{ + unsigned int i; + if (!count) + return; + vty_out(vty, " %s: %u contexts:", name, count); + for (i = 0; i <= MAP_S_NUM_STATES; i++) { + if (!state_count[i]) + continue; + vty_out(vty, " %s:%u", hnbgw_context_map_state_name(i), state_count[i]); + } + vty_out(vty, VTY_NEWLINE); +} + static void vty_dump_hnb_info(struct vty *vty, struct hnb_context *hnb) { struct hnbgw_context_map *map; + unsigned int map_count[2] = {}; + unsigned int state_count[2][MAP_S_NUM_STATES + 1] = {}; - vty_out(vty, "HNB \"%s\" MCC %u MNC %u LAC %u RAC %u SAC %u CID %u%s", hnb->identity_info, - hnb->id.mcc, hnb->id.mnc, hnb->id.lac, hnb->id.rac, hnb->id.sac, hnb->id.cid, - VTY_NEWLINE); - vty_out(vty, " HNBAP ID %u RUA ID %u%s", hnb->hnbap_stream, hnb->rua_stream, VTY_NEWLINE); + vty_out(vty, "HNB "); + vty_out_ofd_addr(vty, hnb->conn? osmo_stream_srv_get_ofd(hnb->conn) : NULL); + vty_out(vty, " \"%s\"%s", hnb->identity_info, VTY_NEWLINE); + vty_out(vty, " MCC %u MNC %u LAC %u RAC %u SAC %u CID %u SCTP-stream:HNBAP=%u,RUA=%u%s", + hnb->id.mcc, hnb->id.mnc, hnb->id.lac, hnb->id.rac, hnb->id.sac, hnb->id.cid, + hnb->hnbap_stream, hnb->rua_stream, VTY_NEWLINE); llist_for_each_entry(map, &hnb->map_list, hnb_list) { - vty_out(vty, " Map %u->%u (RUA->SUA) cnlink=%p state=%u%s", map->rua_ctx_id, map->scu_conn_id, - map->cn_link, map->state, VTY_NEWLINE); - + map_count[map->is_ps? 1 : 0]++; + state_count[map->is_ps? 1 : 0] + [(map->state >= 0 && map->state < MAP_S_NUM_STATES)? + map->state : MAP_S_NUM_STATES]++; } + vty_dump_hnb_info__map_states(vty, "IuCS", map_count[0], state_count[0]); + vty_dump_hnb_info__map_states(vty, "IuPS", map_count[1], state_count[1]); } static void vty_dump_ue_info(struct vty *vty, struct ue_context *ue) @@ -130,11 +203,20 @@ static void vty_dump_ue_info(struct vty *vty, struct ue_context *ue) DEFUN(show_hnb, show_hnb_cmd, "show hnb all", SHOW_STR "Display information about a HNB") { struct hnb_context *hnb; + unsigned int count = 0; + + if (llist_empty(&g_hnb_gw->hnb_list)) { + vty_out(vty, "No HNB connected%s", VTY_NEWLINE); + return CMD_SUCCESS; + } llist_for_each_entry(hnb, &g_hnb_gw->hnb_list, list) { vty_dump_hnb_info(vty, hnb); + count++; } + vty_out(vty, "%u HNB connected%s", count, VTY_NEWLINE); + return CMD_SUCCESS; } @@ -157,6 +239,17 @@ DEFUN(show_talloc, show_talloc_cmd, "show talloc", SHOW_STR "Display talloc info return CMD_SUCCESS; } +DEFUN(cfg_hnbgw_rnc_id, cfg_hnbgw_rnc_id_cmd, + "rnc-id <0-65535>", + "Configure the HNBGW's RNC Id, the common RNC Id used for all connected hNodeB. It is sent to" + " each hNodeB upon HNBAP HNB-Register-Accept, and the hNodeB will subsequently send this as" + " RANAP InitialUE Messages' GlobalRNC-ID IE. Takes effect as soon as the hNodeB re-registers.\n" + "RNC Id value\n") +{ + g_hnb_gw->config.rnc_id = atoi(argv[0]); + return CMD_SUCCESS; +} + DEFUN(cfg_hnbgw_iuh_local_ip, cfg_hnbgw_iuh_local_ip_cmd, "local-ip A.B.C.D", "Accept Iuh connections on local interface\n" "Local interface IP address (default: " HNBGW_LOCAL_IP_DEFAULT ")") @@ -233,9 +326,6 @@ static int config_write_hnbgw_iuh(struct vty *vty) static int config_write_hnbgw_iucs(struct vty *vty) { - const char *addr; - uint16_t port; - if (!g_hnb_gw->config.iucs_remote_addr_name) return CMD_SUCCESS; @@ -248,9 +338,6 @@ static int config_write_hnbgw_iucs(struct vty *vty) static int config_write_hnbgw_iups(struct vty *vty) { - const char *addr; - uint16_t port; - if (!g_hnb_gw->config.iups_remote_addr_name) return CMD_SUCCESS; @@ -268,11 +355,11 @@ void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx) install_element(CONFIG_NODE, &cfg_hnbgw_cmd); install_node(&hnbgw_node, config_write_hnbgw); - vty_install_default(HNBGW_NODE); + + install_element(HNBGW_NODE, &cfg_hnbgw_rnc_id_cmd); install_element(HNBGW_NODE, &cfg_hnbgw_iuh_cmd); install_node(&iuh_node, config_write_hnbgw_iuh); - vty_install_default(IUH_NODE); install_element(IUH_NODE, &cfg_hnbgw_iuh_local_ip_cmd); install_element(IUH_NODE, &cfg_hnbgw_iuh_local_port_cmd); @@ -280,16 +367,15 @@ void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx) install_element(HNBGW_NODE, &cfg_hnbgw_iucs_cmd); install_node(&iucs_node, config_write_hnbgw_iucs); - vty_install_default(IUCS_NODE); install_element(IUCS_NODE, &cfg_hnbgw_iucs_remote_addr_cmd); install_element(HNBGW_NODE, &cfg_hnbgw_iups_cmd); install_node(&iups_node, config_write_hnbgw_iups); - vty_install_default(IUPS_NODE); install_element(IUPS_NODE, &cfg_hnbgw_iups_remote_addr_cmd); + install_element_ve(&show_cnlink_cmd); install_element_ve(&show_hnb_cmd); install_element_ve(&show_ue_cmd); install_element_ve(&show_talloc_cmd); diff --git a/src/iu_client.c b/src/iu_client.c index 17d955d..4aecfec 100644 --- a/src/iu_client.c +++ b/src/iu_client.c @@ -25,12 +25,16 @@ #include <string.h> #include <stdbool.h> +#include <asn1c/asn1helpers.h> + #include <osmocom/ranap/iu_client.h> #include <osmocom/core/logging.h> #include <osmocom/crypt/auth.h> #include <osmocom/gprs/gprs_msgb.h> #include <osmocom/sigtran/sccp_sap.h> +#include <osmocom/sigtran/sccp_helpers.h> +#include <osmocom/ranap/ranap_common_cn.h> #include <osmocom/ranap/ranap_ies_defs.h> #include <osmocom/ranap/ranap_msg_factory.h> @@ -38,11 +42,19 @@ * PLMN identity is a BCD representation of the MCC and MNC. * See iu_grnc_id_parse(). */ struct iu_grnc_id { - uint16_t mcc; - uint16_t mnc; + struct osmo_plmn_id plmn; uint16_t rnc_id; }; +struct iu_lac_rac_entry { + struct llist_head entry; + + /* LAC: Location Area Code (for CS and PS) */ + uint16_t lac; + /* RAC: Routing Area Code (for PS only) */ + uint8_t rac; +}; + /* A remote RNC (Radio Network Controller, like BSC but for UMTS) that has * called us and is currently reachable at the given osmo_sccp_addr. So, when we * know a LAC for a subscriber, we can page it at the RNC matching that LAC or @@ -54,9 +66,10 @@ struct ranap_iu_rnc { struct llist_head entry; uint16_t rnc_id; - uint16_t lac; /* Location Area Code (used for CS and PS) */ - uint8_t rac; /* Routing Area Code (used for PS only) */ struct osmo_sccp_addr sccp_addr; + + /* A list of struct iu_lac_rac_entry */ + struct llist_head lac_rac_list; }; void *talloc_iu_ctx; @@ -78,6 +91,9 @@ int iu_log_subsystem = 0; #define LOGPIU(level, fmt, args...) \ LOGP(iu_log_subsystem, level, fmt, ## args) +#define LOGPIUC(level, fmt, args...) \ + LOGPC(iu_log_subsystem, level, fmt, ## args) + static LLIST_HEAD(ue_conn_ctx_list); static LLIST_HEAD(rnc_list); @@ -115,53 +131,109 @@ static struct ranap_ue_conn_ctx *ue_conn_ctx_find(uint32_t conn_id) return NULL; } -static struct ranap_iu_rnc *iu_rnc_alloc(uint16_t rnc_id, uint16_t lac, uint8_t rac, - struct osmo_sccp_addr *addr) +static struct ranap_iu_rnc *iu_rnc_alloc(uint16_t rnc_id, struct osmo_sccp_addr *addr) { struct ranap_iu_rnc *rnc = talloc_zero(talloc_iu_ctx, struct ranap_iu_rnc); + OSMO_ASSERT(rnc); + + INIT_LLIST_HEAD(&rnc->lac_rac_list); rnc->rnc_id = rnc_id; - rnc->lac = lac; - rnc->rac = rac; rnc->sccp_addr = *addr; llist_add(&rnc->entry, &rnc_list); - LOGPIU(LOGL_NOTICE, "New RNC %d (LAC=%d RAC=%d)\n", - rnc->rnc_id, rnc->lac, rnc->rac); + LOGPIU(LOGL_NOTICE, "New RNC %d at %s\n", rnc->rnc_id, osmo_sccp_addr_dump(addr)); return rnc; } +/* Find a match for the given LAC (and RAC). For CS, pass rac as 0. + * If rnc and lre pointers are not NULL, *rnc / *lre are set to NULL if no match is found, or to the + * match if a match is found. Return true if a match is found. */ +static bool iu_rnc_lac_rac_find(struct ranap_iu_rnc **rnc, struct iu_lac_rac_entry **lre, + uint16_t lac, uint8_t rac) +{ + struct ranap_iu_rnc *r; + struct iu_lac_rac_entry *e; + + if (rnc) + *rnc = NULL; + if (lre) + *lre = NULL; + + llist_for_each_entry(r, &rnc_list, entry) { + llist_for_each_entry(e, &r->lac_rac_list, entry) { + if (e->lac == lac && e->rac == rac) { + if (rnc) + *rnc = r; + if (lre) + *lre = e; + return true; + } + } + } + return false; +} + +static struct ranap_iu_rnc *iu_rnc_id_find(uint16_t rnc_id) +{ + struct ranap_iu_rnc *rnc; + llist_for_each_entry(rnc, &rnc_list, entry) { + if (rnc->rnc_id == rnc_id) + return rnc; + } + return NULL; +} + +static bool same_sccp_addr(struct osmo_sccp_addr *a, struct osmo_sccp_addr *b) +{ + char buf[256]; + osmo_strlcpy(buf, osmo_sccp_addr_dump(a), sizeof(buf)); + return !strcmp(buf, osmo_sccp_addr_dump(b)); +} + static struct ranap_iu_rnc *iu_rnc_register(uint16_t rnc_id, uint16_t lac, uint8_t rac, struct osmo_sccp_addr *addr) { struct ranap_iu_rnc *rnc; - llist_for_each_entry(rnc, &rnc_list, entry) { - if (rnc->rnc_id != rnc_id) - continue; - - /* We have this RNC Id registered already. Make sure that the - * details match. */ - - /* TODO should a mismatch be an error? */ - if (rnc->lac != lac || rnc->rac != rac) - LOGPIU(LOGL_NOTICE, "RNC %d changes its details:" - " LAC=%d RAC=%d --> LAC=%d RAC=%d\n", - rnc->rnc_id, rnc->lac, rnc->rac, - lac, rac); - rnc->lac = lac; - rnc->rac = rac; - - if (addr && memcmp(&rnc->sccp_addr, addr, sizeof(*addr))) - LOGPIU(LOGL_NOTICE, "RNC %d on New SCCP Addr %s" - " (LAC=%d RAC=%d)\n", - rnc->rnc_id, osmo_sccp_addr_dump(addr), rnc->lac, rnc->rac); - rnc->sccp_addr = *addr; - return rnc; + struct ranap_iu_rnc *old_rnc; + struct iu_lac_rac_entry *lre; + + /* Make sure we know this rnc_id and that this SCCP address is in our records */ + rnc = iu_rnc_id_find(rnc_id); + + if (rnc) { + if (!same_sccp_addr(&rnc->sccp_addr, addr)) { + LOGPIU(LOGL_NOTICE, "RNC %u changed its SCCP addr to %s (LAC=%u RAC=%u)\n", + rnc_id, osmo_sccp_addr_dump(addr), lac, rac); + rnc->sccp_addr = *addr; + } + } else + rnc = iu_rnc_alloc(rnc_id, addr); + + /* Detect whether the LAC,RAC is already recorded in another RNC */ + iu_rnc_lac_rac_find(&old_rnc, &lre, lac, rac); + + if (old_rnc && old_rnc != rnc) { + /* LAC,RAC already exists in a different RNC */ + LOGPIU(LOGL_NOTICE, "LAC %u RAC %u moved from RNC %u %s", + lac, rac, old_rnc->rnc_id, osmo_sccp_addr_dump(&old_rnc->sccp_addr)); + LOGPIUC(LOGL_NOTICE, " to RNC %u %s\n", + rnc->rnc_id, osmo_sccp_addr_dump(&rnc->sccp_addr)); + + llist_del(&lre->entry); + llist_add(&lre->entry, &rnc->lac_rac_list); + } else if (!old_rnc) { + /* LAC,RAC not recorded yet */ + LOGPIU(LOGL_NOTICE, "RNC %u: new LAC %u RAC %u\n", rnc_id, lac, rac); + lre = talloc_zero(rnc, struct iu_lac_rac_entry); + lre->lac = lac; + lre->rac = rac; + llist_add(&lre->entry, &rnc->lac_rac_list); } + /* else, LAC,RAC already recorded with the current RNC. */ - /* Not found, make a new one. */ - return iu_rnc_alloc(rnc_id, lac, rac, addr); + return rnc; } /*********************************************************************** @@ -237,8 +309,7 @@ static int iu_grnc_id_parse(struct iu_grnc_id *dst, struct RANAP_GlobalRNC_ID *s " should be 3, is %d\n", src->pLMNidentity.size); return -1; } - gsm48_mcc_mnc_from_bcd(&src->pLMNidentity.buf[0], - &dst->mcc, &dst->mnc); + osmo_plmn_from_bcd(&src->pLMNidentity.buf[0], &dst->plmn); dst->rnc_id = (uint16_t)src->rNC_ID; return 0; } @@ -598,69 +669,44 @@ static int iu_tx_paging_cmd(struct osmo_sccp_addr *called_addr, struct msgb *msg; msg = ranap_new_msg_paging_cmd(imsi, tmsi, is_ps? 1 : 0, paging_cause); msg->l2h = msg->data; - osmo_sccp_tx_unitdata_msg(g_scu, &g_local_sccp_addr, called_addr, msg); - return 0; + return osmo_sccp_tx_unitdata_msg(g_scu, &g_local_sccp_addr, called_addr, msg); } -static int iu_page(const char *imsi, const uint32_t *tmsi_or_ptimsi, +static int iu_page(const char *imsi, const uint32_t *tmsi_or_ptmsi, uint16_t lac, uint8_t rac, bool is_ps) { struct ranap_iu_rnc *rnc; - int pagings_sent = 0; - - if (tmsi_or_ptimsi) { - LOGPIU(LOGL_DEBUG, "%s: Looking for RNCs to page for IMSI %s" - " (paging will use %s %x)\n", - is_ps? "IuPS" : "IuCS", - imsi, - is_ps? "PTMSI" : "TMSI", - *tmsi_or_ptimsi); - } else { - LOGPIU(LOGL_DEBUG, "%s: Looking for RNCs to page for IMSI %s" - " (paging will use IMSI)\n", - is_ps? "IuPS" : "IuCS", - imsi - ); - } - - llist_for_each_entry(rnc, &rnc_list, entry) { - if (rnc->lac != lac) - continue; - if (is_ps && rnc->rac != rac) - continue; - - /* Found a match! */ - if (iu_tx_paging_cmd(&rnc->sccp_addr, imsi, tmsi_or_ptimsi, is_ps, 0) - == 0) { - LOGPIU(LOGL_DEBUG, - "%s: Paged for IMSI %s on RNC %d, on SCCP addr %s\n", - is_ps? "IuPS" : "IuCS", - imsi, rnc->rnc_id, osmo_sccp_addr_dump(&rnc->sccp_addr)); - pagings_sent ++; + const char *log_msg; + int log_level; + int paged = 0; + + iu_rnc_lac_rac_find(&rnc, NULL, lac, rac); + if (rnc) { + if (iu_tx_paging_cmd(&rnc->sccp_addr, imsi, tmsi_or_ptmsi, is_ps, 0) == 0) { + log_msg = "Paging"; + log_level = LOGL_DEBUG; + paged = 1; + } else { + log_msg = "Paging failed"; + log_level = LOGL_ERROR; } + } else { + log_msg = "Found no RNC to Page"; + log_level = LOGL_ERROR; } - /* Some logging... */ - if (pagings_sent > 0) { - LOGPIU(LOGL_DEBUG, - "%s: %d RNCs were paged for IMSI %s.\n", - is_ps? "IuPS" : "IuCS", - pagings_sent, imsi); - } - else { - if (is_ps) { - LOGPIU(LOGL_ERROR, "IuPS: Found no RNC to page for" - " LAC %d RAC %d (would have paged IMSI %s)\n", - lac, rac, imsi); - } - else { - LOGPIU(LOGL_ERROR, "IuCS: Found no RNC to page for" - " LAC %d (would have paged IMSI %s)\n", - lac, imsi); - } - } + if (is_ps) + LOGPIU(log_level, "IuPS: %s on LAC %d RAC %d", log_msg, lac, rac); + else + LOGPIU(log_level, "IuCS: %s on LAC %d", log_msg, lac); + if (rnc) + LOGPIUC(log_level, " at SCCP-addr %s", osmo_sccp_addr_dump(&rnc->sccp_addr)); + if (tmsi_or_ptmsi) + LOGPIUC(log_level, ", for %s %08x\n", is_ps? "PTMSI" : "TMSI", *tmsi_or_ptmsi); + else + LOGPIUC(log_level, ", for IMSI %s\n", imsi); - return pagings_sent; + return paged; } int ranap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac) @@ -775,4 +821,3 @@ int ranap_iu_init(void *ctx, int log_subsystem, const char *sccp_user_name, stru return 0; } - diff --git a/src/iu_helpers.c b/src/iu_helpers.c index 2f44e93..5e78293 100644 --- a/src/iu_helpers.c +++ b/src/iu_helpers.c @@ -20,8 +20,12 @@ #include <stdint.h> #include <string.h> - +#include <errno.h> +#include <arpa/inet.h> +#include "asn1helpers.h" #include <osmocom/core/utils.h> +#include <osmocom/ranap/RANAP_IuTransportAssociation.h> +#include <osmocom/ranap/RANAP_TransportLayerAddress.h> /* decode a BCD-string as used inside ASN.1 encoded Iu interface protocols */ int ranap_bcd_decode(char *out, size_t out_len, const uint8_t *in, size_t in_len) @@ -70,3 +74,50 @@ int ranap_imsi_encode(uint8_t *out, size_t out_len, const char *in) } return i; } + +/* decode a network port as used inside ASN.1 encoded Iu interface protocols */ +int ranap_transp_assoc_decode(uint16_t *port, const RANAP_IuTransportAssociation_t *transp_assoc) +{ + uint32_t result; + + if (!transp_assoc) + return -EINVAL; + + result = asn1bitstr_to_u32((BIT_STRING_t *) & transp_assoc->choice.bindingID); + + /* The lower 16 bits should be zero, otherwise the decoding may + * have yielded some odd result */ + if (result & 0xFFFF) + return -EINVAL; + + *port = (uint16_t) ((result >> 16) & 0xFFFF); + + if (*port == 0) + return -EINVAL; + + return 0; +} + +/* decode a network address as used inside ASN.1 encoded Iu interface protocols */ +int ranap_transp_layer_addr_decode(char *addr, unsigned int addr_len, + const RANAP_TransportLayerAddress_t *trasp_layer_addr) +{ + unsigned char *buf; + int len; + const char *rc; + + buf = trasp_layer_addr->buf; + len = trasp_layer_addr->size; + + if (buf[0] == 0x35 && len > 7) + rc = inet_ntop(AF_INET, buf + 3, addr, addr_len); + else if (len > 3) + rc = inet_ntop(AF_INET, buf, addr, addr_len); + else + return -EINVAL; + + if (!rc) + return -EINVAL; + + return 0; +} diff --git a/src/ranap_common.c b/src/ranap_common.c index 13c913f..46203e0 100644 --- a/src/ranap_common.c +++ b/src/ranap_common.c @@ -24,6 +24,7 @@ #include <osmocom/gsm/gsm48.h> #include <osmocom/ranap/ranap_common.h> +#include <asn1c/asn1helpers.h> extern int asn1_xer_print; int _ranap_DRANAP = 0; @@ -278,7 +279,7 @@ char *ranap_cause_str(const RANAP_Cause_t *cause) cause->choice.misc)); break; case RANAP_Cause_PR_non_Standard: - snprintf(buf, sizeof(buf), "non-standard(%u)", + snprintf(buf, sizeof(buf), "non-standard(%ld)", cause->choice.non_Standard); break; default: @@ -542,7 +543,6 @@ void ranap_set_log_area(int log_area) int ranap_ip_from_transp_layer_addr(const BIT_STRING_t *in, uint32_t *ip) { - uint32_t addr; uint8_t x213[] = {0x35, 0x00, 0x01}; /* Only support IPv4 for now - plain and with x213 encapsulation */ diff --git a/src/ranap_common_cn.c b/src/ranap_common_cn.c index d02eb37..d2c875e 100644 --- a/src/ranap_common_cn.c +++ b/src/ranap_common_cn.c @@ -319,7 +319,7 @@ int ranap_cn_rx_co(ranap_handle_cb cb, void *ctx, uint8_t *data, size_t len) static int cn_ranap_rx_initiating_msg_cl(void *ctx, RANAP_InitiatingMessage_t *imsg, ranap_message *message) { - int rc; + int rc = 0; message->procedureCode = imsg->procedureCode; message->criticality = imsg->criticality; @@ -360,6 +360,8 @@ static int cn_ranap_rx_initiating_msg_cl(void *ctx, RANAP_InitiatingMessage_t *i get_value_string(ranap_procedure_code_vals, imsg->procedureCode)); break; } + + return rc; } static void cn_ranap_free_initiating_msg_cl(ranap_message *message) @@ -403,7 +405,7 @@ static void cn_ranap_free_initiating_msg_cl(ranap_message *message) static int cn_ranap_rx_successful_msg_cl(void *ctx, RANAP_SuccessfulOutcome_t *imsg, ranap_message *message) { - int rc; + int rc = 0; message->procedureCode = imsg->procedureCode; message->criticality = imsg->criticality; @@ -433,6 +435,8 @@ static int cn_ranap_rx_successful_msg_cl(void *ctx, RANAP_SuccessfulOutcome_t *i get_value_string(ranap_procedure_code_vals, imsg->procedureCode)); break; } + + return rc; } static void cn_ranap_free_successful_msg_cl(ranap_message *message) @@ -463,7 +467,7 @@ static void cn_ranap_free_successful_msg_cl(ranap_message *message) static int _cn_ranap_rx_cl(void *ctx, RANAP_RANAP_PDU_t *pdu, ranap_message *message) { - int rc; + int rc = 0; /* Extend _cn_ranap_free_cl as well when extending this function */ @@ -488,6 +492,8 @@ static int _cn_ranap_rx_cl(void *ctx, RANAP_RANAP_PDU_t *pdu, ranap_message *mes get_value_string(ranap_presence_vals, pdu->present)); break; } + + return rc; } static void _cn_ranap_free_cl(ranap_message *message) @@ -505,7 +511,7 @@ static void _cn_ranap_free_cl(ranap_message *message) break; default: LOGP(DRANAP, LOGL_NOTICE, "Suspicious RANAP " - "presence %s (CL) from RNC, ignoring\n", message->direction); + "presence %d (CL) from RNC, ignoring\n", message->direction); break; } } diff --git a/src/ranap_msg_factory.c b/src/ranap_msg_factory.c index 64d87f9..c287f02 100644 --- a/src/ranap_msg_factory.c +++ b/src/ranap_msg_factory.c @@ -185,6 +185,10 @@ struct msgb *ranap_new_msg_dt(uint8_t sapi, const uint8_t *nas, unsigned int nas /* ies -> dt */ rc = ranap_encode_directtransferies(&dt, &ies); + if (rc < 0) { + LOGP(DRANAP, LOGL_ERROR, "error encoding direct transfer IEs: %d\n", rc); + return NULL; + } /* dt -> msg */ msg = ranap_generate_initiating_message(RANAP_ProcedureCode_id_DirectTransfer, @@ -251,6 +255,11 @@ struct msgb *ranap_new_msg_sec_mod_cmd(const uint8_t *ik, const uint8_t *ck, enu if (ck) ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_EncryptionInformation, &ies.encryptionInformation); + if (rc < 0) { + LOGP(DRANAP, LOGL_ERROR, "error encoding security mode command IEs: %d\n", rc); + return NULL; + } + /* out -> msg */ msg = ranap_generate_initiating_message(RANAP_ProcedureCode_id_SecurityModeControl, RANAP_Criticality_reject, @@ -271,7 +280,7 @@ struct msgb *ranap_new_msg_sec_mod_compl( RANAP_SecurityModeCompleteIEs_t ies; RANAP_SecurityModeComplete_t out; struct msgb *msg; - int i, rc; + int rc; memset(&ies, 0, sizeof(ies)); memset(&out, 0, sizeof(out)); @@ -282,6 +291,10 @@ struct msgb *ranap_new_msg_sec_mod_compl( /* ies -> out */ rc = ranap_encode_securitymodecompleteies(&out, &ies); + if (rc < 0) { + LOGP(DRANAP, LOGL_ERROR, "error encoding security mode complete IEs: %d\n", rc); + return NULL; + } /* out -> msg */ msg = ranap_generate_successful_outcome(RANAP_ProcedureCode_id_SecurityModeControl, @@ -317,10 +330,14 @@ struct msgb *ranap_new_msg_common_id(const char *imsi) /* ies -> out */ rc = ranap_encode_commonid_ies(&out, &ies); + /* release dynamic allocations attached to ies */ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_PermanentNAS_UE_ID, &ies.permanentNAS_UE_ID); - if (rc < 0) + + if (rc < 0) { + LOGP(DRANAP, LOGL_ERROR, "error encoding common id IEs: %d\n", rc); return NULL; + } /* out -> msg */ msg = ranap_generate_initiating_message(RANAP_ProcedureCode_id_CommonID, @@ -349,8 +366,10 @@ struct msgb *ranap_new_msg_iu_rel_cmd(const RANAP_Cause_t *cause_in) /* ies -> out */ rc = ranap_encode_iu_releasecommandies(&out, &ies); - if (rc < 0) + if (rc < 0) { + LOGP(DRANAP, LOGL_ERROR, "error encoding release command IEs: %d\n", rc); return NULL; + } /* out -> msg */ msg = ranap_generate_initiating_message(RANAP_ProcedureCode_id_Iu_Release, @@ -377,8 +396,10 @@ struct msgb *ranap_new_msg_iu_rel_compl(void) /* ies -> out */ rc = ranap_encode_iu_releasecompleteies(&out, &ies); - if (rc < 0) + if (rc < 0) { + LOGP(DRANAP, LOGL_ERROR, "error encoding release complete IEs: %d\n", rc); return NULL; + } /* out -> msg */ msg = ranap_generate_successful_outcome(RANAP_ProcedureCode_id_Iu_Release, @@ -434,11 +455,15 @@ struct msgb *ranap_new_msg_paging_cmd(const char *imsi, const uint32_t *tmsi, in /* ies -> out */ rc = ranap_encode_pagingies(&out, &ies); + /* release dynamic allocation attached to ies */ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_PermanentNAS_UE_ID, &ies.permanentNAS_UE_ID); ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_TemporaryUE_ID, &ies.temporaryUE_ID); - if (rc < 0) + + if (rc < 0) { + LOGP(DRANAP, LOGL_ERROR, "error encoding paging IEs: %d\n", rc); return NULL; + } /* out -> msg */ msg = ranap_generate_initiating_message(RANAP_ProcedureCode_id_Paging, @@ -638,6 +663,7 @@ static void new_transp_layer_addr(BIT_STRING_t *out, uint32_t ip, bool use_x213_ { uint8_t *buf; unsigned int len; + uint32_t ip_h = ntohl(ip); if (use_x213_nsap) { len = 160/8; @@ -645,11 +671,11 @@ static void new_transp_layer_addr(BIT_STRING_t *out, uint32_t ip, bool use_x213_ buf[0] = 0x35; /* AFI For IANA ICP */ buf[1] = 0x00; /* See A.5.2.1.2.7 of X.213 */ buf[2] = 0x01; - *(uint32_t *)&buf[3] = ntohl(ip); + memcpy(&buf[3], &ip_h, sizeof(ip_h)); } else { - len = 4; + len = sizeof(ip_h); buf = CALLOC(len, sizeof(uint8_t)); - *(uint32_t *)buf = ntohl(ip); + memcpy(buf, &ip_h, sizeof(ip_h)); } out->buf = buf; out->size = len; @@ -716,7 +742,8 @@ static void assign_new_ra_id(RANAP_RAB_ID_t *id, uint8_t rab_id) /*! \brief generate RANAP RAB ASSIGNMENT REQUEST message for CS (voice). * See 3GPP TS 25.413 8.2. - * RAB ID: 3GPP TS 25.413 9.2.1.2 + * RAB ID: 3GPP TS 25.413 9.2.1.2. + * \param rtp_ip MGW's RTP IPv4 address in *network* byte order. */ struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id, uint32_t rtp_ip, uint16_t rtp_port, @@ -785,7 +812,8 @@ struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id, uint32_t rtp_ip, return msg; } -/*! \brief generate RANAP RAB ASSIGNMENT REQUEST message for PS (data) */ +/*! \brief generate RANAP RAB ASSIGNMENT REQUEST message for PS (data) + * \param gtp_ip SGSN's GTP IPv4 address in *network* byte order. */ struct msgb *ranap_new_msg_rab_assign_data(uint8_t rab_id, uint32_t gtp_ip, uint32_t gtp_tei, bool use_x213_nsap) { @@ -876,13 +904,17 @@ struct msgb *ranap_new_msg_iu_rel_req(const RANAP_Cause_t *cause) memcpy(&ies.cause, cause, sizeof(ies.cause)); rc = ranap_encode_iu_releaserequesties(&out, &ies); - if (rc < 0) + if (rc < 0) { + LOGP(DRANAP, LOGL_ERROR, "error encoding release request IEs: %d\n", rc); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_Iu_ReleaseRequest, &out); return NULL; + } /* encode the output into the msgb */ msg = ranap_generate_initiating_message(RANAP_ProcedureCode_id_Iu_ReleaseRequest, RANAP_Criticality_reject, &asn_DEF_RANAP_Iu_ReleaseRequest, &out); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_Iu_ReleaseRequest, &out); return msg; @@ -912,11 +944,15 @@ struct msgb *ranap_new_msg_rab_rel_req(uint8_t rab_id, const RANAP_Cause_t *caus /* encoe the list IEs into the output */ rc = ranap_encode_rab_releaserequesties(&out, &ies); - if (rc < 0) - return NULL; + /* 'out' has been generated, we can release the input */ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_ReleaseList, &ies.raB_ReleaseList); + if (rc < 0) { + LOGP(DRANAP, LOGL_ERROR, "error encoding release request IEs: %d\n", rc); + return NULL; + } + /* encode the output into the msgb */ msg = ranap_generate_initiating_message(RANAP_ProcedureCode_id_RAB_ReleaseRequest, RANAP_Criticality_reject, diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 28ad56c..493bc18 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -1,4 +1,4 @@ -AM_CFLAGS = -g -I$(top_srcdir)/src/tests \ +AM_CFLAGS = -g -Wall -I$(top_srcdir)/src/tests \ -I$(top_srcdir)/include -I$(top_builddir)/include \ $(OSMOVTY_CFLAGS) $(OSMOCORE_CFLAGS) $(OSMOGSM_CFLAGS) \ $(OSMONETIF_CFLAGS) $(ASN1C_CFLAGS) $(OSMOSIGTRAN_CFLAGS) diff --git a/src/tests/hnb-test.c b/src/tests/hnb-test.c index 68a3117..446a14a 100644 --- a/src/tests/hnb-test.c +++ b/src/tests/hnb-test.c @@ -267,10 +267,11 @@ int hnb_test_nas_rx_lu_accept(struct gsm48_hdr *gh, int len, int *sent_tmsi) lai = (struct gsm48_loc_area_id *)&gh->data[0]; - uint16_t mcc, mnc, lac; - gsm48_decode_lai(lai, &mcc, &mnc, &lac); - printf("LU: mcc %hd mnc %hd lac %hd\n", - mcc, mnc, lac); + struct osmo_location_area_id laid; + gsm48_decode_lai2(lai, &laid); + printf("LU: mcc %s mnc %s lac %hd\n", + osmo_mcc_name(laid.plmn.mcc), osmo_mnc_name(laid.plmn.mnc, laid.plmn.mnc_3_digits), + laid.lac); struct tlv_parsed tp; int parse_res; @@ -904,7 +905,6 @@ static void hnbtest_vty_init(void) install_element_ve(&chan_cmd); install_node(&chan_node, NULL); - vty_install_default(CHAN_NODE); } static void handle_options(int argc, char **argv) diff --git a/src/tests/test-helpers.c b/src/tests/test-helpers.c index 44fd735..f218a79 100644 --- a/src/tests/test-helpers.c +++ b/src/tests/test-helpers.c @@ -105,6 +105,8 @@ void test_asn1_helpers(void) ASSERT(enc.size == 24/8); ASSERT(enc.bits_unused == 0); + talloc_free(buffer); + rc = aper_encode_to_new_buffer(&asn_DEF_BIT_STRING, 0, &enc, (void **) &buffer); printf("Encoded: %s\n", osmo_hexdump_nospc(buffer, rc)); @@ -118,6 +120,7 @@ void test_asn1_helpers(void) printf("Decoding large string from asn1: %s\n", text); ASSERT(rc == 31); + talloc_free(buffer); } void test_ranap_common(void) @@ -211,5 +214,6 @@ int main(int argc, char **argv) test_asn1_helpers(); test_ranap_common(); + test_common_cleanup(); return 0; } diff --git a/src/tests/test-hnbap.c b/src/tests/test-hnbap.c index ef46070..dfd5ae9 100644 --- a/src/tests/test-hnbap.c +++ b/src/tests/test-hnbap.c @@ -142,6 +142,8 @@ void test_asn1_decoding(void) printf("HNBAP UE Register request from IMSI %s\n", imsi); hnbap_free_ueregisterrequesties(&ue_req_ies); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_PDU, pdu); + memset(pdu, 0, sizeof(*pdu)); dec_ret = aper_decode(NULL, &asn_DEF_HNBAP_PDU, (void **) &pdu, hnbap_ue_reg_acc, sizeof(hnbap_ue_reg_acc), 0, 0); @@ -163,6 +165,7 @@ void test_asn1_decoding(void) printf("HNBAP UE Register accept to IMSI %s\n", imsi); hnbap_free_ueregisteraccepties(&ue_acc_ies); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_PDU, pdu); } int main(int argc, char **argv) @@ -175,6 +178,7 @@ int main(int argc, char **argv) test_asn1_decoding(); + test_common_cleanup(); return 0; } diff --git a/src/tests/test-ranap.c b/src/tests/test-ranap.c index c1c7003..05be874 100644 --- a/src/tests/test-ranap.c +++ b/src/tests/test-ranap.c @@ -197,6 +197,8 @@ int main(int argc, char **argv) talloc_report(talloc_asn1_ctx, stdout); talloc_report(tall_msgb_ctx, stdout); //talloc_report(NULL, stdout); + + test_common_cleanup(); printf("exit\n"); exit(0); } diff --git a/src/tests/test_common.c b/src/tests/test_common.c index c8aafdd..eeb0bec 100644 --- a/src/tests/test_common.c +++ b/src/tests/test_common.c @@ -37,6 +37,8 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/logging.h> +#include <osmocom/ranap/ranap_common.h> + #include <osmocom/iuh/hnbgw.h> void *talloc_asn1_ctx; @@ -69,11 +71,13 @@ static const struct log_info test_log_info = { .num_cat = ARRAY_SIZE(log_cat), }; +static void *msgb_ctx; + int test_common_init(void) { int rc; - msgb_talloc_ctx_init(NULL, 0); + msgb_ctx = msgb_talloc_ctx_init(NULL, 0); talloc_asn1_ctx = talloc_named_const(NULL, 0, "asn1_context"); rc = osmo_init_logging(&test_log_info); @@ -85,3 +89,22 @@ int test_common_init(void) log_set_print_filename(osmo_stderr_target, 0); log_set_use_color(osmo_stderr_target, 0); } + +void test_common_cleanup(void) +{ + if (talloc_total_blocks(msgb_ctx) != 1 + || talloc_total_size(msgb_ctx) != 0) + talloc_report_full(msgb_ctx, stderr); + + OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1); + OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0); + talloc_free(msgb_ctx); + + if (talloc_total_blocks(talloc_asn1_ctx) != 1 + || talloc_total_size(talloc_asn1_ctx) != 0) + talloc_report_full(talloc_asn1_ctx, stderr); + + OSMO_ASSERT(talloc_total_blocks(talloc_asn1_ctx) == 1); + OSMO_ASSERT(talloc_total_size(talloc_asn1_ctx) == 0); + talloc_free(talloc_asn1_ctx); +} diff --git a/src/tests/test_common.h b/src/tests/test_common.h index 1af1abd..836d999 100644 --- a/src/tests/test_common.h +++ b/src/tests/test_common.h @@ -1,3 +1,4 @@ #pragma once int test_common_init(void); +void test_common_cleanup(void); |