mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
				synced 2025-11-03 13:43:29 +00:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
			neels/dgsm
			...
			laforge/us
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					c3de9a62aa | ||
| 
						 | 
					08d5204f0c | ||
| 
						 | 
					edc1fc0c25 | ||
| 
						 | 
					e2e5cefda9 | ||
| 
						 | 
					9e59287467 | ||
| 
						 | 
					7057711ca9 | ||
| 
						 | 
					3a190461ee | ||
| 
						 | 
					f76578d6a8 | ||
| 
						 | 
					2c8bfe7278 | ||
| 
						 | 
					0c0c3a1dde | ||
| 
						 | 
					d1bc55c3b6 | 
@@ -17,3 +17,4 @@ ctrl
 | 
				
			|||||||
hlr
 | 
					hlr
 | 
				
			||||||
 gsup
 | 
					 gsup
 | 
				
			||||||
  bind ip 127.0.0.1
 | 
					  bind ip 127.0.0.1
 | 
				
			||||||
 | 
					 ussd route prefix *#100# internal own-msisdn
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,12 +32,14 @@ noinst_HEADERS = \
 | 
				
			|||||||
	ctrl.h \
 | 
						ctrl.h \
 | 
				
			||||||
	hlr_vty.h \
 | 
						hlr_vty.h \
 | 
				
			||||||
	hlr_vty_subscr.h \
 | 
						hlr_vty_subscr.h \
 | 
				
			||||||
 | 
						hlr_ussd.h \
 | 
				
			||||||
	db_bootstrap.h \
 | 
						db_bootstrap.h \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bin_PROGRAMS = \
 | 
					bin_PROGRAMS = \
 | 
				
			||||||
	osmo-hlr \
 | 
						osmo-hlr \
 | 
				
			||||||
	osmo-hlr-db-tool \
 | 
						osmo-hlr-db-tool \
 | 
				
			||||||
 | 
						osmo-euse-demo \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
osmo_hlr_SOURCES = \
 | 
					osmo_hlr_SOURCES = \
 | 
				
			||||||
@@ -55,6 +57,7 @@ osmo_hlr_SOURCES = \
 | 
				
			|||||||
	hlr_vty.c \
 | 
						hlr_vty.c \
 | 
				
			||||||
	hlr_vty_subscr.c \
 | 
						hlr_vty_subscr.c \
 | 
				
			||||||
	gsup_send.c \
 | 
						gsup_send.c \
 | 
				
			||||||
 | 
						hlr_ussd.c \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
osmo_hlr_LDADD = \
 | 
					osmo_hlr_LDADD = \
 | 
				
			||||||
@@ -96,6 +99,16 @@ db_test_LDADD = \
 | 
				
			|||||||
	$(SQLITE3_LIBS) \
 | 
						$(SQLITE3_LIBS) \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					osmo_euse_demo_SOURCES = \
 | 
				
			||||||
 | 
						osmo-euse-demo.c \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					osmo_euse_demo_LDADD = \
 | 
				
			||||||
 | 
						$(top_builddir)/src/gsupclient/libosmo-gsup-client.la \
 | 
				
			||||||
 | 
						$(LIBOSMOCORE_LIBS) \
 | 
				
			||||||
 | 
						$(LIBOSMOGSM_LIBS) \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BOOTSTRAP_SQL = $(top_srcdir)/sql/hlr.sql
 | 
					BOOTSTRAP_SQL = $(top_srcdir)/sql/hlr.sql
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_bootstrap.sed
 | 
					db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_bootstrap.sed
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@
 | 
				
			|||||||
#include <osmocom/core/linuxlist.h>
 | 
					#include <osmocom/core/linuxlist.h>
 | 
				
			||||||
#include <osmocom/core/talloc.h>
 | 
					#include <osmocom/core/talloc.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "logging.h"
 | 
				
			||||||
#include "gsup_server.h"
 | 
					#include "gsup_server.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct gsup_route {
 | 
					struct gsup_route {
 | 
				
			||||||
@@ -60,6 +61,8 @@ int gsup_route_add(struct osmo_gsup_conn *conn, const uint8_t *addr, size_t addr
 | 
				
			|||||||
	if (!gr)
 | 
						if (!gr)
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DMAIN, LOGL_INFO, "Adding GSUP route for %s\n", addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gr->addr = talloc_memdup(gr, addr, addrlen);
 | 
						gr->addr = talloc_memdup(gr, addr, addrlen);
 | 
				
			||||||
	gr->conn = conn;
 | 
						gr->conn = conn;
 | 
				
			||||||
	llist_add_tail(&gr->list, &conn->server->routes);
 | 
						llist_add_tail(&gr->list, &conn->server->routes);
 | 
				
			||||||
@@ -75,6 +78,8 @@ int gsup_route_del_conn(struct osmo_gsup_conn *conn)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	llist_for_each_entry_safe(gr, gr2, &conn->server->routes, list) {
 | 
						llist_for_each_entry_safe(gr, gr2, &conn->server->routes, list) {
 | 
				
			||||||
		if (gr->conn == conn) {
 | 
							if (gr->conn == conn) {
 | 
				
			||||||
 | 
								LOGP(DMAIN, LOGL_INFO, "Removing GSUP route for %s (GSUP disconnect)\n",
 | 
				
			||||||
 | 
								     gr->addr);
 | 
				
			||||||
			llist_del(&gr->list);
 | 
								llist_del(&gr->list);
 | 
				
			||||||
			talloc_free(gr);
 | 
								talloc_free(gr);
 | 
				
			||||||
			num_deleted++;
 | 
								num_deleted++;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								src/hlr.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/hlr.c
									
									
									
									
									
								
							@@ -42,8 +42,9 @@
 | 
				
			|||||||
#include "rand.h"
 | 
					#include "rand.h"
 | 
				
			||||||
#include "luop.h"
 | 
					#include "luop.h"
 | 
				
			||||||
#include "hlr_vty.h"
 | 
					#include "hlr_vty.h"
 | 
				
			||||||
 | 
					#include "hlr_ussd.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct hlr *g_hlr;
 | 
					struct hlr *g_hlr;
 | 
				
			||||||
static int quit = 0;
 | 
					static int quit = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients.
 | 
					/* Trigger 'Insert Subscriber Data' messages to all connected GSUP clients.
 | 
				
			||||||
@@ -297,10 +298,19 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
 | 
				
			|||||||
	} else
 | 
						} else
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							int rc;
 | 
				
			||||||
 | 
							uint8_t *addr;
 | 
				
			||||||
 | 
							rc = osmo_gsup_conn_ccm_get(conn, &addr, IPAC_IDTAG_SERNR);
 | 
				
			||||||
 | 
							if (rc <= 0) {
 | 
				
			||||||
 | 
								osmo_strlcpy(luop->subscr.imsi, gsup->imsi, sizeof(luop->subscr.imsi));
 | 
				
			||||||
 | 
								lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL);
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		/* TODO: Subscriber allowed to roam in PLMN? */
 | 
							/* TODO: Subscriber allowed to roam in PLMN? */
 | 
				
			||||||
		/* TODO: Update RoutingInfo */
 | 
							/* TODO: Update RoutingInfo */
 | 
				
			||||||
		/* TODO: Reset Flag MS Purged (cs/ps) */
 | 
							/* TODO: Reset Flag MS Purged (cs/ps) */
 | 
				
			||||||
		/* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
 | 
							/* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
 | 
				
			||||||
 | 
							db_subscr_lu(g_hlr->dbc, luop->subscr.id, (char *)addr, luop->is_ps);
 | 
				
			||||||
		lu_op_tx_insert_subscr_data(luop);
 | 
							lu_op_tx_insert_subscr_data(luop);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
@@ -402,6 +412,13 @@ static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
 | 
				
			|||||||
		LOGP(DMAIN, LOGL_ERROR, "Deleting subscriber data for IMSI %s\n",
 | 
							LOGP(DMAIN, LOGL_ERROR, "Deleting subscriber data for IMSI %s\n",
 | 
				
			||||||
		     gsup.imsi);
 | 
							     gsup.imsi);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_PROC_SS_RESULT:
 | 
				
			||||||
 | 
							rx_proc_ss_req(conn, &gsup);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_PROC_SS_ERROR:
 | 
				
			||||||
 | 
							rx_proc_ss_error(conn, &gsup);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
 | 
						case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
 | 
				
			||||||
	case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
 | 
						case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
 | 
				
			||||||
	case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
 | 
						case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
 | 
				
			||||||
@@ -559,6 +576,10 @@ int main(int argc, char **argv)
 | 
				
			|||||||
	vty_info.tall_ctx = hlr_ctx;
 | 
						vty_info.tall_ctx = hlr_ctx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g_hlr = talloc_zero(hlr_ctx, struct hlr);
 | 
						g_hlr = talloc_zero(hlr_ctx, struct hlr);
 | 
				
			||||||
 | 
						INIT_LLIST_HEAD(&g_hlr->euse_list);
 | 
				
			||||||
 | 
						INIT_LLIST_HEAD(&g_hlr->iuse_list);
 | 
				
			||||||
 | 
						INIT_LLIST_HEAD(&g_hlr->ss_sessions);
 | 
				
			||||||
 | 
						INIT_LLIST_HEAD(&g_hlr->ussd_routes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
 | 
						rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
 | 
				
			||||||
	if (rc < 0) {
 | 
						if (rc < 0) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								src/hlr.h
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/hlr.h
									
									
									
									
									
								
							@@ -23,6 +23,9 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/linuxlist.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct hlr_euse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct hlr {
 | 
					struct hlr {
 | 
				
			||||||
	/* GSUP server pointer */
 | 
						/* GSUP server pointer */
 | 
				
			||||||
@@ -37,8 +40,18 @@ struct hlr {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/* Local bind addr */
 | 
						/* Local bind addr */
 | 
				
			||||||
	char *gsup_bind_addr;
 | 
						char *gsup_bind_addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct llist_head euse_list;
 | 
				
			||||||
 | 
						struct hlr_euse *euse_default;
 | 
				
			||||||
 | 
						struct llist_head iuse_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct llist_head ussd_routes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct llist_head ss_sessions;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern struct hlr *g_hlr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct hlr_subscriber;
 | 
					struct hlr_subscriber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr);
 | 
					void osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										565
									
								
								src/hlr_ussd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										565
									
								
								src/hlr_ussd.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,565 @@
 | 
				
			|||||||
 | 
					/* OsmoHLR SS/USSD implementation */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* (C) 2018 Harald Welte <laforge@gnumonks.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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 <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/talloc.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/timer.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsup.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm0480.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/protocol/gsm_04_80.h>
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hlr.h"
 | 
				
			||||||
 | 
					#include "hlr_ussd.h"
 | 
				
			||||||
 | 
					#include "gsup_server.h"
 | 
				
			||||||
 | 
					#include "gsup_router.h"
 | 
				
			||||||
 | 
					#include "logging.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***********************************************************************
 | 
				
			||||||
 | 
					 * core data structures expressing config from VTY
 | 
				
			||||||
 | 
					 ***********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct hlr_euse *euse_find(struct hlr *hlr, const char *name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_euse *euse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_for_each_entry(euse, &hlr->euse_list, list) {
 | 
				
			||||||
 | 
							if (!strcmp(euse->name, name))
 | 
				
			||||||
 | 
								return euse;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct hlr_euse *euse_alloc(struct hlr *hlr, const char *name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_euse *euse = euse_find(hlr, name);
 | 
				
			||||||
 | 
						if (euse)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						euse = talloc_zero(hlr, struct hlr_euse);
 | 
				
			||||||
 | 
						euse->name = talloc_strdup(euse, name);
 | 
				
			||||||
 | 
						euse->hlr = hlr;
 | 
				
			||||||
 | 
						llist_add_tail(&euse->list, &hlr->euse_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return euse;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void euse_del(struct hlr_euse *euse)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						llist_del(&euse->list);
 | 
				
			||||||
 | 
						talloc_free(euse);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct hlr_ussd_route *ussd_route_find_prefix(struct hlr *hlr, const char *prefix)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_ussd_route *rt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_for_each_entry(rt, &hlr->ussd_routes, list) {
 | 
				
			||||||
 | 
							if (!strcmp(rt->prefix, prefix))
 | 
				
			||||||
 | 
								return rt;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct hlr_ussd_route *ussd_route_prefix_alloc_int(struct hlr *hlr, const char *prefix,
 | 
				
			||||||
 | 
											   const struct hlr_iuse *iuse)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_ussd_route *rt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ussd_route_find_prefix(hlr, prefix))
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rt = talloc_zero(hlr, struct hlr_ussd_route);
 | 
				
			||||||
 | 
						rt->prefix = talloc_strdup(rt, prefix);
 | 
				
			||||||
 | 
						rt->u.iuse = iuse;
 | 
				
			||||||
 | 
						llist_add_tail(&rt->list, &hlr->ussd_routes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rt;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct hlr_ussd_route *ussd_route_prefix_alloc_ext(struct hlr *hlr, const char *prefix,
 | 
				
			||||||
 | 
											   struct hlr_euse *euse)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_ussd_route *rt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ussd_route_find_prefix(hlr, prefix))
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rt = talloc_zero(hlr, struct hlr_ussd_route);
 | 
				
			||||||
 | 
						rt->prefix = talloc_strdup(rt, prefix);
 | 
				
			||||||
 | 
						rt->is_external = true;
 | 
				
			||||||
 | 
						rt->u.euse = euse;
 | 
				
			||||||
 | 
						llist_add_tail(&rt->list, &hlr->ussd_routes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rt;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ussd_route_del(struct hlr_ussd_route *rt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						llist_del(&rt->list);
 | 
				
			||||||
 | 
						talloc_free(rt);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct hlr_ussd_route *ussd_route_lookup_7bit(struct hlr *hlr, const char *ussd_code)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_ussd_route *rt;
 | 
				
			||||||
 | 
						llist_for_each_entry(rt, &hlr->ussd_routes, list) {
 | 
				
			||||||
 | 
							if (!strncmp(ussd_code, rt->prefix, strlen(rt->prefix))) {
 | 
				
			||||||
 | 
								LOGP(DSS, LOGL_DEBUG, "Found EUSE %s (prefix %s) for USSD Code '%s'\n",
 | 
				
			||||||
 | 
									rt->u.euse->name, rt->prefix, ussd_code);
 | 
				
			||||||
 | 
								return rt;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DSS, LOGL_DEBUG, "Could not find Route for USSD Code '%s'\n", ussd_code);
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***********************************************************************
 | 
				
			||||||
 | 
					 * handling functions for individual GSUP messages
 | 
				
			||||||
 | 
					 ***********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOGPSS(ss, lvl, fmt, args...) \
 | 
				
			||||||
 | 
						LOGP(DSS, lvl, "%s/0x%08x: " fmt, (ss)->imsi, (ss)->session_id, ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ss_session {
 | 
				
			||||||
 | 
						/* link us to hlr->ss_sessions */
 | 
				
			||||||
 | 
						struct llist_head list;
 | 
				
			||||||
 | 
						/* imsi of this session */
 | 
				
			||||||
 | 
						char imsi[GSM23003_IMSI_MAX_DIGITS+2];
 | 
				
			||||||
 | 
						/* ID of this session (unique per IMSI) */
 | 
				
			||||||
 | 
						uint32_t session_id;
 | 
				
			||||||
 | 
						/* state of the session */
 | 
				
			||||||
 | 
						enum osmo_gsup_session_state state;
 | 
				
			||||||
 | 
						/* time-out when we will delete the session */
 | 
				
			||||||
 | 
						struct osmo_timer_list timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* is this USSD for an external handler (EUSE): true */
 | 
				
			||||||
 | 
						bool is_external;
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							/* external USSD Entity responsible for this session */
 | 
				
			||||||
 | 
							struct hlr_euse *euse;
 | 
				
			||||||
 | 
							/* internal USSD Entity responsible for this session */
 | 
				
			||||||
 | 
							const struct hlr_iuse *iuse;
 | 
				
			||||||
 | 
						} u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* we don't keep a pointer to the osmo_gsup_{route,conn} towards the MSC/VLR here,
 | 
				
			||||||
 | 
						 * as this might change during inter-VLR hand-over, and we simply look-up the serving MSC/VLR
 | 
				
			||||||
 | 
						 * every time we receive an USSD component from the EUSE */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ss_session *ss_session_find(struct hlr *hlr, const char *imsi, uint32_t session_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ss_session *ss;
 | 
				
			||||||
 | 
						llist_for_each_entry(ss, &hlr->ss_sessions, list) {
 | 
				
			||||||
 | 
							if (!strcmp(ss->imsi, imsi) && ss->session_id == session_id)
 | 
				
			||||||
 | 
								return ss;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ss_session_free(struct ss_session *ss)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						osmo_timer_del(&ss->timeout);
 | 
				
			||||||
 | 
						llist_del(&ss->list);
 | 
				
			||||||
 | 
						talloc_free(ss);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void ss_session_timeout(void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ss_session *ss = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPSS(ss, LOGL_NOTICE, "SS Session Timeout, destroying\n");
 | 
				
			||||||
 | 
						/* FIXME: should we send a ReturnError component to the MS? */
 | 
				
			||||||
 | 
						ss_session_free(ss);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ss_session *ss_session_alloc(struct hlr *hlr, const char *imsi, uint32_t session_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ss_session *ss;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OSMO_ASSERT(!ss_session_find(hlr, imsi, session_id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ss = talloc_zero(hlr, struct ss_session);
 | 
				
			||||||
 | 
						OSMO_ASSERT(ss);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OSMO_STRLCPY_ARRAY(ss->imsi, imsi);
 | 
				
			||||||
 | 
						ss->session_id = session_id;
 | 
				
			||||||
 | 
						osmo_timer_setup(&ss->timeout, ss_session_timeout, ss);
 | 
				
			||||||
 | 
						/* NOTE: The timeout is currently global and not refreshed with subsequent messages
 | 
				
			||||||
 | 
						 * within the SS/USSD session.  So 30s after the initial SS message, the session will
 | 
				
			||||||
 | 
						 * timeout! */
 | 
				
			||||||
 | 
						osmo_timer_schedule(&ss->timeout, 30, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_add_tail(&ss->list, &hlr->ss_sessions);
 | 
				
			||||||
 | 
						return ss;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***********************************************************************
 | 
				
			||||||
 | 
					 * handling functions for encoding SS messages + wrapping them in GSUP
 | 
				
			||||||
 | 
					 ***********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ss_tx_to_ms(struct ss_session *ss, enum osmo_gsup_message_type gsup_msg_type,
 | 
				
			||||||
 | 
								bool final, struct msgb *ss_msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct osmo_gsup_message resp = {0};
 | 
				
			||||||
 | 
						struct msgb *resp_msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp.message_type = gsup_msg_type;
 | 
				
			||||||
 | 
						OSMO_STRLCPY_ARRAY(resp.imsi, ss->imsi);
 | 
				
			||||||
 | 
						if (final)
 | 
				
			||||||
 | 
							resp.session_state = OSMO_GSUP_SESSION_STATE_END;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							resp.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
 | 
				
			||||||
 | 
						resp.session_id = ss->session_id;
 | 
				
			||||||
 | 
						if (ss_msg) {
 | 
				
			||||||
 | 
							resp.ss_info = msgb_data(ss_msg);
 | 
				
			||||||
 | 
							resp.ss_info_len = msgb_length(ss_msg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp_msg = gsm0480_msgb_alloc_name(__func__);
 | 
				
			||||||
 | 
						OSMO_ASSERT(resp_msg);
 | 
				
			||||||
 | 
						osmo_gsup_encode(resp_msg, &resp);
 | 
				
			||||||
 | 
						msgb_free(ss_msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* FIXME: resolve this based on the database vlr_addr */
 | 
				
			||||||
 | 
						return osmo_gsup_addr_send(g_hlr->gs, (uint8_t *)"MSC-00-00-00-00-00-00", 22, resp_msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					static int ss_tx_reject(struct ss_session *ss, int invoke_id, uint8_t problem_tag,
 | 
				
			||||||
 | 
								uint8_t problem_code)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msgb *msg = gsm0480_gen_reject(invoke_id, problem_tag, problem_code);
 | 
				
			||||||
 | 
						LOGPSS(ss, LOGL_NOTICE, "Tx Reject(%u, 0x%02x, 0x%02x)\n", invoke_id,
 | 
				
			||||||
 | 
							problem_tag, problem_code);
 | 
				
			||||||
 | 
						OSMO_ASSERT(msg);
 | 
				
			||||||
 | 
						return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ss_tx_error(struct ss_session *ss, uint8_t invoke_id, uint8_t error_code)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msgb *msg = gsm0480_gen_return_error(invoke_id, error_code);
 | 
				
			||||||
 | 
						LOGPSS(ss, LOGL_NOTICE, "Tx ReturnError(%u, 0x%02x)\n", invoke_id, error_code);
 | 
				
			||||||
 | 
						OSMO_ASSERT(msg);
 | 
				
			||||||
 | 
						return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ss_tx_ussd_7bit(struct ss_session *ss, bool final, uint8_t invoke_id, const char *text)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msgb *msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);
 | 
				
			||||||
 | 
						LOGPSS(ss, LOGL_INFO, "Tx USSD '%s'\n", text);
 | 
				
			||||||
 | 
						OSMO_ASSERT(msg);
 | 
				
			||||||
 | 
						return ss_tx_to_ms(ss, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***********************************************************************
 | 
				
			||||||
 | 
					 * Internal USSD Handlers
 | 
				
			||||||
 | 
					 ***********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "db.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_own_msisdn(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
 | 
									  const struct osmo_gsup_message *gsup, const struct ss_request *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_subscriber subscr;
 | 
				
			||||||
 | 
						char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
 | 
				
			||||||
 | 
						switch (rc) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
							if (strlen(subscr.msisdn) == 0)
 | 
				
			||||||
 | 
								snprintf(buf, sizeof(buf), "You have no MSISDN!");
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								snprintf(buf, sizeof(buf), "Your extension is %s\r", subscr.msisdn);
 | 
				
			||||||
 | 
							ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -ENOENT:
 | 
				
			||||||
 | 
							ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case -EIO:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
 | 
									const struct osmo_gsup_message *gsup, const struct ss_request *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
 | 
				
			||||||
 | 
						snprintf(buf, sizeof(buf), "Your IMSI is %s!\n", ss->imsi);
 | 
				
			||||||
 | 
						ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct hlr_iuse hlr_iuses[] = {
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.name = "own-msisdn",
 | 
				
			||||||
 | 
							.handle_ussd = handle_ussd_own_msisdn,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.name = "own-imsi",
 | 
				
			||||||
 | 
							.handle_ussd = handle_ussd_own_imsi,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const struct hlr_iuse *iuse_find(const char *name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < ARRAY_SIZE(hlr_iuses); i++) {
 | 
				
			||||||
 | 
							const struct hlr_iuse *iuse = &hlr_iuses[i];
 | 
				
			||||||
 | 
							if (!strcmp(name, iuse->name))
 | 
				
			||||||
 | 
								return iuse;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***********************************************************************
 | 
				
			||||||
 | 
					 * handling functions for individual GSUP messages
 | 
				
			||||||
 | 
					 ***********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool ss_op_is_ussd(uint8_t opcode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (opcode) {
 | 
				
			||||||
 | 
						case GSM0480_OP_CODE_PROCESS_USS_DATA:
 | 
				
			||||||
 | 
						case GSM0480_OP_CODE_PROCESS_USS_REQ:
 | 
				
			||||||
 | 
						case GSM0480_OP_CODE_USS_REQUEST:
 | 
				
			||||||
 | 
						case GSM0480_OP_CODE_USS_NOTIFY:
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* is this GSUP connection an EUSE (true) or not (false)? */
 | 
				
			||||||
 | 
					static bool conn_is_euse(struct osmo_gsup_conn *conn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						uint8_t *addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = osmo_gsup_conn_ccm_get(conn, &addr, IPAC_IDTAG_SERNR);
 | 
				
			||||||
 | 
						if (rc <= 5)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						if (!strncmp((char *)addr, "EUSE-", 5))
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct hlr_euse *euse_by_conn(struct osmo_gsup_conn *conn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						char *addr;
 | 
				
			||||||
 | 
						struct hlr *hlr = conn->server->priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &addr, IPAC_IDTAG_SERNR);
 | 
				
			||||||
 | 
						if (rc <= 5)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						if (strncmp(addr, "EUSE-", 5))
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return euse_find(hlr, addr+5);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_ss(struct ss_session *ss, const struct osmo_gsup_message *gsup,
 | 
				
			||||||
 | 
								const struct ss_request *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint8_t comp_type = gsup->ss_info[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPSS(ss, LOGL_INFO, "SS CompType=%s, OpCode=%s\n",
 | 
				
			||||||
 | 
							gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode));
 | 
				
			||||||
 | 
						/* FIXME */
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Handle a USSD GSUP message for a given SS Session received from VLR or EUSE */
 | 
				
			||||||
 | 
					static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
 | 
								const struct osmo_gsup_message *gsup, const struct ss_request *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint8_t comp_type = gsup->ss_info[0];
 | 
				
			||||||
 | 
						struct msgb *msg_out;
 | 
				
			||||||
 | 
						bool is_euse_originated = conn_is_euse(conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPSS(ss, LOGL_INFO, "USSD CompType=%s, OpCode=%s '%s'\n",
 | 
				
			||||||
 | 
							gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode),
 | 
				
			||||||
 | 
							req->ussd_text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((ss->is_external && !ss->u.euse) || !ss->u.iuse) {
 | 
				
			||||||
 | 
							LOGPSS(ss, LOGL_NOTICE, "USSD for unknown code '%s'\n", req->ussd_text);
 | 
				
			||||||
 | 
							ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SS_NOT_AVAILABLE);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (is_euse_originated) {
 | 
				
			||||||
 | 
							msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP USSD FW");
 | 
				
			||||||
 | 
							OSMO_ASSERT(msg_out);
 | 
				
			||||||
 | 
							/* Received from EUSE, Forward to VLR */
 | 
				
			||||||
 | 
							osmo_gsup_encode(msg_out, gsup);
 | 
				
			||||||
 | 
							/* FIXME: resolve this based on the database vlr_addr */
 | 
				
			||||||
 | 
							osmo_gsup_addr_send(conn->server, (uint8_t *)"MSC-00-00-00-00-00-00", 22, msg_out);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							/* Received from VLR (MS) */
 | 
				
			||||||
 | 
							if (ss->is_external) {
 | 
				
			||||||
 | 
								/* Forward to EUSE */
 | 
				
			||||||
 | 
								char addr[128];
 | 
				
			||||||
 | 
								strcpy(addr, "EUSE-");
 | 
				
			||||||
 | 
								osmo_strlcpy(addr+5, ss->u.euse->name, sizeof(addr)-5);
 | 
				
			||||||
 | 
								conn = gsup_route_find(conn->server, (uint8_t *)addr, strlen(addr)+1);
 | 
				
			||||||
 | 
								if (!conn) {
 | 
				
			||||||
 | 
									LOGPSS(ss, LOGL_ERROR, "Cannot find conn for EUSE %s\n", addr);
 | 
				
			||||||
 | 
									ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_SYSTEM_FAILURE);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP USSD FW");
 | 
				
			||||||
 | 
									OSMO_ASSERT(msg_out);
 | 
				
			||||||
 | 
									osmo_gsup_encode(msg_out, gsup);
 | 
				
			||||||
 | 
									osmo_gsup_conn_send(conn, msg_out);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								/* Handle internally */
 | 
				
			||||||
 | 
								ss->u.iuse->handle_ussd(conn, ss, gsup, req);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* this function is called for any SS_REQ/SS_RESP messages from both the MSC/VLR side as well
 | 
				
			||||||
 | 
					 * as from the EUSE side */
 | 
				
			||||||
 | 
					int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr *hlr = conn->server->priv;
 | 
				
			||||||
 | 
						struct ss_session *ss;
 | 
				
			||||||
 | 
						struct ss_request req = {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DSS, LOGL_DEBUG, "%s/0x%08x: Process SS (%s)\n", gsup->imsi, gsup->session_id,
 | 
				
			||||||
 | 
							osmo_gsup_session_state_name(gsup->session_state));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* decode and find out what kind of SS message it is */
 | 
				
			||||||
 | 
						if (gsup->ss_info && gsup->ss_info_len) {
 | 
				
			||||||
 | 
							if (gsm0480_parse_facility_ie(gsup->ss_info, gsup->ss_info_len, &req)) {
 | 
				
			||||||
 | 
								LOGP(DSS, LOGL_ERROR, "%s/0x%082x: Unable to parse SS request: %s\n",
 | 
				
			||||||
 | 
									gsup->imsi, gsup->session_id,
 | 
				
			||||||
 | 
									osmo_hexdump(gsup->ss_info, gsup->ss_info_len));
 | 
				
			||||||
 | 
								/* FIXME: Send a Reject component? */
 | 
				
			||||||
 | 
								goto out_err;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (gsup->session_state) {
 | 
				
			||||||
 | 
						case OSMO_GSUP_SESSION_STATE_BEGIN:
 | 
				
			||||||
 | 
							/* Check for overlapping Session ID usage */
 | 
				
			||||||
 | 
							if (ss_session_find(hlr, gsup->imsi, gsup->session_id)) {
 | 
				
			||||||
 | 
								LOGP(DSS, LOGL_ERROR, "%s/0x%08x: BEGIN with non-unique session ID!\n",
 | 
				
			||||||
 | 
									gsup->imsi, gsup->session_id);
 | 
				
			||||||
 | 
								goto out_err;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ss = ss_session_alloc(hlr, gsup->imsi, gsup->session_id);
 | 
				
			||||||
 | 
							if (!ss) {
 | 
				
			||||||
 | 
								LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unable to allocate SS session\n",
 | 
				
			||||||
 | 
									gsup->imsi, gsup->session_id);
 | 
				
			||||||
 | 
								goto out_err;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (ss_op_is_ussd(req.opcode)) {
 | 
				
			||||||
 | 
								if (conn_is_euse(conn)) {
 | 
				
			||||||
 | 
									/* EUSE->VLR: MT USSD. EUSE is known ('conn'), VLR is to be resolved */
 | 
				
			||||||
 | 
									ss->u.euse = euse_by_conn(conn);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									/* VLR->EUSE: MO USSD. VLR is known ('conn'), EUSE is to be resolved */
 | 
				
			||||||
 | 
									struct hlr_ussd_route *rt;
 | 
				
			||||||
 | 
									rt = ussd_route_lookup_7bit(hlr, (const char *) req.ussd_text);
 | 
				
			||||||
 | 
									if (rt) {
 | 
				
			||||||
 | 
										if (rt->is_external) {
 | 
				
			||||||
 | 
											ss->is_external = true;
 | 
				
			||||||
 | 
											ss->u.euse = rt->u.euse;
 | 
				
			||||||
 | 
										} else if (rt) {
 | 
				
			||||||
 | 
											ss->is_external = false;
 | 
				
			||||||
 | 
											ss->u.iuse = rt->u.iuse;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								/* dispatch unstructured SS to routing */
 | 
				
			||||||
 | 
								handle_ussd(conn, ss, gsup, &req);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								/* dispatch non-call SS to internal code */
 | 
				
			||||||
 | 
								handle_ss(ss, gsup, &req);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case OSMO_GSUP_SESSION_STATE_CONTINUE:
 | 
				
			||||||
 | 
							ss = ss_session_find(hlr, gsup->imsi, gsup->session_id);
 | 
				
			||||||
 | 
							if (!ss) {
 | 
				
			||||||
 | 
								LOGP(DSS, LOGL_ERROR, "%s/0x%08x: CONTINUE for unknown SS session\n",
 | 
				
			||||||
 | 
									gsup->imsi, gsup->session_id);
 | 
				
			||||||
 | 
								goto out_err;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (ss_op_is_ussd(req.opcode)) {
 | 
				
			||||||
 | 
								/* dispatch unstructured SS to routing */
 | 
				
			||||||
 | 
								handle_ussd(conn, ss, gsup, &req);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								/* dispatch non-call SS to internal code */
 | 
				
			||||||
 | 
								handle_ss(ss, gsup, &req);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case OSMO_GSUP_SESSION_STATE_END:
 | 
				
			||||||
 | 
							ss = ss_session_find(hlr, gsup->imsi, gsup->session_id);
 | 
				
			||||||
 | 
							if (!ss) {
 | 
				
			||||||
 | 
								LOGP(DSS, LOGL_ERROR, "%s/0x%08x: END for unknown SS session\n",
 | 
				
			||||||
 | 
									gsup->imsi, gsup->session_id);
 | 
				
			||||||
 | 
								goto out_err;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (ss_op_is_ussd(req.opcode)) {
 | 
				
			||||||
 | 
								/* dispatch unstructured SS to routing */
 | 
				
			||||||
 | 
								handle_ussd(conn, ss, gsup, &req);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								/* dispatch non-call SS to internal code */
 | 
				
			||||||
 | 
								handle_ss(ss, gsup, &req);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ss_session_free(ss);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							LOGP(DSS, LOGL_ERROR, "%s/0x%08x: Unknown SS State %d\n", gsup->imsi,
 | 
				
			||||||
 | 
								gsup->session_id, gsup->session_state);
 | 
				
			||||||
 | 
							goto out_err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out_err:
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						LOGP(DSS, LOGL_NOTICE, "%s/0x%08x: Process SS ERROR (%s)\n", gsup->imsi, gsup->session_id,
 | 
				
			||||||
 | 
							osmo_gsup_session_state_name(gsup->session_state));
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										57
									
								
								src/hlr_ussd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/hlr_ussd.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/linuxlist.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsup.h>
 | 
				
			||||||
 | 
					#include "gsup_server.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct osmo_gsup_conn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct hlr_ussd_route {
 | 
				
			||||||
 | 
						/* g_hlr.routes */
 | 
				
			||||||
 | 
						struct llist_head list;
 | 
				
			||||||
 | 
						const char *prefix;
 | 
				
			||||||
 | 
						bool is_external;
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct hlr_euse *euse;
 | 
				
			||||||
 | 
							const struct hlr_iuse *iuse;
 | 
				
			||||||
 | 
						} u;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct hlr_euse {
 | 
				
			||||||
 | 
						/* list in the per-hlr list of EUSEs */
 | 
				
			||||||
 | 
						struct llist_head list;
 | 
				
			||||||
 | 
						struct hlr *hlr;
 | 
				
			||||||
 | 
						/* name (must match the IPA ID tag) */
 | 
				
			||||||
 | 
						const char *name;
 | 
				
			||||||
 | 
						/* human-readable description */
 | 
				
			||||||
 | 
						const char *description;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* GSUP connection to the EUSE, if any */
 | 
				
			||||||
 | 
						struct osmo_gsup_conn *conn;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct hlr_euse *euse_find(struct hlr *hlr, const char *name);
 | 
				
			||||||
 | 
					struct hlr_euse *euse_alloc(struct hlr *hlr, const char *name);
 | 
				
			||||||
 | 
					void euse_del(struct hlr_euse *euse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const struct hlr_iuse *iuse_find(const char *name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct hlr_ussd_route *ussd_route_find_prefix(struct hlr *hlr, const char *prefix);
 | 
				
			||||||
 | 
					struct hlr_ussd_route *ussd_route_prefix_alloc_int(struct hlr *hlr, const char *prefix,
 | 
				
			||||||
 | 
											   const struct hlr_iuse *iuse);
 | 
				
			||||||
 | 
					struct hlr_ussd_route *ussd_route_prefix_alloc_ext(struct hlr *hlr, const char *prefix,
 | 
				
			||||||
 | 
											   struct hlr_euse *euse);
 | 
				
			||||||
 | 
					void ussd_route_del(struct hlr_ussd_route *rt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
 | 
				
			||||||
 | 
					int rx_proc_ss_error(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *gsup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ss_session;
 | 
				
			||||||
 | 
					struct ss_request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Internal USSD Handler */
 | 
				
			||||||
 | 
					struct hlr_iuse {
 | 
				
			||||||
 | 
						const char *name;
 | 
				
			||||||
 | 
						/* call-back to be called for any incoming USSD messages for this IUSE */
 | 
				
			||||||
 | 
						int (*handle_ussd)(struct osmo_gsup_conn *conn, struct ss_session *ss,
 | 
				
			||||||
 | 
								   const struct osmo_gsup_message *gsup, const struct ss_request *req);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										186
									
								
								src/hlr_vty.c
									
									
									
									
									
								
							
							
						
						
									
										186
									
								
								src/hlr_vty.c
									
									
									
									
									
								
							@@ -6,6 +6,10 @@
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * All Rights Reserved
 | 
					 * All Rights Reserved
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * (C) 2018 Harald Welte <laforge@gnumonks.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or modify
 | 
					 * 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
 | 
					 * 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
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
@@ -28,12 +32,11 @@
 | 
				
			|||||||
#include <osmocom/vty/misc.h>
 | 
					#include <osmocom/vty/misc.h>
 | 
				
			||||||
#include <osmocom/abis/ipa.h>
 | 
					#include <osmocom/abis/ipa.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hlr.h"
 | 
				
			||||||
#include "hlr_vty.h"
 | 
					#include "hlr_vty.h"
 | 
				
			||||||
#include "hlr_vty_subscr.h"
 | 
					#include "hlr_vty_subscr.h"
 | 
				
			||||||
#include "gsup_server.h"
 | 
					#include "gsup_server.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct hlr *g_hlr = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct cmd_node hlr_node = {
 | 
					struct cmd_node hlr_node = {
 | 
				
			||||||
	HLR_NODE,
 | 
						HLR_NODE,
 | 
				
			||||||
	"%s(config-hlr)# ",
 | 
						"%s(config-hlr)# ",
 | 
				
			||||||
@@ -119,12 +122,180 @@ DEFUN(cfg_hlr_gsup_bind_ip,
 | 
				
			|||||||
	return CMD_SUCCESS;
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***********************************************************************
 | 
				
			||||||
 | 
					 * USSD Entity
 | 
				
			||||||
 | 
					 ***********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hlr_ussd.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define USSD_STR "USSD Configuration\n"
 | 
				
			||||||
 | 
					#define UROUTE_STR "Routing Configuration\n"
 | 
				
			||||||
 | 
					#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define INT_CHOICE "(own-msisdn|own-imsi)"
 | 
				
			||||||
 | 
					#define INT_STR "Internal USSD Handler\n" \
 | 
				
			||||||
 | 
							"Respond with subscribers' own MSISDN\n" \
 | 
				
			||||||
 | 
							"Respond with subscribers' own IMSI\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define EXT_STR "External USSD Handler\n" \
 | 
				
			||||||
 | 
							"Name of External USSD Handler (IPA CCM ID)\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_ussd_route_pfx_int, cfg_ussd_route_pfx_int_cmd,
 | 
				
			||||||
 | 
						"ussd route prefix PREFIX internal " INT_CHOICE,
 | 
				
			||||||
 | 
						USSD_STR UROUTE_STR PREFIX_STR INT_STR)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct hlr_iuse *iuse = iuse_find(argv[1]);
 | 
				
			||||||
 | 
						struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]);
 | 
				
			||||||
 | 
						if (rt) {
 | 
				
			||||||
 | 
							vty_out(vty, "%% Cannot add [another?] route for prefix %s%s", argv[0], VTY_NEWLINE);
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ussd_route_prefix_alloc_int(g_hlr, argv[0], iuse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_ussd_route_pfx_ext, cfg_ussd_route_pfx_ext_cmd,
 | 
				
			||||||
 | 
						"ussd route prefix PREFIX external EUSE",
 | 
				
			||||||
 | 
						USSD_STR UROUTE_STR PREFIX_STR EXT_STR)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_euse *euse = euse_find(g_hlr, argv[1]);
 | 
				
			||||||
 | 
						struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]);
 | 
				
			||||||
 | 
						if (rt) {
 | 
				
			||||||
 | 
							vty_out(vty, "%% Cannot add [another?] route for prefix %s%s", argv[0], VTY_NEWLINE);
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!euse) {
 | 
				
			||||||
 | 
							vty_out(vty, "%% Cannot find euse '%s'%s", argv[1], VTY_NEWLINE);
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ussd_route_prefix_alloc_ext(g_hlr, argv[0], euse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_ussd_no_route_pfx, cfg_ussd_no_route_pfx_cmd,
 | 
				
			||||||
 | 
						"no ussd route prefix PREFIX",
 | 
				
			||||||
 | 
						NO_STR USSD_STR UROUTE_STR PREFIX_STR)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]);
 | 
				
			||||||
 | 
						if (!rt) {
 | 
				
			||||||
 | 
							vty_out(vty, "%% Cannot find route for prefix %s%s", argv[0], VTY_NEWLINE);
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ussd_route_del(rt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_ussd_defaultroute, cfg_ussd_defaultroute_cmd,
 | 
				
			||||||
 | 
						"ussd default-route external EUSE",
 | 
				
			||||||
 | 
						USSD_STR "Configure default-route for all USSD to unknown destinations\n"
 | 
				
			||||||
 | 
						EXT_STR)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (g_hlr->euse_default != euse) {
 | 
				
			||||||
 | 
							vty_out(vty, "Switching default route from %s to %s%s",
 | 
				
			||||||
 | 
								g_hlr->euse_default->name, euse->name, VTY_NEWLINE);
 | 
				
			||||||
 | 
							g_hlr->euse_default = euse;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_ussd_no_defaultroute, cfg_ussd_no_defaultroute_cmd,
 | 
				
			||||||
 | 
						"no ussd default-route",
 | 
				
			||||||
 | 
						NO_STR USSD_STR "Remove the default-route for all USSD to unknown destinations\n")
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						g_hlr->euse_default = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct cmd_node euse_node = {
 | 
				
			||||||
 | 
						EUSE_NODE,
 | 
				
			||||||
 | 
						"%s(config-hlr-euse)# ",
 | 
				
			||||||
 | 
						1,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_euse, cfg_euse_cmd,
 | 
				
			||||||
 | 
						"euse NAME",
 | 
				
			||||||
 | 
						"Configure a particular External USSD Entity\n"
 | 
				
			||||||
 | 
						"Alphanumeric name of the External USSD Entity\n")
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_euse *euse;
 | 
				
			||||||
 | 
						const char *id = argv[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						euse = euse_find(g_hlr, id);
 | 
				
			||||||
 | 
						if (!euse) {
 | 
				
			||||||
 | 
							euse = euse_alloc(g_hlr, id);
 | 
				
			||||||
 | 
							if (!euse)
 | 
				
			||||||
 | 
								return CMD_WARNING;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						vty->index = euse;
 | 
				
			||||||
 | 
						vty->index_sub = &euse->description;
 | 
				
			||||||
 | 
						vty->node = EUSE_NODE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN(cfg_no_euse, cfg_no_euse_cmd,
 | 
				
			||||||
 | 
						"no euse NAME",
 | 
				
			||||||
 | 
						NO_STR "Remove a particular External USSD Entity\n"
 | 
				
			||||||
 | 
						"Alphanumeric name of the External USSD Entity\n")
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
 | 
				
			||||||
 | 
						if (!euse) {
 | 
				
			||||||
 | 
							vty_out(vty, "%% Cannot remove non-existant EUSE %s%s", argv[0], VTY_NEWLINE);
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (g_hlr->euse_default == euse) {
 | 
				
			||||||
 | 
							vty_out(vty, "%% Cannot remove EUSE %s, it is the default route%s", argv[0], VTY_NEWLINE);
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						euse_del(euse);
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void dump_one_euse(struct vty *vty, struct hlr_euse *euse)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vty_out(vty, " euse %s%s", euse->name, VTY_NEWLINE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (g_hlr->euse_default == euse)
 | 
				
			||||||
 | 
							vty_out(vty, "  default-route%s", VTY_NEWLINE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int config_write_euse(struct vty *vty)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hlr_euse *euse;
 | 
				
			||||||
 | 
						struct hlr_ussd_route *rt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_for_each_entry(euse, &g_hlr->euse_list, list)
 | 
				
			||||||
 | 
							dump_one_euse(vty, euse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_for_each_entry(rt, &g_hlr->ussd_routes, list) {
 | 
				
			||||||
 | 
							vty_out(vty, " ussd route prefix %s %s %s%s", rt->prefix,
 | 
				
			||||||
 | 
								rt->is_external ? "external" : "internal",
 | 
				
			||||||
 | 
								rt->is_external ? rt->u.euse->name : rt->u.iuse->name,
 | 
				
			||||||
 | 
								VTY_NEWLINE);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***********************************************************************
 | 
				
			||||||
 | 
					 * Common Code
 | 
				
			||||||
 | 
					 ***********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int hlr_vty_go_parent(struct vty *vty)
 | 
					int hlr_vty_go_parent(struct vty *vty)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	switch (vty->node) {
 | 
						switch (vty->node) {
 | 
				
			||||||
	case GSUP_NODE:
 | 
						case GSUP_NODE:
 | 
				
			||||||
 | 
						case EUSE_NODE:
 | 
				
			||||||
		vty->node = HLR_NODE;
 | 
							vty->node = HLR_NODE;
 | 
				
			||||||
		vty->index = NULL;
 | 
							vty->index = NULL;
 | 
				
			||||||
 | 
							vty->index_sub = NULL;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
	case HLR_NODE:
 | 
						case HLR_NODE:
 | 
				
			||||||
@@ -154,8 +325,6 @@ int hlr_vty_is_config_node(struct vty *vty, int node)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void hlr_vty_init(struct hlr *hlr, const struct log_info *cat)
 | 
					void hlr_vty_init(struct hlr *hlr, const struct log_info *cat)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	g_hlr = hlr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logging_vty_add_cmds(cat);
 | 
						logging_vty_add_cmds(cat);
 | 
				
			||||||
	osmo_talloc_vty_add_cmds();
 | 
						osmo_talloc_vty_add_cmds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -169,5 +338,14 @@ void hlr_vty_init(struct hlr *hlr, const struct log_info *cat)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
 | 
						install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_euse_cmd);
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_no_euse_cmd);
 | 
				
			||||||
 | 
						install_node(&euse_node, config_write_euse);
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_ussd_route_pfx_int_cmd);
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_ussd_route_pfx_ext_cmd);
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_ussd_no_route_pfx_cmd);
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd);
 | 
				
			||||||
 | 
						install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hlr_vty_subscriber_init(hlr);
 | 
						hlr_vty_subscriber_init(hlr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,6 +30,7 @@
 | 
				
			|||||||
enum hlr_vty_node {
 | 
					enum hlr_vty_node {
 | 
				
			||||||
	HLR_NODE = _LAST_OSMOVTY_NODE + 1,
 | 
						HLR_NODE = _LAST_OSMOVTY_NODE + 1,
 | 
				
			||||||
	GSUP_NODE,
 | 
						GSUP_NODE,
 | 
				
			||||||
 | 
						EUSE_NODE,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int hlr_vty_is_config_node(struct vty *vty, int node);
 | 
					int hlr_vty_is_config_node(struct vty *vty, int node);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,8 +33,6 @@ struct vty;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
 | 
					#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct hlr *g_hlr = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
					static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
@@ -478,8 +476,6 @@ DEFUN(subscriber_aud3g,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void hlr_vty_subscriber_init(struct hlr *hlr)
 | 
					void hlr_vty_subscriber_init(struct hlr *hlr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	g_hlr = hlr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	install_element_ve(&subscriber_show_cmd);
 | 
						install_element_ve(&subscriber_show_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_create_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_create_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &subscriber_delete_cmd);
 | 
						install_element(ENABLE_NODE, &subscriber_delete_cmd);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,13 @@ const struct log_info_cat hlr_log_info_cat[] = {
 | 
				
			|||||||
		.color = "\033[1;33m",
 | 
							.color = "\033[1;33m",
 | 
				
			||||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
							.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
						[DSS] = {
 | 
				
			||||||
 | 
							.name = "DSS",
 | 
				
			||||||
 | 
							.description = "Supplementary Services",
 | 
				
			||||||
 | 
							.color = "\033[1;34m",
 | 
				
			||||||
 | 
							.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct log_info hlr_log_info = {
 | 
					const struct log_info hlr_log_info = {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ enum {
 | 
				
			|||||||
	DDB,
 | 
						DDB,
 | 
				
			||||||
	DGSUP,
 | 
						DGSUP,
 | 
				
			||||||
	DAUC,
 | 
						DAUC,
 | 
				
			||||||
 | 
						DSS,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern const struct log_info hlr_log_info;
 | 
					extern const struct log_info hlr_log_info;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										196
									
								
								src/osmo-euse-demo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/osmo-euse-demo.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,196 @@
 | 
				
			|||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <signal.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/msgb.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/select.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/application.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsup.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm0480.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/protocol/gsm_04_80.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/gsupclient/gsup_client.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "logging.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct osmo_gsup_client *g_gc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! send a SS/USSD response to a given imsi/session.
 | 
				
			||||||
 | 
					 *  \param[in] gsupc GSUP client connection through which to send
 | 
				
			||||||
 | 
					 *  \param[in] imsi IMSI of the subscriber
 | 
				
			||||||
 | 
					 *  \param[in] session_id Unique identifier of SS session for which this response is
 | 
				
			||||||
 | 
					 *  \param[in] gsup_msg_type GSUP message type (OSMO_GSUP_MSGT_PROC_SS_{REQUEST,RESULT,ERROR})
 | 
				
			||||||
 | 
					 *  \param[in] final Is this the final result (true=END) or an intermediate result (false=CONTINUE)
 | 
				
			||||||
 | 
					 *  \param[in] msg Optional binary/BER encoded SS date (for FACILITY IE). Can be NULL. Freed in
 | 
				
			||||||
 | 
					 *  		   this function call.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int euse_tx_ss(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id,
 | 
				
			||||||
 | 
							      enum osmo_gsup_message_type gsup_msg_type, bool final, struct msgb *ss_msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct osmo_gsup_message resp = {0};
 | 
				
			||||||
 | 
						struct msgb *resp_msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (gsup_msg_type) {
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_PROC_SS_RESULT:
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_PROC_SS_ERROR:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							msgb_free(ss_msg);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp.message_type = gsup_msg_type;
 | 
				
			||||||
 | 
						OSMO_STRLCPY_ARRAY(resp.imsi, imsi);
 | 
				
			||||||
 | 
						if (final)
 | 
				
			||||||
 | 
							resp.session_state = OSMO_GSUP_SESSION_STATE_END;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							resp.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
 | 
				
			||||||
 | 
						resp.session_id = session_id;
 | 
				
			||||||
 | 
						if (ss_msg) {
 | 
				
			||||||
 | 
							resp.ss_info = msgb_data(ss_msg);
 | 
				
			||||||
 | 
							resp.ss_info_len = msgb_length(ss_msg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp_msg = gsm0480_msgb_alloc_name(__func__);
 | 
				
			||||||
 | 
						OSMO_ASSERT(resp_msg);
 | 
				
			||||||
 | 
						osmo_gsup_encode(resp_msg, &resp);
 | 
				
			||||||
 | 
						msgb_free(ss_msg);
 | 
				
			||||||
 | 
						return osmo_gsup_client_send(gsupc, resp_msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! send a SS/USSD reject to a given IMSI/session.
 | 
				
			||||||
 | 
					 * \param[in] gsupc		GSUP client connection through which to send
 | 
				
			||||||
 | 
					 * \param[in] imsi		IMSI of the subscriber
 | 
				
			||||||
 | 
					 * \param[in] session_id	Unique identifier of SS session for which this response is
 | 
				
			||||||
 | 
					 * \param[in] invoke_id		InvokeID of the request
 | 
				
			||||||
 | 
					 * \param[in] problem_tag	Problem code tag (table 3.13)
 | 
				
			||||||
 | 
					 * \param[in] problem_code	Problem code (table 3.14-3.17)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int euse_tx_ussd_reject(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id,
 | 
				
			||||||
 | 
									int invoke_id, uint8_t problem_tag, uint8_t problem_code)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msgb *msg = gsm0480_gen_reject(invoke_id, problem_tag, problem_code);
 | 
				
			||||||
 | 
						LOGP(DMAIN, LOGL_NOTICE, "Tx %s/0x%08x: Reject(%d, 0x%02x, 0x%02x)\n", imsi, session_id,
 | 
				
			||||||
 | 
							invoke_id, problem_tag, problem_code);
 | 
				
			||||||
 | 
						OSMO_ASSERT(msg);
 | 
				
			||||||
 | 
						return euse_tx_ss(gsupc, imsi, session_id, OSMO_GSUP_MSGT_PROC_SS_RESULT, true, msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! send a SS/USSD response in 7-bit GSM default alphabet o a given imsi/session.
 | 
				
			||||||
 | 
					 * \param[in] gsupc		GSUP client connection through which to send
 | 
				
			||||||
 | 
					 * \param[in] imsi		IMSI of the subscriber
 | 
				
			||||||
 | 
					 * \param[in] session_id	Unique identifier of SS session for which this response is
 | 
				
			||||||
 | 
					 * \param[in] final		Is this the final result (true=END) or an intermediate result
 | 
				
			||||||
 | 
					 * 				(false=CONTINUE)
 | 
				
			||||||
 | 
					 * \param[in] invoke_id		InvokeID of the request
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int euse_tx_ussd_resp_7bit(struct osmo_gsup_client *gsupc, const char *imsi, uint32_t session_id,
 | 
				
			||||||
 | 
									  bool final, uint8_t invoke_id, const char *text)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msgb *ss_msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* encode response; remove L3 header */
 | 
				
			||||||
 | 
						ss_msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);
 | 
				
			||||||
 | 
						LOGP(DMAIN, LOGL_DEBUG, "Tx %s/0x%08x: USSD Result(%d, %s, '%s')\n", imsi, session_id,
 | 
				
			||||||
 | 
							invoke_id, final ? "END" : "CONTINUE", text);
 | 
				
			||||||
 | 
						OSMO_ASSERT(ss_msg);
 | 
				
			||||||
 | 
						return euse_tx_ss(gsupc, imsi, session_id, OSMO_GSUP_MSGT_PROC_SS_RESULT, final, ss_msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int euse_rx_proc_ss_req(struct osmo_gsup_client *gsupc, const struct osmo_gsup_message *gsup)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
 | 
				
			||||||
 | 
						struct ss_request req = {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (gsup->ss_info && gsup->ss_info_len) {
 | 
				
			||||||
 | 
							if (gsm0480_parse_facility_ie(gsup->ss_info, gsup->ss_info_len, &req)) {
 | 
				
			||||||
 | 
								return euse_tx_ussd_reject(gsupc, gsup->imsi, gsup->session_id, -1,
 | 
				
			||||||
 | 
											   GSM_0480_PROBLEM_CODE_TAG_GENERAL,
 | 
				
			||||||
 | 
											   GSM_0480_GEN_PROB_CODE_BAD_STRUCTURE);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DMAIN, LOGL_INFO, "Rx %s/0x%08x: USSD SessionState=%s, OpCode=%s, '%s'\n", gsup->imsi,
 | 
				
			||||||
 | 
							gsup->session_id, osmo_gsup_session_state_name(gsup->session_state),
 | 
				
			||||||
 | 
							gsm0480_op_code_name(req.opcode), req.ussd_text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* we only handle single-request-response USSD in this demo */
 | 
				
			||||||
 | 
						if (gsup->session_state != OSMO_GSUP_SESSION_STATE_BEGIN) {
 | 
				
			||||||
 | 
							return euse_tx_ussd_reject(gsupc, gsup->imsi, gsup->session_id, req.invoke_id,
 | 
				
			||||||
 | 
										   GSM_0480_PROBLEM_CODE_TAG_GENERAL,
 | 
				
			||||||
 | 
										   GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						snprintf(buf, sizeof(buf), "You sent \"%s\"", req.ussd_text);
 | 
				
			||||||
 | 
						return euse_tx_ussd_resp_7bit(gsupc, gsup->imsi, gsup->session_id, true, req.invoke_id, buf);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gsupc_read_cb(struct osmo_gsup_client *gsupc, struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct osmo_gsup_message gsup_msg = {0};
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_ERROR, "Error decoding GSUP: %s\n", msgb_hexdump(msg));
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						DEBUGP(DMAIN, "Rx GSUP %s: %s\n", osmo_gsup_message_type_name(gsup_msg.message_type),
 | 
				
			||||||
 | 
							msgb_hexdump(msg));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//if (strlen(gsup_msg.imsi) < 5)
 | 
				
			||||||
 | 
							//return gsup_send_err_reply(gsupc, gsup.imsi, gsup.message_type, GMM_CAUSE_INV_MAND_INFO);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (gsup_msg.message_type) {
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_PROC_SS_RESULT:
 | 
				
			||||||
 | 
							euse_rx_proc_ss_req(gsupc, &gsup_msg);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case OSMO_GSUP_MSGT_PROC_SS_ERROR:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
 | 
				
			||||||
 | 
								osmo_gsup_message_type_name(gsup_msg.message_type));
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msgb_free(msg);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct log_info_cat default_categories[] = {
 | 
				
			||||||
 | 
						[DMAIN] = {
 | 
				
			||||||
 | 
							.name = "DMAIN",
 | 
				
			||||||
 | 
							.description = "Main Program",
 | 
				
			||||||
 | 
							.enabled = 1, .loglevel = LOGL_DEBUG,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct log_info gsup_log_info = {
 | 
				
			||||||
 | 
						.cat = default_categories,
 | 
				
			||||||
 | 
						.num_cat = ARRAY_SIZE(default_categories),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char **argv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char *server_host = "127.0.0.1";
 | 
				
			||||||
 | 
						uint16_t server_port = OSMO_GSUP_PORT;
 | 
				
			||||||
 | 
						void *ctx = talloc_named_const(NULL, 0, "demo-euse");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_init_logging2(ctx, &gsup_log_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g_gc = osmo_gsup_client_create(ctx, "EUSE-foobar", server_host, server_port, gsupc_read_cb, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (1) {
 | 
				
			||||||
 | 
							osmo_select_main(0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exit(0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Reference in New Issue
	
	Block a user