blob: 99317b500a0e3271b754eaa118934be9c587acd8 [file] [log] [blame]
Neels Hofmeyrc4628a32018-12-07 14:47:34 +01001/*
2 * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
3 * All Rights Reserved
4 *
5 * SPDX-License-Identifier: AGPL-3.0+
6 *
7 * Author: Neels Hofmeyr
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#include <osmocom/core/logging.h>
24
25#include <osmocom/sccp/sccp_types.h>
26#include <osmocom/sigtran/sccp_sap.h>
27#include <osmocom/sigtran/sccp_helpers.h>
28
29#include <osmocom/msc/debug.h>
30#include <osmocom/msc/sccp_ran.h>
31#include <osmocom/msc/ran_infra.h>
32
33struct osmo_tdef g_sccp_tdefs[] = {
34 {}
35};
36
37static int sccp_ran_sap_up(struct osmo_prim_hdr *oph, void *_scu);
38
39struct sccp_ran_inst *sccp_ran_init(void *talloc_ctx, struct osmo_sccp_instance *sccp, enum osmo_sccp_ssn ssn,
40 const char *sccp_user_name, struct ran_infra *ran, void *user_data)
41{
42 struct sccp_ran_inst *sri = talloc(talloc_ctx, struct sccp_ran_inst);
43 *sri = (struct sccp_ran_inst){
44 .ran = ran,
45 .sccp = sccp,
46 .user_data = user_data,
47 };
48
49 INIT_LLIST_HEAD(&sri->ran_peers);
50 INIT_LLIST_HEAD(&sri->ran_conns);
51
52 osmo_sccp_local_addr_by_instance(&sri->local_sccp_addr, sccp, ssn);
53 sri->scu = osmo_sccp_user_bind(sccp, sccp_user_name, sccp_ran_sap_up, ssn);
54 osmo_sccp_user_set_priv(sri->scu, sri);
55
56 OSMO_ASSERT(!ran->sri);
57 ran->sri = sri;
58
59 return sri;
60}
61
62static int sccp_ran_sap_up(struct osmo_prim_hdr *oph, void *_scu)
63{
64 struct osmo_sccp_user *scu = _scu;
65 struct sccp_ran_inst *sri = osmo_sccp_user_get_priv(scu);
66 struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
67 struct osmo_sccp_addr *my_addr;
68 struct osmo_sccp_addr *peer_addr;
69 uint32_t conn_id;
70 int rc;
71
72 if (!sri->ran || !sri->ran->sccp_ran_ops.up_l2) {
73 LOG_SCCP_RAN_CL(sri, NULL, LOGL_ERROR, "This RAN type is not set up\n");
74 msgb_free(oph->msg);
75 return -1;
76 }
77
78 switch (OSMO_PRIM_HDR(oph)) {
79 case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
80 /* indication of new inbound connection request */
81 conn_id = prim->u.connect.conn_id;
82 my_addr = &prim->u.connect.called_addr;
83 peer_addr = &prim->u.connect.calling_addr;
84 LOG_SCCP_RAN_CO(sri, peer_addr, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
85
86 if (!msgb_l2(oph->msg) || msgb_l2len(oph->msg) == 0) {
87 LOG_SCCP_RAN_CO(sri, peer_addr, conn_id, LOGL_NOTICE, "Received invalid N-CONNECT.ind\n");
88 rc = -1;
89 break;
90 }
91
92 if (osmo_sccp_addr_ri_cmp(&sri->local_sccp_addr, my_addr))
93 LOG_SCCP_RAN_CO(sri, NULL, conn_id, LOGL_INFO,
94 "Called address is %s which is not the locally configured address\n",
95 osmo_sccp_inst_addr_name(sri->sccp, my_addr));
96
97 /* ensure the local SCCP socket is ACTIVE */
98 osmo_sccp_tx_conn_resp(scu, conn_id, my_addr, NULL, 0);
99
100 rc = sri->ran->sccp_ran_ops.up_l2(sri, peer_addr, true, conn_id, oph->msg);
101 if (rc)
102 osmo_sccp_tx_disconn(scu, conn_id, my_addr, SCCP_RETURN_CAUSE_UNQUALIFIED);
103 break;
104
105 case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
106 /* connection-oriented data received */
107 conn_id = prim->u.data.conn_id;
108 LOG_SCCP_RAN_CO(sri, NULL, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
109
110 rc = sri->ran->sccp_ran_ops.up_l2(sri, NULL, true, conn_id, oph->msg);
111 break;
112
113 case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
114 /* indication of disconnect */
115 conn_id = prim->u.disconnect.conn_id;
116 LOG_SCCP_RAN_CO(sri, NULL, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
117
118 /* If there is no L2 payload in the N-DISCONNECT, no need to dispatch up_l2(). */
119 if (msgb_l2len(oph->msg))
120 rc = sri->ran->sccp_ran_ops.up_l2(sri, NULL, true, conn_id, oph->msg);
121 else
122 rc = 0;
123
124 /* Make sure the ran_conn is dropped. It might seem more optimal to combine the disconnect() into
125 * up_l2(), but since an up_l2() dispatch might already cause the ran_conn to be discarded for other
126 * reasons, a separate disconnect() with a separate conn_id lookup is actually necessary. */
127 sri->ran->sccp_ran_ops.disconnect(sri, conn_id);
128 break;
129
130 case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
131 /* connection-less data received */
132 my_addr = &prim->u.unitdata.called_addr;
133 peer_addr = &prim->u.unitdata.calling_addr;
134 LOG_SCCP_RAN_CL(sri, peer_addr, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
135
136 if (osmo_sccp_addr_ri_cmp(&sri->local_sccp_addr, my_addr))
137 LOG_SCCP_RAN_CL(sri, NULL, LOGL_INFO,
138 "Called address is %s which is not the locally configured address\n",
139 osmo_sccp_inst_addr_name(sri->sccp, my_addr));
140
141 rc = sri->ran->sccp_ran_ops.up_l2(sri, peer_addr, false, 0, oph->msg);
142 break;
143
144 default:
145 LOG_SCCP_RAN_CL(sri, NULL, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
146 rc = -1;
147 break;
148 }
149
150 msgb_free(oph->msg);
151 return rc;
152}
153
154/* Push some padding if necessary to reach a multiple-of-eight offset to be msgb_push() an osmo_scu_prim that will then
155 * be 8-byte aligned. */
156static void msgb_pad_mod8(struct msgb *msg)
157{
158 uint8_t mod8 = (intptr_t)(msg->data) % 8;
159 if (mod8)
160 msgb_push(msg, mod8);
161}
162
163int sccp_ran_down_l2_co_initial(struct sccp_ran_inst *sri,
164 const struct osmo_sccp_addr *called_addr,
165 uint32_t conn_id, struct msgb *l2)
166{
167 struct osmo_scu_prim *prim;
168
169 l2->l2h = l2->data;
170
171 msgb_pad_mod8(l2);
172 prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));
173 prim->u.connect = (struct osmo_scu_connect_param){
174 .called_addr = *called_addr,
175 .calling_addr = sri->local_sccp_addr,
176 .sccp_class = 2,
177 //.importance = ?,
178 .conn_id = conn_id,
179 };
180 osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST, l2);
181 return osmo_sccp_user_sap_down_nofree(sri->scu, &prim->oph);
182}
183
184int sccp_ran_down_l2_co(struct sccp_ran_inst *sri, uint32_t conn_id, struct msgb *l2)
185{
186 struct osmo_scu_prim *prim;
187
188 l2->l2h = l2->data;
189
190 msgb_pad_mod8(l2);
191 prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));
192 prim->u.data.conn_id = conn_id;
193 osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, l2);
194 return osmo_sccp_user_sap_down_nofree(sri->scu, &prim->oph);
195}
196
197int sccp_ran_down_l2_cl(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *called_addr, struct msgb *l2)
198{
199 struct osmo_scu_prim *prim;
200
201 l2->l2h = l2->data;
202
203 msgb_pad_mod8(l2);
204 prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));
205 prim->u.unitdata = (struct osmo_scu_unitdata_param){
206 .called_addr = *called_addr,
207 .calling_addr = sri->local_sccp_addr,
208 };
209 osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST, l2);
210 return osmo_sccp_user_sap_down_nofree(sri->scu, &prim->oph);
211}
212
213int sccp_ran_disconnect(struct sccp_ran_inst *sri, uint32_t conn_id, uint32_t cause)
214{
215 return osmo_sccp_tx_disconn(sri->scu, conn_id, NULL, cause);
216}