blob: 1abb473942dd06cef9022186e1f1af43a0549fa9 [file] [log] [blame]
Jonathan Santos03fd8d02011-05-25 13:54:02 -04001/* Interaction with the SCCP subsystem */
2/*
3 * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
4 * (C) 2009-2010 by On-Waves
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include <openbsc/gsm_data.h>
23#include <openbsc/osmo_bsc.h>
24#include <openbsc/osmo_bsc_grace.h>
25#include <openbsc/osmo_msc_data.h>
26#include <openbsc/debug.h>
27#include <openbsc/ipaccess.h>
28#include <openbsc/signal.h>
29
30#include <osmocore/gsm0808.h>
31#include <osmocore/talloc.h>
32#include <osmocore/protocol/gsm_08_08.h>
33
34#include <osmocom/sccp/sccp.h>
35
36/* SCCP helper */
37#define SCCP_IT_TIMER 60
38
39static LLIST_HEAD(active_connections);
40
41static void free_queued(struct osmo_bsc_sccp_con *conn)
42{
43 struct msgb *msg;
44
45 while (!llist_empty(&conn->sccp_queue)) {
46 /* this is not allowed to fail */
47 msg = msgb_dequeue(&conn->sccp_queue);
48 msgb_free(msg);
49 }
50
51 conn->sccp_queue_size = 0;
52}
53
54static void send_queued(struct osmo_bsc_sccp_con *conn)
55{
56 struct msgb *msg;
57
58 while (!llist_empty(&conn->sccp_queue)) {
59 /* this is not allowed to fail */
60 msg = msgb_dequeue(&conn->sccp_queue);
61 sccp_connection_write(conn->sccp, msg);
62 msgb_free(msg);
63 conn->sccp_queue_size -= 1;
64 }
65}
66
67static void msc_outgoing_sccp_data(struct sccp_connection *conn,
68 struct msgb *msg, unsigned int len)
69{
70 struct osmo_bsc_sccp_con *bsc_con =
71 (struct osmo_bsc_sccp_con *) conn->data_ctx;
72
73 bsc_handle_dt1(bsc_con, msg, len);
74}
75
76static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state)
77{
78 struct osmo_bsc_sccp_con *con_data;
79
80 if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
81 con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx;
82 if(con_data->conn) {
83 LOGP(DMSC, LOGL_ERROR,
84 "ERROR: The lchan is still associated\n.");
85 gsm0808_clear(con_data->conn);
86 subscr_con_free(con_data->conn);
87 con_data->conn = NULL;
88 }
89
90 con_data->sccp = NULL;
91 free_queued(con_data);
92 sccp_connection_free(conn);
93 bsc_delete_connection(con_data);
94 } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) {
95 LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn);
96 con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx;
97
98 bsc_del_timer(&con_data->sccp_cc_timeout);
99 bsc_schedule_timer(&con_data->sccp_it_timeout, SCCP_IT_TIMER, 0);
100
101 send_queued(con_data);
102 }
103}
104
105static void bsc_sccp_force_free(struct osmo_bsc_sccp_con *data)
106{
107 if (data->conn) {
108 gsm0808_clear(data->conn);
109 subscr_con_free(data->conn);
110 data->conn = NULL;
111 }
112
113 free_queued(data);
114 sccp_connection_force_free(data->sccp);
115 data->sccp = NULL;
116 bsc_delete_connection(data);
117}
118
119static void sccp_it_timeout(void *_data)
120{
121 struct osmo_bsc_sccp_con *data =
122 (struct osmo_bsc_sccp_con *) _data;
123
124 sccp_connection_send_it(data->sccp);
125 bsc_schedule_timer(&data->sccp_it_timeout, SCCP_IT_TIMER, 0);
126}
127
128static void sccp_cc_timeout(void *_data)
129{
130 struct osmo_bsc_sccp_con *data =
131 (struct osmo_bsc_sccp_con *) _data;
132
133 if (data->sccp->connection_state >= SCCP_CONNECTION_STATE_ESTABLISHED)
134 return;
135
136 LOGP(DMSC, LOGL_ERROR, "The connection was never established.\n");
137 bsc_sccp_force_free(data);
138}
139
140static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg, void *data)
141{
142 struct gsm_network *net = (struct gsm_network *) data;
143 msc_queue_write(net->msc_data->msc_con, msg, IPAC_PROTO_SCCP);
144}
145
146static int msc_sccp_accept(struct sccp_connection *connection, void *data)
147{
148 LOGP(DMSC, LOGL_DEBUG, "Rejecting incoming SCCP connection.\n");
149 return -1;
150}
151
152static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data)
153{
154 struct gsm_network *net = (struct gsm_network *) data;
155 return bsc_handle_udt(net, net->msc_data->msc_con, msgb, length);
156}
157
158int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
159{
160 struct sccp_connection *sccp = conn->sccp;
161
162 if (sccp->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
163 LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp);
164 msgb_free(msg);
165 } else if (sccp->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED
166 && conn->sccp_queue_size == 0) {
167 sccp_connection_write(sccp, msg);
168 msgb_free(msg);
169 } else if (conn->sccp_queue_size > 10) {
170 LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp);
171 msgb_free(msg);
172 } else {
173 LOGP(DMSC, LOGL_DEBUG, "Queueing packet on %p. Queue size: %d\n", sccp, conn->sccp_queue_size);
174 conn->sccp_queue_size += 1;
175 msgb_enqueue(&conn->sccp_queue, msg);
176 }
177
178 return 0;
179}
180
181int bsc_create_new_connection(struct gsm_subscriber_connection *conn)
182{
183 struct gsm_network *net;
184 struct osmo_bsc_sccp_con *bsc_con;
185 struct sccp_connection *sccp;
186
187 net = conn->bts->network;
188 if (!net->msc_data->msc_con->is_authenticated) {
189 LOGP(DMSC, LOGL_ERROR, "Not connected to a MSC. Not forwarding data.\n");
190 return -1;
191 }
192
193 if (!bsc_grace_allow_new_connection(net)) {
194 LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n");
195 return -1;
196 }
197
198 sccp = sccp_connection_socket();
199 if (!sccp) {
200 LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n");
201 return -ENOMEM;
202 }
203
204 bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con);
205 if (!bsc_con) {
206 LOGP(DMSC, LOGL_ERROR, "Failed to allocate.\n");
207 sccp_connection_free(sccp);
208 return -1;
209 }
210
211 /* callbacks */
212 sccp->state_cb = msc_outgoing_sccp_state;
213 sccp->data_cb = msc_outgoing_sccp_data;
214 sccp->data_ctx = bsc_con;
215
216 /* prepare the timers */
217 bsc_con->sccp_it_timeout.cb = sccp_it_timeout;
218 bsc_con->sccp_it_timeout.data = bsc_con;
219 bsc_con->sccp_cc_timeout.cb = sccp_cc_timeout;
220 bsc_con->sccp_cc_timeout.data = bsc_con;
221
222 INIT_LLIST_HEAD(&bsc_con->sccp_queue);
223
224 bsc_con->sccp = sccp;
225 bsc_con->msc_con = net->msc_data->msc_con;
226 bsc_con->conn = conn;
227 llist_add(&bsc_con->entry, &active_connections);
228 conn->sccp_con = bsc_con;
229 return 0;
230}
231
232int bsc_open_connection(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
233{
234 bsc_schedule_timer(&conn->sccp_cc_timeout, 10, 0);
235 sccp_connection_connect(conn->sccp, &sccp_ssn_bssap, msg);
236 msgb_free(msg);
237 return 0;
238}
239
240int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp)
241{
242 if (!sccp)
243 return 0;
244
245 if (sccp->conn)
246 LOGP(DMSC, LOGL_ERROR, "Should have been cleared.\n");
247
248 llist_del(&sccp->entry);
249 bsc_del_timer(&sccp->sccp_it_timeout);
250 bsc_del_timer(&sccp->sccp_cc_timeout);
251 talloc_free(sccp);
252 return 0;
253}
254
255static void bsc_close_connections(struct bsc_msc_connection *msc_con)
256{
257 struct osmo_bsc_sccp_con *con, *tmp;
258
259 llist_for_each_entry_safe(con, tmp, &active_connections, entry)
260 bsc_sccp_force_free(con);
261}
262
263static int handle_msc_signal(unsigned int subsys, unsigned int signal,
264 void *handler_data, void *signal_data)
265{
266 struct osmo_msc_data *data;
267
268 if (subsys != SS_MSC)
269 return 0;
270
271 data = (struct osmo_msc_data *) signal_data;
272 if (signal == S_MSC_LOST)
273 bsc_close_connections(data->msc_con);
274
275 return 0;
276}
277
278int osmo_bsc_sccp_init(struct gsm_network *gsmnet)
279{
280 sccp_set_log_area(DSCCP);
281 sccp_system_init(msc_sccp_write_ipa, gsmnet);
282 sccp_connection_set_incoming(&sccp_ssn_bssap, msc_sccp_accept, NULL);
283 sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, gsmnet);
284
285 register_signal_handler(SS_MSC, handle_msc_signal, gsmnet);
286
287 return 0;
288}