/* SMLC Lb connection implementation */ /* * (C) 2020 by sysmocom s.f.m.c. * All Rights Reserved * * Author: Neels Hofmeyr * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include static int lb_conn_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line) { struct lb_conn *lb_conn = e->use_count->talloc_object; int32_t total; int level; if (!e->use) return -EINVAL; total = osmo_use_count_total(&lb_conn->use_count); if (total == 0 || (total == 1 && old_use_count == 0 && e->count == 1)) level = LOGL_INFO; else level = LOGL_DEBUG; LOG_LB_CONN_SL(lb_conn, DREF, level, file, line, "%s %s: now used by %s\n", (e->count - old_use_count) > 0? "+" : "-", e->use, osmo_use_count_to_str_c(OTC_SELECT, &lb_conn->use_count)); if (e->count < 0) return -ERANGE; if (total == 0) lb_conn_close(lb_conn); return 0; } static struct lb_conn *lb_conn_alloc(struct lb_peer *lb_peer, uint32_t sccp_conn_id, const char *use_token) { struct lb_conn *lb_conn; lb_conn = talloc(lb_peer, struct lb_conn); OSMO_ASSERT(lb_conn); *lb_conn = (struct lb_conn){ .lb_peer = lb_peer, .sccp_conn_id = sccp_conn_id, .use_count = { .talloc_object = lb_conn, .use_cb = lb_conn_use_cb, }, }; llist_add(&lb_conn->entry, &lb_peer->sli->lb_conns); lb_conn_get(lb_conn, use_token); return lb_conn; } struct lb_conn *lb_conn_create_incoming(struct lb_peer *lb_peer, uint32_t sccp_conn_id, const char *use_token) { LOG_LB_PEER(lb_peer, LOGL_DEBUG, "Incoming lb_conn id: %u\n", sccp_conn_id); return lb_conn_alloc(lb_peer, sccp_conn_id, use_token); } struct lb_conn *lb_conn_create_outgoing(struct lb_peer *lb_peer, const char *use_token) { int new_conn_id = sccp_lb_inst_next_conn_id(); if (new_conn_id < 0) return NULL; LOG_LB_PEER(lb_peer, LOGL_DEBUG, "Outgoing lb_conn id: %u\n", new_conn_id); return lb_conn_alloc(lb_peer, new_conn_id, use_token); } struct lb_conn *lb_conn_find_by_smlc_subscr(struct smlc_subscr *smlc_subscr, const char *use_token) { struct lb_conn *lb_conn; llist_for_each_entry(lb_conn, &g_smlc->lb->lb_conns, entry) { if (lb_conn->smlc_subscr == smlc_subscr) { lb_conn_get(lb_conn, use_token); return lb_conn; } } return NULL; } int lb_conn_down_l2_co(struct lb_conn *lb_conn, struct msgb *l3, bool initial) { struct lb_peer_ev_ctx co = { .conn_id = lb_conn->sccp_conn_id, .lb_conn = lb_conn, .msg = l3, }; if (!lb_conn->lb_peer) return -EIO; return osmo_fsm_inst_dispatch(lb_conn->lb_peer->fi, initial ? LB_PEER_EV_MSG_DOWN_CO_INITIAL : LB_PEER_EV_MSG_DOWN_CO, &co); } int lb_conn_rx(struct lb_conn *lb_conn, struct msgb *msg, bool initial) { struct bssap_le_pdu bssap_le; struct osmo_bssap_le_err *err; if (osmo_bssap_le_dec(&bssap_le, &err, msg, msg)) { LOG_LB_CONN(lb_conn, LOGL_ERROR, "Rx BSSAP-LE with error: %s\n", err->logmsg); return -EINVAL; } return smlc_loc_req_rx_bssap_le(lb_conn, &bssap_le); } int lb_conn_send_bssmap_le(struct lb_conn *lb_conn, const struct bssmap_le_pdu *bssmap_le) { struct msgb *msg; int rc; struct bssap_le_pdu bssap_le = { .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE, .bssmap_le = *bssmap_le, }; msg = osmo_bssap_le_enc(&bssap_le); if (!msg) { LOG_LB_CONN(lb_conn, LOGL_ERROR, "Unable to encode %s\n", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le)); return -EINVAL; } rc = lb_conn_down_l2_co(lb_conn, msg, false); msgb_free(msg); if (rc) LOG_LB_CONN(lb_conn, LOGL_ERROR, "Unable to send %s\n", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le)); return rc; } /* Regularly close the lb_conn */ void lb_conn_close(struct lb_conn *lb_conn) { if (!lb_conn) return; if (lb_conn->closing) return; lb_conn->closing = true; LOG_LB_PEER(lb_conn->lb_peer, LOGL_DEBUG, "Closing lb_conn\n"); if (lb_conn->lb_peer) { /* Todo: pass a useful SCCP cause? */ sccp_lb_disconnect(lb_conn->lb_peer->sli, lb_conn->sccp_conn_id, 0); lb_conn->lb_peer = NULL; } if (lb_conn->smlc_loc_req) osmo_fsm_inst_term(lb_conn->smlc_loc_req->fi, OSMO_FSM_TERM_REGULAR, NULL); if (lb_conn->smlc_subscr) smlc_subscr_put(lb_conn->smlc_subscr, SMLC_SUBSCR_USE_LB_CONN); llist_del(&lb_conn->entry); talloc_free(lb_conn); } /* Same as lb_conn_close() but without sending any SCCP messages (e.g. after RESET) */ void lb_conn_discard(struct lb_conn *lb_conn) { if (!lb_conn) return; /* Make sure to drop dead and don't dispatch things like DISCONNECT requests on SCCP. */ lb_conn->lb_peer = NULL; lb_conn_close(lb_conn); }