blob: aeda1406a6c76121dc0a49e8496150388025dd22 [file] [log] [blame]
Neels Hofmeyr84da6b12016-05-20 21:59:55 +02001/* Code to manage MSC subscriber connections over IuCS interface */
2
3/*
4 * (C) 2016,2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
5 *
6 * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
7 *
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
19 *
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25#include <inttypes.h>
26
27#include <osmocom/core/logging.h>
28#include <openbsc/debug.h>
29
30#include <openbsc/gsm_data.h>
31#include <openbsc/iu.h>
32#include <openbsc/gsm_subscriber.h>
33#include <openbsc/osmo_msc.h>
34#include <openbsc/vlr.h>
35
36/* For A-interface see libbsc/bsc_api.c subscr_con_allocate() */
37static struct gsm_subscriber_connection *subscr_conn_allocate_iu(struct gsm_network *network,
38 struct ue_conn_ctx *ue,
39 uint16_t lac)
40{
41 struct gsm_subscriber_connection *conn;
42
43 DEBUGP(DIUCS, "Allocating IuCS subscriber conn: lac %d, link_id %p, conn_id %" PRIx32 "\n",
44 lac, ue->link, ue->conn_id);
45
46 conn = talloc_zero(network, struct gsm_subscriber_connection);
47 if (!conn)
48 return NULL;
49
50 conn->network = network;
51 conn->via_ran = RAN_UTRAN_IU;
52 conn->iu.ue_ctx = ue;
53 conn->iu.ue_ctx->rab_assign_addr_enc = network->iu.rab_assign_addr_enc;
54 conn->lac = lac;
55
56 llist_add_tail(&conn->entry, &network->subscr_conns);
57 return conn;
58}
59
60static int same_ue_conn(struct ue_conn_ctx *a, struct ue_conn_ctx *b)
61{
62 if (a == b)
63 return 1;
64 return (a->link == b->link)
65 && (a->conn_id == b->conn_id);
66}
67
68static inline void log_subscribers(struct gsm_network *network)
69{
70 if (!log_check_level(DIUCS, LOGL_DEBUG))
71 return;
72
73 struct gsm_subscriber_connection *conn;
74 int i = 0;
75 llist_for_each_entry(conn, &network->subscr_conns, entry) {
76 DEBUGP(DIUCS, "%3d: %s", i, vlr_subscr_name(conn->vsub));
77 switch (conn->via_ran) {
78 case RAN_UTRAN_IU:
79 DEBUGPC(DIUCS, " Iu");
80 if (conn->iu.ue_ctx) {
81 DEBUGPC(DIUCS, " link %p, conn_id %d",
82 conn->iu.ue_ctx->link,
83 conn->iu.ue_ctx->conn_id
84 );
85 }
86 break;
87 case RAN_GERAN_A:
88 DEBUGPC(DIUCS, " A");
89 /* TODO log A-interface connection details */
90 break;
91 case RAN_UNKNOWN:
92 DEBUGPC(DIUCS, " ?");
93 break;
94 default:
95 DEBUGPC(DIUCS, " invalid");
96 break;
97 }
98 DEBUGPC(DIUCS, "\n");
99 i++;
100 }
101 DEBUGP(DIUCS, "subscribers registered: %d\n", i);
102}
103
104/* Return an existing IuCS subscriber connection record for the given link and
105 * connection IDs, or return NULL if not found. */
106struct gsm_subscriber_connection *subscr_conn_lookup_iu(
107 struct gsm_network *network,
108 struct ue_conn_ctx *ue)
109{
110 struct gsm_subscriber_connection *conn;
111
112 DEBUGP(DIUCS, "Looking for IuCS subscriber: link_id %p, conn_id %" PRIx32 "\n",
113 ue->link, ue->conn_id);
114 log_subscribers(network);
115
116 llist_for_each_entry(conn, &network->subscr_conns, entry) {
117 if (conn->via_ran != RAN_UTRAN_IU)
118 continue;
119 if (!same_ue_conn(conn->iu.ue_ctx, ue))
120 continue;
121 DEBUGP(DIUCS, "Found IuCS subscriber for link_id %p, conn_id %" PRIx32 "\n",
122 ue->link, ue->conn_id);
123 return conn;
124 }
125 DEBUGP(DIUCS, "No IuCS subscriber found for link_id %p, conn_id %" PRIx32 "\n",
126 ue->link, ue->conn_id);
127 return NULL;
128}
129
130/* Receive MM/CC/... message from IuCS (SCCP user SAP).
131 * msg->dst must reference a struct ue_conn_ctx, which identifies the peer that
132 * sent the msg.
133 *
134 * For A-interface see libbsc/bsc_api.c gsm0408_rcvmsg(). */
135int gsm0408_rcvmsg_iucs(struct gsm_network *network, struct msgb *msg,
136 uint16_t *lac)
137{
138 int rc;
139 struct ue_conn_ctx *ue_ctx;
140 struct gsm_subscriber_connection *conn;
141
142 ue_ctx = (struct ue_conn_ctx*)msg->dst;
143
144 /* TODO: are there message types that could allow us to skip this
145 * search? */
146 conn = subscr_conn_lookup_iu(network, ue_ctx);
147
148 if (conn && lac && (conn->lac != *lac)) {
149 LOGP(DIUCS, LOGL_ERROR, "IuCS subscriber has changed LAC"
150 " within the same connection, discarding connection:"
151 " %s from LAC %d to %d\n",
152 vlr_subscr_name(conn->vsub), conn->lac, *lac);
153 /* Deallocate conn with previous LAC */
154 msc_subscr_conn_close(conn, GSM_CAUSE_INV_MAND_INFO);
155 /* At this point we could be tolerant and allocate a new
156 * connection, but changing the LAC within the same connection
157 * is shifty. Rather cancel everything. */
158 return -1;
159 }
160
161 if (conn) {
162 /* Make sure we don't receive RR over IuCS; otherwise all
163 * messages handled by gsm0408_dispatch() are of interest (CC,
164 * MM, SMS, NS_SS, maybe even MM_GPRS and SM_GPRS). */
165 struct gsm48_hdr *gh = msgb_l3(msg);
166 uint8_t pdisc = gh->proto_discr & 0x0f;
167 OSMO_ASSERT(pdisc != GSM48_PDISC_RR);
168
169 msc_dtap(conn, ue_ctx->conn_id, msg);
170 rc = 0;
171 } else {
172 /* allocate a new connection */
173
174 if (!lac) {
175 LOGP(DIUCS, LOGL_ERROR, "New IuCS subscriber"
176 " but no LAC available. Expecting an InitialUE"
177 " message containing a LAI IE."
178 " Dropping connection.\n");
179 return -1;
180 }
181
182 conn = subscr_conn_allocate_iu(network, ue_ctx, *lac);
183 if (!conn)
184 abort();
185
186 /* ownership of conn hereby goes to the MSC: */
187 rc = msc_compl_l3(conn, msg, 0);
188 }
189
190 return rc;
191}