blob: 691d8898c358786765987c17901e705dbe6a2bf5 [file] [log] [blame]
Harald Weltec4338de2015-12-24 00:40:52 +01001/* IuCS/IuPS Core Network interface of HNB-GW */
2
3/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include <osmocom/core/msgb.h>
22#include <osmocom/core/utils.h>
23#include <osmocom/core/timer.h>
24
25#include <osmocom/sigtran/protocol/sua.h>
26#include <osmocom/sigtran/sua.h>
27#include <osmocom/sigtran/sccp_sap.h>
Neels Hofmeyr3da86082016-03-30 12:36:15 +020028#include <osmocom/sigtran/sccp_helpers.h>
Harald Weltec4338de2015-12-24 00:40:52 +010029
30#include "hnbgw.h"
31#include "hnbgw_rua.h"
Neels Hofmeyr96979af2016-01-05 15:19:44 +010032#include <osmocom/ranap/ranap_ies_defs.h>
33#include <osmocom/ranap/ranap_msg_factory.h>
Harald Weltec4338de2015-12-24 00:40:52 +010034#include "context_map.h"
Harald Weltec4338de2015-12-24 00:40:52 +010035
Harald Weltec4338de2015-12-24 00:40:52 +010036/***********************************************************************
37 * Outbound RANAP RESET to CN
38 ***********************************************************************/
39
40int hnbgw_cnlink_change_state(struct hnbgw_cnlink *cnlink, enum hnbgw_cnlink_state state);
41
42static int transmit_rst(struct hnbgw_cnlink *cnlink)
43{
44 struct msgb *msg;
45 struct msgb *msgprim;
46 RANAP_CN_DomainIndicator_t domain;
47 RANAP_Cause_t cause = {
48 .present = RANAP_Cause_PR_transmissionNetwork,
49 .choice. transmissionNetwork = RANAP_CauseTransmissionNetwork_signalling_transport_resource_failure,
50 };
51
52 if (cnlink->is_ps)
53 domain = RANAP_CN_DomainIndicator_ps_domain;
54 else
55 domain = RANAP_CN_DomainIndicator_cs_domain;
56
57 msg = ranap_new_msg_reset(domain, &cause);
58
Neels Hofmeyr576f6422016-03-30 13:32:40 +020059 return osmo_sccp_tx_unitdata_msg(cnlink->sua_link, &cnlink->local_addr,
60 &cnlink->remote_addr, msg);
Harald Weltec4338de2015-12-24 00:40:52 +010061}
62
63/* Timer callback once T_RafC expires */
64static void cnlink_trafc_cb(void *data)
65{
66 struct hnbgw_cnlink *cnlink = data;
67
68 transmit_rst(cnlink);
69 hnbgw_cnlink_change_state(cnlink, CNLINK_S_EST_RST_TX_WAIT_ACK);
70 /* The spec states that we should abandon after a configurable
71 * number of times. We decide to simply continue trying */
72}
73
74/* change the state of a CN Link */
75int hnbgw_cnlink_change_state(struct hnbgw_cnlink *cnlink, enum hnbgw_cnlink_state state)
76{
77 switch (state) {
78 case CNLINK_S_NULL:
79 case CNLINK_S_EST_PEND:
80 break;
81 case CNLINK_S_EST_CONF:
82 cnlink_trafc_cb(cnlink);
83 break;
84 case CNLINK_S_EST_RST_TX_WAIT_ACK:
85 osmo_timer_schedule(&cnlink->T_RafC, 5, 0);
86 break;
87 case CNLINK_S_EST_ACTIVE:
88 osmo_timer_del(&cnlink->T_RafC);
89 break;
90 }
91}
92
93/***********************************************************************
94 * Incoming primitives from SCCP User SAP
95 ***********************************************************************/
96
97static int cn_ranap_rx_reset_cmd(struct hnbgw_cnlink *cnlink,
98 RANAP_InitiatingMessage_t *imsg)
99{
100 RANAP_ResetIEs_t ies;
101 int rc;
102
103 rc = ranap_decode_reseties(&ies, &imsg->value);
104 /* FIXME: reset resources and return reset ack */
Daniel Willmann11e912a2016-01-07 13:19:30 +0100105
106 ranap_free_reseties(&ies);
Harald Weltec4338de2015-12-24 00:40:52 +0100107 return rc;
108}
109
110static int cn_ranap_rx_reset_ack(struct hnbgw_cnlink *cnlink,
111 RANAP_SuccessfulOutcome_t *omsg)
112{
113 RANAP_ResetAcknowledgeIEs_t ies;
114 int rc;
115
116 rc = ranap_decode_resetacknowledgeies(&ies, &omsg->value);
117
118 hnbgw_cnlink_change_state(cnlink, CNLINK_S_EST_ACTIVE);
119
Daniel Willmann11e912a2016-01-07 13:19:30 +0100120 ranap_free_resetacknowledgeies(&ies);
Harald Weltec4338de2015-12-24 00:40:52 +0100121 return rc;
122}
123
124static int cn_ranap_rx_paging_cmd(struct hnbgw_cnlink *cnlink,
Harald Weltebc4560c2015-12-24 08:46:58 +0100125 RANAP_InitiatingMessage_t *imsg,
126 const uint8_t *data, unsigned int len)
Harald Weltec4338de2015-12-24 00:40:52 +0100127{
Harald Weltebc4560c2015-12-24 08:46:58 +0100128 struct hnb_gw *gw;
129 struct hnb_context *hnb;
Harald Weltec4338de2015-12-24 00:40:52 +0100130 RANAP_PagingIEs_t ies;
Harald Weltebc4560c2015-12-24 08:46:58 +0100131 int rc = 0;
Harald Weltec4338de2015-12-24 00:40:52 +0100132
133 rc = ranap_decode_pagingies(&ies, &imsg->value);
134
Harald Weltebc4560c2015-12-24 08:46:58 +0100135 /* FIXME: determine which HNBs to send this Paging command,
136 * rather than broadcasting to all HNBs */
137 llist_for_each_entry(hnb, &gw->hnb_list, list) {
138 rc = rua_tx_udt(hnb, data, len);
139 }
Daniel Willmann11e912a2016-01-07 13:19:30 +0100140
141 ranap_free_pagingies(&ies);
Harald Weltebc4560c2015-12-24 08:46:58 +0100142 return 0;
Harald Weltec4338de2015-12-24 00:40:52 +0100143}
144
145static int cn_ranap_rx_initiating_msg(struct hnbgw_cnlink *cnlink,
Harald Weltebc4560c2015-12-24 08:46:58 +0100146 RANAP_InitiatingMessage_t *imsg,
147 const uint8_t *data, unsigned int len)
Harald Weltec4338de2015-12-24 00:40:52 +0100148{
149 int rc;
150
151 switch (imsg->procedureCode) {
152 case RANAP_ProcedureCode_id_Reset:
153 return cn_ranap_rx_reset_cmd(cnlink, imsg);
154 case RANAP_ProcedureCode_id_Paging:
Harald Weltebc4560c2015-12-24 08:46:58 +0100155 return cn_ranap_rx_paging_cmd(cnlink, imsg, data, len);
Harald Weltec4338de2015-12-24 00:40:52 +0100156 case RANAP_ProcedureCode_id_OverloadControl: /* Overload ind */
157 break;
158 case RANAP_ProcedureCode_id_ErrorIndication: /* Error ind */
159 break;
160 case RANAP_ProcedureCode_id_ResetResource: /* request */
161 case RANAP_ProcedureCode_id_InformationTransfer:
162 case RANAP_ProcedureCode_id_DirectInformationTransfer:
163 case RANAP_ProcedureCode_id_UplinkInformationExchange:
164 LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
165 "Procedure %u from CN, ignoring\n", imsg->procedureCode);
166 break;
167 default:
168 LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
169 "Procedure %u from CN, ignoring\n", imsg->procedureCode);
170 break;
171 }
172 return 0;
173}
174
175static int cn_ranap_rx_successful_msg(struct hnbgw_cnlink *cnlink,
176 RANAP_SuccessfulOutcome_t *omsg)
177{
178 int rc;
179
180 switch (omsg->procedureCode) {
181 case RANAP_ProcedureCode_id_Reset: /* Reset acknowledge */
182 return cn_ranap_rx_reset_ack(cnlink, omsg);
183 case RANAP_ProcedureCode_id_ResetResource: /* response */
184 case RANAP_ProcedureCode_id_InformationTransfer:
185 case RANAP_ProcedureCode_id_DirectInformationTransfer:
186 case RANAP_ProcedureCode_id_UplinkInformationExchange:
187 LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
188 "Procedure %u from CN, ignoring\n", omsg->procedureCode);
189 break;
190 default:
191 LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
192 "Procedure %u from CN, ignoring\n", omsg->procedureCode);
193 break;
194 }
195 return 0;
196}
197
198
Harald Weltebc4560c2015-12-24 08:46:58 +0100199static int _cn_ranap_rx(struct hnbgw_cnlink *cnlink, RANAP_RANAP_PDU_t *pdu,
200 const uint8_t *data, unsigned int len)
Harald Weltec4338de2015-12-24 00:40:52 +0100201{
202 int rc;
203
204 switch (pdu->present) {
205 case RANAP_RANAP_PDU_PR_initiatingMessage:
Harald Weltebc4560c2015-12-24 08:46:58 +0100206 rc = cn_ranap_rx_initiating_msg(cnlink, &pdu->choice.initiatingMessage,
207 data, len);
Harald Weltec4338de2015-12-24 00:40:52 +0100208 break;
209 case RANAP_RANAP_PDU_PR_successfulOutcome:
210 rc = cn_ranap_rx_successful_msg(cnlink, &pdu->choice.successfulOutcome);
211 break;
212 case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
213 LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
214 "unsuccessful outcome procedure %u from CN, ignoring\n",
215 pdu->choice.unsuccessfulOutcome.procedureCode);
216 break;
217 default:
218 LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
219 "presence %u from CN, ignoring\n", pdu->present);
220 break;
221 }
222}
223
224static int handle_cn_ranap(struct hnbgw_cnlink *cnlink, const uint8_t *data,
225 unsigned int len)
226{
227 RANAP_RANAP_PDU_t _pdu, *pdu = &_pdu;
228 asn_dec_rval_t dec_ret;
229 int rc;
230
231 memset(pdu, 0, sizeof(*pdu));
232 dec_ret = aper_decode(NULL,&asn_DEF_RANAP_RANAP_PDU, (void **) &pdu,
233 data, len, 0, 0);
234 if (dec_ret.code != RC_OK) {
235 LOGP(DRANAP, LOGL_ERROR, "Error in RANAP ASN.1 decode\n");
236 return rc;
237 }
238
Harald Weltebc4560c2015-12-24 08:46:58 +0100239 rc = _cn_ranap_rx(cnlink, pdu, data, len);
Harald Weltec4338de2015-12-24 00:40:52 +0100240
241 return rc;
242}
243
244
245static int handle_cn_unitdata(struct hnbgw_cnlink *cnlink,
246 const struct osmo_scu_unitdata_param *param,
247 struct osmo_prim_hdr *oph)
248{
Harald Welte8c572fe2015-12-26 08:42:07 +0100249 if (param->called_addr.ssn != OSMO_SCCP_SSN_RANAP) {
Harald Weltec4338de2015-12-24 00:40:52 +0100250 LOGP(DMAIN, LOGL_NOTICE, "N-UNITDATA.ind for unknown SSN %u\n",
251 param->called_addr.ssn);
252 return -1;
253 }
254
255 return handle_cn_ranap(cnlink, msgb_l2(oph->msg), msgb_l2len(oph->msg));
256}
257
Harald Welte2ebe42f2015-12-26 23:38:38 +0100258static int handle_cn_conn_conf(void *slink,
Harald Weltec4338de2015-12-24 00:40:52 +0100259 const struct osmo_scu_connect_param *param,
260 struct osmo_prim_hdr *oph)
261{
262 /* we don't actually need to do anything, as RUA towards the HNB
263 * doesn't seem to know any confirmations to its CONNECT
264 * operation */
265
Neels Hofmeyr0ff24432016-04-04 18:33:33 +0200266 LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d\n",
267 param->conn_id);
268 LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() called_addr=%s\n",
269 inet_ntoa(param->called_addr.ip.v4));
270 LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() calling_addr=%s\n",
271 inet_ntoa(param->calling_addr.ip.v4));
272 LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() responding_addr=%s\n",
273 inet_ntoa(param->responding_addr.ip.v4));
274
Harald Weltec4338de2015-12-24 00:40:52 +0100275 return 0;
276}
277
Harald Welte2ebe42f2015-12-26 23:38:38 +0100278static int handle_cn_data_ind(void *slink,
Harald Weltec4338de2015-12-24 00:40:52 +0100279 const struct osmo_scu_data_param *param,
280 struct osmo_prim_hdr *oph)
281{
282 struct hnbgw_context_map *map;
Harald Welte2ebe42f2015-12-26 23:38:38 +0100283 struct hnbgw_cnlink *cnlink = osmo_sua_link_get_user_priv(slink);
Harald Weltec4338de2015-12-24 00:40:52 +0100284
285 /* connection-oriented data is always passed transparently
286 * towards the specific HNB, via a RUA connection identified by
287 * conn_id */
288
289 map = context_map_by_cn(cnlink, param->conn_id);
290 if (!map) {
291 /* FIXME: Return an error / released primitive */
292 return 0;
293 }
294
295 return rua_tx_dt(map->hnb_ctx, map->cn_link->is_ps, map->rua_ctx_id,
296 msgb_l2(oph->msg), msgb_l2len(oph->msg));
297}
298
Harald Welte2ebe42f2015-12-26 23:38:38 +0100299static int handle_cn_disc_ind(void *slink,
Harald Weltec4338de2015-12-24 00:40:52 +0100300 const struct osmo_scu_disconn_param *param,
301 struct osmo_prim_hdr *oph)
302{
303 struct hnbgw_context_map *map;
Harald Welte2ebe42f2015-12-26 23:38:38 +0100304 struct hnbgw_cnlink *cnlink = osmo_sua_link_get_user_priv(slink);
Harald Weltec4338de2015-12-24 00:40:52 +0100305
306 RUA_Cause_t rua_cause = {
307 .present = RUA_Cause_PR_NOTHING,
308 /* FIXME: Convert incoming SCCP cause to RUA cause */
309 };
310
311 /* we need to notify the HNB associated with this connection via
312 * a RUA DISCONNECT */
313
314 map = context_map_by_cn(cnlink, param->conn_id);
315 if (!map) {
316 /* FIXME: Return an error / released primitive */
317 return 0;
318 }
319
320 return rua_tx_disc(map->hnb_ctx, map->cn_link->is_ps, map->rua_ctx_id,
321 &rua_cause, msgb_l2(oph->msg), msgb_l2len(oph->msg));
322}
323
324/* Entry point for primitives coming up from SCCP User SAP */
325static int sccp_sap_up(struct osmo_prim_hdr *oph, void *slink)
326{
327 struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
328 int rc;
329
330 LOGP(DMAIN, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph));
331
332 switch (OSMO_PRIM_HDR(oph)) {
333 case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
334 rc = handle_cn_unitdata(slink, &prim->u.unitdata, oph);
335 break;
336 case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
337 rc = handle_cn_conn_conf(slink, &prim->u.connect, oph);
338 break;
339 case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
340 rc = handle_cn_data_ind(slink, &prim->u.data, oph);
341 break;
342 case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
343 rc = handle_cn_disc_ind(slink, &prim->u.disconnect, oph);
344 break;
345 defualt:
346 LOGP(DMAIN, LOGL_ERROR,
347 "Received unknown prim %u from SCCP USER SAP\n",
348 OSMO_PRIM_HDR(oph));
349 break;
350 }
351
352 msgb_free(oph->msg);
353
354 return 0;
355}
356
357
Neels Hofmeyr1befe6b2016-04-04 18:05:36 +0200358/* Set up a link towards the core network for the circuit switched (is_ps == 0)
359 * or packet switched (is_ps != 0) domain. */
Daniel Willmann4deab942016-01-14 15:35:11 +0100360struct hnbgw_cnlink *hnbgw_cnlink_init(struct hnb_gw *gw, const char *host, uint16_t port, int is_ps)
Harald Weltec4338de2015-12-24 00:40:52 +0100361{
362 struct hnbgw_cnlink *cnlink = talloc_zero(gw, struct hnbgw_cnlink);
363 int rc;
364
365 INIT_LLIST_HEAD(&cnlink->map_list);
366 cnlink->T_RafC.cb = cnlink_trafc_cb;
367 cnlink->T_RafC.data = cnlink;
Harald Welte552fdf12015-12-26 23:39:30 +0100368 cnlink->next_conn_id = 1000;
Daniel Willmann4deab942016-01-14 15:35:11 +0100369 cnlink->is_ps = is_ps;
Neels Hofmeyr576f6422016-03-30 13:32:40 +0200370 osmo_sccp_make_addr_pc_ssn(&cnlink->local_addr, 2,
371 OSMO_SCCP_SSN_RANAP);
372 osmo_sccp_make_addr_pc_ssn(&cnlink->remote_addr, 1,
373 OSMO_SCCP_SSN_RANAP);
Harald Weltec4338de2015-12-24 00:40:52 +0100374
Harald Welte2ebe42f2015-12-26 23:38:38 +0100375 cnlink->sua_user = osmo_sua_user_create(cnlink, sccp_sap_up, cnlink);
Harald Weltec4338de2015-12-24 00:40:52 +0100376 if (!cnlink->sua_user) {
377 LOGP(DMAIN, LOGL_ERROR, "Failed to init SUA\n");
378 goto out_free;
379 }
380 rc = osmo_sua_client_connect(cnlink->sua_user, host, port);
381 if (rc < 0) {
382 LOGP(DMAIN, LOGL_ERROR, "Failed to connect SUA\n");
383 goto out_user;
384 }
385 cnlink->sua_link = osmo_sua_client_get_link(cnlink->sua_user);
386 if (!cnlink->sua_link) {
387 LOGP(DMAIN, LOGL_ERROR, "Failed to get SUA link\n");
388 goto out_disconnect;
389 }
390
391 llist_add_tail(&cnlink->list, &gw->cn_list);
392
393 return cnlink;
394
395out_disconnect:
396 /* FIXME */
397out_user:
398 osmo_sua_user_destroy(cnlink->sua_user);
399out_free:
400 talloc_free(cnlink);
401}