Philipp Maier | 4b60d07 | 2017-04-09 12:32:51 +0200 | [diff] [blame] | 1 | /* (C) 2017 by sysmocom s.f.m.c. GmbH |
| 2 | * All Rights Reserved |
| 3 | * |
| 4 | * Author: Philipp Maier |
| 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/utils.h> |
| 22 | #include <osmocom/core/logging.h> |
| 23 | #include <osmocom/sigtran/osmo_ss7.h> |
| 24 | #include <osmocom/sigtran/sccp_sap.h> |
| 25 | #include <osmocom/sccp/sccp_types.h> |
| 26 | #include <osmocom/core/linuxlist.h> |
| 27 | #include <osmocom/gsm/gsm0808.h> |
| 28 | #include <osmocom/core/msgb.h> |
| 29 | #include <openbsc/bsc_msc_data.h> |
| 30 | #include <openbsc/debug.h> |
| 31 | #include <openbsc/osmo_bsc.h> |
| 32 | #include <openbsc/osmo_bsc_grace.h> |
| 33 | #include <openbsc/osmo_bsc_sigtran.h> |
| 34 | #include <openbsc/a_reset.h> |
| 35 | #include <openbsc/gsm_04_80.h> |
| 36 | |
| 37 | /* A pointer to a list with all involved MSCs |
| 38 | * (a copy of the pointer location submitted with osmo_bsc_sigtran_init() */ |
| 39 | static struct llist_head *msc_list; |
| 40 | |
| 41 | #define RESET_INTERVAL 1 /* sek */ |
| 42 | #define SCCP_MSG_MAXSIZE 1024 |
| 43 | #define CS7_POINTCODE_DEFAULT_OFFSET 2 |
| 44 | |
| 45 | /* Internal list with connections we currently maintain. This |
| 46 | * list is of type struct osmo_bsc_sccp_con */ |
| 47 | static LLIST_HEAD(active_connections); |
| 48 | |
| 49 | /* The SCCP stack will not assign connection IDs to us automatically, we |
| 50 | * will do this ourselves using a counter variable, that counts one up |
| 51 | * for every new connection */ |
| 52 | static uint32_t conn_id_counter; |
| 53 | |
| 54 | /* Helper function to Check if the given connection id is already assigned */ |
| 55 | static struct osmo_bsc_sccp_con *get_bsc_conn_by_conn_id(int conn_id) |
| 56 | { |
| 57 | conn_id &= 0xFFFFFF; |
| 58 | struct osmo_bsc_sccp_con *bsc_con; |
| 59 | |
| 60 | llist_for_each_entry(bsc_con, &active_connections, entry) { |
| 61 | if (bsc_con->conn_id == conn_id) |
| 62 | return bsc_con; |
| 63 | } |
| 64 | |
| 65 | return NULL; |
| 66 | } |
| 67 | |
| 68 | /* Pick a free connection id */ |
| 69 | static int pick_free_conn_id(const struct bsc_msc_data *msc) |
| 70 | { |
| 71 | int conn_id = conn_id_counter; |
| 72 | int i; |
| 73 | |
| 74 | for (i = 0; i < 0xFFFFFF; i++) { |
| 75 | conn_id++; |
| 76 | conn_id &= 0xFFFFFF; |
| 77 | if (get_bsc_conn_by_conn_id(conn_id) == false) { |
| 78 | conn_id_counter = conn_id; |
| 79 | return conn_id; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | return -1; |
| 84 | } |
| 85 | |
| 86 | /* Send reset to MSC */ |
| 87 | static void osmo_bsc_sigtran_tx_reset(const struct bsc_msc_data *msc) |
| 88 | { |
| 89 | struct osmo_ss7_instance *ss7; |
| 90 | struct msgb *msg; |
| 91 | |
| 92 | ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); |
| 93 | OSMO_ASSERT(ss7); |
| 94 | LOGP(DMSC, LOGL_NOTICE, "Sending RESET to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); |
| 95 | msg = gsm0808_create_reset(); |
| 96 | osmo_sccp_tx_unitdata_msg(msc->a.sccp_user, &msc->a.bsc_addr, |
| 97 | &msc->a.msc_addr, msg); |
| 98 | } |
| 99 | |
| 100 | /* Send reset-ack to MSC */ |
| 101 | void osmo_bsc_sigtran_tx_reset_ack(const struct bsc_msc_data *msc) |
| 102 | { |
| 103 | struct osmo_ss7_instance *ss7; |
| 104 | struct msgb *msg; |
| 105 | OSMO_ASSERT(msc); |
| 106 | |
| 107 | ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); |
| 108 | OSMO_ASSERT(ss7); |
| 109 | LOGP(DMSC, LOGL_NOTICE, "Sending RESET ACK to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); |
| 110 | msg = gsm0808_create_reset_ack(); |
| 111 | osmo_sccp_tx_unitdata_msg(msc->a.sccp_user, &msc->a.bsc_addr, |
| 112 | &msc->a.msc_addr, msg); |
| 113 | } |
| 114 | |
| 115 | /* Find an MSC by its sigtran point code */ |
| 116 | static struct bsc_msc_data *get_msc_by_addr(const struct osmo_sccp_addr *msc_addr) |
| 117 | { |
| 118 | struct osmo_ss7_instance *ss7; |
| 119 | struct bsc_msc_data *msc; |
| 120 | llist_for_each_entry(msc, msc_list, entry) { |
| 121 | if (memcmp(msc_addr, &msc->a.msc_addr, sizeof(*msc_addr)) == 0) |
| 122 | return msc; |
| 123 | } |
| 124 | |
| 125 | ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); |
| 126 | OSMO_ASSERT(ss7); |
| 127 | LOGP(DMSC, LOGL_ERROR, "Unable to find MSC data under address: %s\n", osmo_sccp_addr_name(ss7, msc_addr)); |
| 128 | return NULL; |
| 129 | } |
| 130 | |
| 131 | /* Send data to MSC, use the connection id which MSC it is */ |
| 132 | static int handle_data_from_msc(int conn_id, struct msgb *msg) |
| 133 | { |
| 134 | struct osmo_bsc_sccp_con *bsc_con = get_bsc_conn_by_conn_id(conn_id); |
| 135 | int rc = -EINVAL; |
| 136 | |
| 137 | if (bsc_con) { |
| 138 | msg->l3h = msgb_l2(msg); |
| 139 | rc = bsc_handle_dt(bsc_con, msg, msgb_l2len(msg)); |
| 140 | } else |
| 141 | LOGP(DMSC, LOGL_NOTICE, "incoming data from unknown connection id: %i\n", conn_id); |
| 142 | |
| 143 | return rc; |
| 144 | } |
| 145 | |
| 146 | /* Sent unitdata to MSC, use the point code to determine which MSC it is */ |
| 147 | static int handle_unitdata_from_msc(const struct osmo_sccp_addr *msc_addr, struct msgb *msg, |
| 148 | const struct osmo_sccp_user *scu) |
| 149 | { |
| 150 | struct osmo_ss7_instance *ss7; |
| 151 | struct bsc_msc_data *msc = get_msc_by_addr(msc_addr); |
| 152 | int rc = -EINVAL; |
| 153 | |
| 154 | if (msc) { |
| 155 | msg->l3h = msgb_l2(msg); |
| 156 | rc = bsc_handle_udt(msc, msg, msgb_l2len(msg)); |
| 157 | } else { |
| 158 | ss7 = osmo_sccp_get_ss7(osmo_sccp_get_sccp(scu)); |
| 159 | OSMO_ASSERT(ss7); |
| 160 | LOGP(DMSC, LOGL_NOTICE, "incoming unitdata data from unknown remote address: %s\n", |
| 161 | osmo_sccp_addr_name(ss7, msc_addr)); |
| 162 | } |
| 163 | return rc; |
| 164 | } |
| 165 | |
| 166 | /* Callback function, called by the SSCP stack when data arrives */ |
| 167 | static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) |
| 168 | { |
| 169 | struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph; |
| 170 | struct osmo_sccp_user *scu = _scu; |
| 171 | struct osmo_bsc_sccp_con *bsc_con; |
| 172 | int rc = 0; |
| 173 | |
| 174 | switch (OSMO_PRIM_HDR(&scu_prim->oph)) { |
| 175 | case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): |
| 176 | /* Handle inbound UNITDATA */ |
| 177 | DEBUGP(DMSC, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); |
| 178 | rc = handle_unitdata_from_msc(&scu_prim->u.unitdata.calling_addr, oph->msg, scu); |
| 179 | break; |
| 180 | |
| 181 | case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION): |
| 182 | /* Handle (Reject) inbound connections */ |
| 183 | DEBUGP(DMSC, "N-CONNECT.ind(X->%u)\n", scu_prim->u.connect.conn_id); |
| 184 | LOGP(DMSC, LOGL_DEBUG, "Rejecting inbound SCCP connection...\n"); |
| 185 | rc = osmo_sccp_tx_disconn(scu, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, 0); |
| 186 | break; |
| 187 | |
| 188 | case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM): |
| 189 | /* Handle outbound connection confirmation */ |
| 190 | if (msgb_l2len(oph->msg) > 0) { |
| 191 | DEBUGP(DMSC, "N-CONNECT.cnf(%u, %s)\n", scu_prim->u.connect.conn_id, |
| 192 | osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); |
| 193 | rc = handle_data_from_msc(scu_prim->u.connect.conn_id, oph->msg); |
| 194 | } else |
| 195 | DEBUGP(DRANAP, "N-CONNECT.cnf(%u)\n", scu_prim->u.connect.conn_id); |
| 196 | break; |
| 197 | |
| 198 | case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION): |
| 199 | /* Handle incoming connection oriented data */ |
| 200 | DEBUGP(DMSC, "N-DATA.ind(%u, %s)\n", scu_prim->u.data.conn_id, |
| 201 | osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); |
| 202 | |
| 203 | /* Incoming data is a sign of a vital connection */ |
| 204 | bsc_con = get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id); |
| 205 | if (bsc_con) |
| 206 | a_reset_conn_success(bsc_con->msc->a.reset); |
| 207 | |
| 208 | rc = handle_data_from_msc(scu_prim->u.data.conn_id, oph->msg); |
| 209 | break; |
| 210 | |
| 211 | case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION): |
| 212 | /* indication of disconnect */ |
| 213 | if (msgb_l2len(oph->msg) > 0) { |
| 214 | DEBUGP(DMSC, "N-DISCONNECT.ind(%u, %s, cause=%i)\n", scu_prim->u.disconnect.conn_id, |
| 215 | osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)), scu_prim->u.disconnect.cause); |
| 216 | handle_data_from_msc(scu_prim->u.disconnect.conn_id, oph->msg); |
| 217 | } else |
| 218 | DEBUGP(DRANAP, "N-DISCONNECT.ind(%u, cause=%i)\n", scu_prim->u.disconnect.conn_id, |
| 219 | scu_prim->u.disconnect.cause); |
| 220 | |
| 221 | bsc_con = get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id); |
| 222 | if (bsc_con) { |
| 223 | /* We might have a connectivity problem. Maybe we need to go |
| 224 | * through the reset procedure again? */ |
| 225 | if (scu_prim->u.disconnect.cause == 0) |
| 226 | a_reset_conn_fail(bsc_con->msc->a.reset); |
| 227 | |
| 228 | rc = osmo_bsc_sigtran_del_conn(bsc_con); |
| 229 | } |
| 230 | break; |
| 231 | |
| 232 | default: |
| 233 | LOGP(DMSC, LOGL_ERROR, "Unhandled SIGTRAN primitive: %u:%u\n", oph->primitive, oph->operation); |
| 234 | break; |
| 235 | } |
| 236 | |
| 237 | msgb_free(oph->msg); |
| 238 | return rc; |
| 239 | } |
| 240 | |
| 241 | /* Allocate resources to make a new connection oriented sigtran connection |
| 242 | * (not the connection ittself!) */ |
| 243 | enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, struct bsc_msc_data *msc) |
| 244 | { |
| 245 | struct osmo_ss7_instance *ss7; |
| 246 | struct osmo_bsc_sccp_con *bsc_con; |
| 247 | int conn_id; |
| 248 | |
| 249 | OSMO_ASSERT(conn); |
| 250 | OSMO_ASSERT(msc); |
| 251 | |
| 252 | ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); |
| 253 | OSMO_ASSERT(ss7); |
| 254 | LOGP(DMSC, LOGL_NOTICE, "Initializing resources for new SIGTRAN connection to MSC: %s...\n", |
| 255 | osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); |
| 256 | |
| 257 | if (a_reset_conn_ready(msc->a.reset) == false) { |
| 258 | LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n"); |
| 259 | return BSC_CON_REJECT_NO_LINK; |
| 260 | } |
| 261 | |
| 262 | if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) { |
| 263 | LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n"); |
| 264 | return BSC_CON_REJECT_RF_GRACE; |
| 265 | } |
| 266 | |
| 267 | bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con); |
| 268 | if (!bsc_con) { |
| 269 | LOGP(DMSC, LOGL_ERROR, "Failed to allocate new SIGTRAN connection.\n"); |
| 270 | return BSC_CON_NO_MEM; |
| 271 | } |
| 272 | |
| 273 | bsc_con->msc = msc; |
| 274 | bsc_con->conn = conn; |
| 275 | llist_add_tail(&bsc_con->entry, &active_connections); |
| 276 | conn->sccp_con = bsc_con; |
| 277 | |
| 278 | /* Pick a free connection id */ |
| 279 | conn_id = pick_free_conn_id(msc); |
| 280 | if (conn_id < 0) |
| 281 | return BSC_CON_REJECT_NO_LINK; |
| 282 | bsc_con->conn_id = conn_id; |
| 283 | |
| 284 | LOGP(DMSC, LOGL_NOTICE, "Allocated new connection id: %i\n", conn_id); |
| 285 | |
| 286 | return BSC_CON_SUCCESS; |
| 287 | } |
| 288 | |
| 289 | /* Open a new connection oriented sigtran connection */ |
| 290 | int osmo_bsc_sigtran_open_conn(const struct osmo_bsc_sccp_con *conn, struct msgb *msg) |
| 291 | { |
| 292 | struct osmo_ss7_instance *ss7; |
| 293 | struct bsc_msc_data *msc; |
| 294 | int conn_id; |
| 295 | int rc; |
| 296 | |
| 297 | OSMO_ASSERT(conn); |
| 298 | OSMO_ASSERT(msg); |
| 299 | OSMO_ASSERT(conn->msc); |
| 300 | |
| 301 | msc = conn->msc; |
| 302 | |
| 303 | if (a_reset_conn_ready(msc->a.reset) == false) { |
| 304 | LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n"); |
| 305 | return -EINVAL; |
| 306 | } |
| 307 | |
| 308 | conn_id = conn->conn_id; |
| 309 | ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); |
| 310 | OSMO_ASSERT(ss7); |
| 311 | LOGP(DMSC, LOGL_NOTICE, "Opening new SIGTRAN connection (id=%i) to MSC: %s\n", conn_id, |
| 312 | osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); |
| 313 | |
| 314 | rc = osmo_sccp_tx_conn_req_msg(msc->a.sccp_user, conn_id, &msc->a.bsc_addr, |
| 315 | &msc->a.msc_addr, msg); |
| 316 | |
| 317 | return rc; |
| 318 | } |
| 319 | |
| 320 | /* Send data to MSC */ |
| 321 | int osmo_bsc_sigtran_send(const struct osmo_bsc_sccp_con *conn, struct msgb *msg) |
| 322 | { |
| 323 | struct osmo_ss7_instance *ss7; |
| 324 | int conn_id; |
| 325 | int rc; |
| 326 | struct bsc_msc_data *msc; |
| 327 | |
| 328 | OSMO_ASSERT(conn); |
| 329 | OSMO_ASSERT(msg); |
| 330 | OSMO_ASSERT(conn->msc); |
| 331 | |
| 332 | msc = conn->msc; |
| 333 | |
| 334 | if (a_reset_conn_ready(msc->a.reset) == false) { |
| 335 | LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n"); |
| 336 | return -EINVAL; |
| 337 | } |
| 338 | |
| 339 | conn_id = conn->conn_id; |
| 340 | |
| 341 | ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); |
| 342 | OSMO_ASSERT(ss7); |
| 343 | LOGP(DMSC, LOGL_DEBUG, "Sending connection (id=%i) oriented data to MSC: %si\n", |
| 344 | conn_id, osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); |
| 345 | |
| 346 | rc = osmo_sccp_tx_data_msg(msc->a.sccp_user, conn_id, msg); |
| 347 | |
| 348 | return rc; |
| 349 | } |
| 350 | |
| 351 | /* Delete a connection from the list with open connections |
| 352 | * (called by osmo_bsc_api.c on failing open connections and |
| 353 | * locally, when a connection is closed by the MSC */ |
| 354 | int osmo_bsc_sigtran_del_conn(struct osmo_bsc_sccp_con *conn) |
| 355 | { |
| 356 | if (!conn) |
| 357 | return 0; |
| 358 | |
| 359 | if (conn->conn) { |
| 360 | LOGP(DMSC, LOGL_ERROR, |
| 361 | "sccp connection (id=%i) not cleared (gsm subscriber connection still active) -- forcefully clearing it now!\n", |
| 362 | conn->conn_id); |
| 363 | bsc_subscr_con_free(conn->conn); |
| 364 | conn->conn = NULL; |
| 365 | |
| 366 | /* This bahaviour might be caused by a bad connection. Maybe we |
| 367 | * will have to go through the reset procedure again */ |
| 368 | a_reset_conn_fail(conn->msc->a.reset); |
| 369 | } |
| 370 | |
| 371 | llist_del(&conn->entry); |
| 372 | talloc_free(conn); |
| 373 | |
| 374 | return 0; |
| 375 | } |
| 376 | |
| 377 | /* Send an USSD notification in case we loose the connection to the MSC */ |
| 378 | static void bsc_notify_msc_lost(const struct osmo_bsc_sccp_con *conn) |
| 379 | { |
| 380 | struct gsm_subscriber_connection *subscr_conn; |
| 381 | |
| 382 | /* Check if sccp conn is still present */ |
| 383 | if (!conn) |
| 384 | return; |
| 385 | subscr_conn = conn->conn; |
| 386 | |
| 387 | /* send USSD notification if string configured and conn->data is set */ |
| 388 | if (!subscr_conn) |
| 389 | return; |
| 390 | |
| 391 | /* check for config string */ |
| 392 | if (!conn->msc->ussd_msc_lost_txt) |
| 393 | return; |
| 394 | if (conn->msc->ussd_msc_lost_txt[0] == '\0') |
| 395 | return; |
| 396 | |
| 397 | /* send USSD notification */ |
| 398 | bsc_send_ussd_notify(subscr_conn, 1, subscr_conn->sccp_con->msc->ussd_msc_lost_txt); |
| 399 | bsc_send_ussd_release_complete(subscr_conn); |
| 400 | } |
| 401 | |
| 402 | /* Close all open sigtran connections and channels */ |
| 403 | void osmo_bsc_sigtran_reset(const struct bsc_msc_data *msc) |
| 404 | { |
| 405 | struct osmo_bsc_sccp_con *conn; |
| 406 | struct osmo_bsc_sccp_con *conn_temp; |
| 407 | OSMO_ASSERT(msc); |
| 408 | |
| 409 | /* Close all open connections */ |
| 410 | llist_for_each_entry_safe(conn, conn_temp, &active_connections, entry) { |
| 411 | |
| 412 | /* We only may close connections which actually belong to this |
| 413 | * MSC. All other open connections are left untouched */ |
| 414 | if (conn->msc == msc) { |
| 415 | /* Notify active connection users via USSD that the MSC is down */ |
| 416 | bsc_notify_msc_lost(conn); |
| 417 | |
| 418 | /* Take down all occopied RF channels */ |
| 419 | if (conn->conn) |
| 420 | gsm0808_clear(conn->conn); |
| 421 | |
| 422 | /* Disconnect all Sigtran connections */ |
| 423 | osmo_sccp_tx_disconn(msc->a.sccp_user, conn->conn_id, &msc->a.bsc_addr, 0); |
| 424 | |
| 425 | /* Delete subscriber connection */ |
| 426 | osmo_bsc_sigtran_del_conn(conn); |
| 427 | } |
| 428 | } |
| 429 | } |
| 430 | |
| 431 | /* Callback function: Close all open connections */ |
| 432 | static void osmo_bsc_sigtran_reset_cb(const void *priv) |
| 433 | { |
| 434 | struct bsc_msc_data *msc = (struct bsc_msc_data*) priv; |
| 435 | |
| 436 | /* Shut down all ongoing traffic */ |
| 437 | osmo_bsc_sigtran_reset(msc); |
| 438 | |
| 439 | /* Send reset to MSC */ |
| 440 | osmo_bsc_sigtran_tx_reset(msc); |
| 441 | } |
| 442 | |
| 443 | /* Default point-code to be used as local address (BSC) */ |
| 444 | #define BSC_DEFAULT_PC "0.23.3" |
| 445 | |
| 446 | /* Default point-code to be used as remote address (MSC) */ |
| 447 | #define MSC_DEFAULT_PC "0.23.1" |
| 448 | |
| 449 | /* Initalize osmo sigtran backhaul */ |
| 450 | int osmo_bsc_sigtran_init(struct llist_head *mscs) |
| 451 | { |
| 452 | bool free_attempt_used = false; |
| 453 | bool fail_on_next_invalid_cfg = false; |
| 454 | |
| 455 | struct bsc_msc_data *msc; |
| 456 | char msc_name[32]; |
| 457 | uint32_t default_pc; |
| 458 | |
| 459 | OSMO_ASSERT(mscs); |
| 460 | msc_list = mscs; |
| 461 | |
| 462 | llist_for_each_entry(msc, msc_list, entry) { |
| 463 | snprintf(msc_name, sizeof(msc_name), "msc-%u", msc->nr); |
| 464 | LOGP(DMSC, LOGL_NOTICE, "Initializing SCCP connection to MSC %s\n", msc_name); |
| 465 | |
| 466 | /* Check if the VTY could determine a valid CS7 instance, |
| 467 | * use safe default in case none is set */ |
| 468 | if (msc->a.cs7_instance_valid == false) { |
| 469 | msc->a.cs7_instance = 0; |
| 470 | if (fail_on_next_invalid_cfg) |
| 471 | goto fail_auto_cofiguration; |
| 472 | free_attempt_used = true; |
| 473 | } |
| 474 | LOGP(DMSC, LOGL_NOTICE, "CS7 Instance identifier, A-Interface: %u\n", msc->a.cs7_instance); |
| 475 | |
| 476 | /* Pre-Check if there is an ss7 instance present */ |
| 477 | if (osmo_ss7_instance_find(msc->a.cs7_instance) == NULL) { |
| 478 | if (fail_on_next_invalid_cfg) |
| 479 | goto fail_auto_cofiguration; |
| 480 | free_attempt_used = true; |
| 481 | } |
| 482 | |
| 483 | /* SS7 Protocol stack */ |
| 484 | default_pc = osmo_ss7_pointcode_parse(NULL, BSC_DEFAULT_PC); |
| 485 | msc->a.sccp = |
| 486 | osmo_sccp_simple_client_on_ss7_id(msc, msc->a.cs7_instance, msc_name, default_pc, |
| 487 | OSMO_SS7_ASP_PROT_M3UA, 0, NULL, 0, NULL); |
| 488 | if (!msc->a.sccp) |
| 489 | return -EINVAL; |
| 490 | |
| 491 | /* Check if the sccp-address fullfills minimum requirements (SSN+PC is present, |
| 492 | * automatically recover addresses if the addresses are not set up properly) */ |
| 493 | if (!osmo_sccp_check_addr(&msc->a.bsc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) { |
| 494 | if (fail_on_next_invalid_cfg) |
| 495 | goto fail_auto_cofiguration; |
| 496 | free_attempt_used = true; |
| 497 | |
| 498 | LOGP(DMSC, LOGL_NOTICE, |
| 499 | "A-interface: invalid or missing local (BSC) SCCP address (a.bsc_addr=%s)\n", |
| 500 | osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.bsc_addr)); |
| 501 | osmo_sccp_local_addr_by_instance(&msc->a.bsc_addr, msc->a.sccp, SCCP_SSN_BSSAP); |
| 502 | LOGP(DMSC, LOGL_NOTICE, |
| 503 | "A-interface: using automatically generated local (BSC) SCCP address (a.bsc_addr=%s)\n", |
| 504 | osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.bsc_addr)); |
| 505 | } else { |
| 506 | LOGP(DMSC, LOGL_NOTICE, |
| 507 | "A-interface: using local (BSC) automatically SCCP address (a.msc_addr=%s)\n", |
| 508 | osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.bsc_addr)); |
| 509 | } |
| 510 | |
| 511 | if (!osmo_sccp_check_addr(&msc->a.msc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) { |
| 512 | if (fail_on_next_invalid_cfg) |
| 513 | goto fail_auto_cofiguration; |
| 514 | free_attempt_used = true; |
| 515 | |
| 516 | LOGP(DMSC, LOGL_NOTICE, |
| 517 | "A-interface: invalid or missing remote (MSC) SCCP address for the MSC (a.msc_addr=%s)\n", |
| 518 | osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.msc_addr)); |
| 519 | osmo_sccp_local_addr_by_instance(&msc->a.msc_addr, msc->a.sccp, SCCP_SSN_BSSAP); |
| 520 | msc->a.msc_addr.pc = osmo_ss7_pointcode_parse(NULL, MSC_DEFAULT_PC); |
| 521 | LOGP(DMSC, LOGL_NOTICE, |
| 522 | "A-interface: using automatically generated remote (MSC) SCCP address (a.msc_addr=%s)\n", |
| 523 | osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.msc_addr)); |
| 524 | free_attempt_used = true; |
| 525 | } else { |
| 526 | LOGP(DMSC, LOGL_NOTICE, |
| 527 | "A-interface: using remote (MSC) automatically SCCP address (a.msc_addr=%s)\n", |
| 528 | osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.msc_addr)); |
| 529 | } |
| 530 | |
| 531 | /* Bind SCCP user */ |
| 532 | msc->a.sccp_user = osmo_sccp_user_bind(msc->a.sccp, msc_name, sccp_sap_up, msc->a.bsc_addr.ssn); |
| 533 | if (!msc->a.sccp_user) |
| 534 | return -EINVAL; |
| 535 | |
| 536 | /* Start MSC-Reset procedure */ |
| 537 | msc->a.reset = a_reset_alloc(msc, msc_name, osmo_bsc_sigtran_reset_cb, msc); |
| 538 | if (!msc->a.reset) |
| 539 | return -EINVAL; |
| 540 | |
| 541 | /* If we have detected that the SS7 configuration of the MSC we have just initalized |
| 542 | * was incomplete or completely missing, we can not tolerate another incomplete |
| 543 | * configuration. The reson for this is that we do only specify exactly one default |
| 544 | * pointcode pair. We also specify localhost as default IP-Address. If we have wanted |
| 545 | * to support multiple MSCs with automatic configuration we would be forced to invent |
| 546 | * a complex ruleset how to allocate the pointcodes and respective IP-Addresses. |
| 547 | * Furthermore, the situation where a single BSC is connected to multiple MSCs |
| 548 | * is a very rare situation anyway. In this case we expect the user to experienced |
| 549 | * enough to create a valid SS7/CS7 VTY configuration that does not lack any |
| 550 | * components */ |
| 551 | if (free_attempt_used) |
| 552 | fail_on_next_invalid_cfg = true; |
| 553 | } |
| 554 | |
| 555 | return 0; |
| 556 | |
| 557 | fail_auto_cofiguration: |
| 558 | LOGP(DMSC, LOGL_ERROR, |
| 559 | "A-interface: More than one invalid/inclomplete configuration detected, unable to revover - check config file!\n"); |
| 560 | return -EINVAL; |
| 561 | } |