blob: 7e79de02fb3ddb2b4d611af2b25eeeeafe43fca7 [file] [log] [blame]
Neels Hofmeyr72992152020-09-19 02:36:08 +02001/* SMLC Lb connection implementation */
2
3/*
4 * (C) 2020 by sysmocom s.m.f.c. <info@sysmocom.de>
5 * All Rights Reserved
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
24#include <errno.h>
25
26#include <osmocom/core/logging.h>
27#include <osmocom/core/fsm.h>
28#include <osmocom/core/signal.h>
29#include <osmocom/gsm/bssmap_le.h>
30
31#include <osmocom/smlc/debug.h>
32#include <osmocom/smlc/smlc_data.h>
33#include <osmocom/smlc/sccp_lb_inst.h>
34#include <osmocom/smlc/lb_peer.h>
35#include <osmocom/smlc/lb_conn.h>
36#include <osmocom/smlc/smlc_loc_req.h>
37
38static int lb_conn_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
39{
40 struct lb_conn *lb_conn = e->use_count->talloc_object;
41 int32_t total;
42 int level;
43
44 if (!e->use)
45 return -EINVAL;
46
47 total = osmo_use_count_total(&lb_conn->use_count);
48
49 if (total == 0
50 || (total == 1 && old_use_count == 0 && e->count == 1))
51 level = LOGL_INFO;
52 else
53 level = LOGL_DEBUG;
54
55 LOG_LB_CONN_SL(lb_conn, DREF, level, file, line, "%s %s: now used by %s\n",
56 (e->count - old_use_count) > 0? "+" : "-", e->use,
57 osmo_use_count_to_str_c(OTC_SELECT, &lb_conn->use_count));
58
59 if (e->count < 0)
60 return -ERANGE;
61
62 if (total == 0)
63 lb_conn_close(lb_conn);
64 return 0;
65}
66
67static struct lb_conn *lb_conn_alloc(struct lb_peer *lb_peer, uint32_t sccp_conn_id, const char *use_token)
68{
69 struct lb_conn *lb_conn;
70
71 lb_conn = talloc(lb_peer, struct lb_conn);
72 OSMO_ASSERT(lb_conn);
73
74 *lb_conn = (struct lb_conn){
75 .lb_peer = lb_peer,
76 .sccp_conn_id = sccp_conn_id,
77 .use_count = {
78 .talloc_object = lb_conn,
79 .use_cb = lb_conn_use_cb,
80 },
81 };
82
83 llist_add(&lb_conn->entry, &lb_peer->sli->lb_conns);
84 lb_conn_get(lb_conn, use_token);
85 return lb_conn;
86}
87
88struct lb_conn *lb_conn_create_incoming(struct lb_peer *lb_peer, uint32_t sccp_conn_id, const char *use_token)
89{
90 LOG_LB_PEER(lb_peer, LOGL_DEBUG, "Incoming lb_conn id: %u\n", sccp_conn_id);
91 return lb_conn_alloc(lb_peer, sccp_conn_id, use_token);
92}
93
94struct lb_conn *lb_conn_create_outgoing(struct lb_peer *lb_peer, const char *use_token)
95{
96 int new_conn_id = sccp_lb_inst_next_conn_id();
97 if (new_conn_id < 0)
98 return NULL;
99 LOG_LB_PEER(lb_peer, LOGL_DEBUG, "Outgoing lb_conn id: %u\n", new_conn_id);
100 return lb_conn_alloc(lb_peer, new_conn_id, use_token);
101}
102
103struct lb_conn *lb_conn_find_by_smlc_subscr(struct smlc_subscr *smlc_subscr, const char *use_token)
104{
105 struct lb_conn *lb_conn;
106 llist_for_each_entry(lb_conn, &g_smlc->lb->lb_conns, entry) {
107 if (lb_conn->smlc_subscr == smlc_subscr) {
108 lb_conn_get(lb_conn, use_token);
109 return lb_conn;
110 }
111 }
112 return NULL;
113}
114
115int lb_conn_down_l2_co(struct lb_conn *lb_conn, struct msgb *l3, bool initial)
116{
117 struct lb_peer_ev_ctx co = {
118 .conn_id = lb_conn->sccp_conn_id,
119 .lb_conn = lb_conn,
120 .msg = l3,
121 };
122 if (!lb_conn->lb_peer)
123 return -EIO;
124 return osmo_fsm_inst_dispatch(lb_conn->lb_peer->fi,
125 initial ? LB_PEER_EV_MSG_DOWN_CO_INITIAL : LB_PEER_EV_MSG_DOWN_CO,
126 &co);
127}
128
129int lb_conn_rx(struct lb_conn *lb_conn, struct msgb *msg, bool initial)
130{
131 struct bssap_le_pdu bssap_le;
132 struct osmo_bssap_le_err *err;
133 if (osmo_bssap_le_dec(&bssap_le, &err, msg, msg)) {
134 LOG_LB_CONN(lb_conn, LOGL_ERROR, "Rx BSSAP-LE with error: %s\n", err->logmsg);
135 return -EINVAL;
136 }
137
138 return smlc_loc_req_rx_bssap_le(lb_conn, &bssap_le);
139}
140
141int lb_conn_send_bssmap_le(struct lb_conn *lb_conn, const struct bssmap_le_pdu *bssmap_le)
142{
143 struct msgb *msg;
144 int rc;
145 struct bssap_le_pdu bssap_le = {
146 .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
147 .bssmap_le = *bssmap_le,
148 };
149
150 msg = osmo_bssap_le_enc(&bssap_le);
151 if (!msg) {
152 LOG_LB_CONN(lb_conn, LOGL_ERROR, "Unable to encode %s\n",
153 osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le));
154 return -EINVAL;
155 }
156 rc = lb_conn_down_l2_co(lb_conn, msg, false);
157 msgb_free(msg);
158 if (rc)
159 LOG_LB_CONN(lb_conn, LOGL_ERROR, "Unable to send %s\n",
160 osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le));
161 return rc;
162}
163
164/* Regularly close the lb_conn */
165void lb_conn_close(struct lb_conn *lb_conn)
166{
167 if (!lb_conn)
168 return;
169 if (lb_conn->closing)
170 return;
171 lb_conn->closing = true;
172 LOG_LB_PEER(lb_conn->lb_peer, LOGL_DEBUG, "Closing lb_conn\n");
173
174 if (lb_conn->lb_peer) {
175 /* Todo: pass a useful SCCP cause? */
176 sccp_lb_disconnect(lb_conn->lb_peer->sli, lb_conn->sccp_conn_id, 0);
177 lb_conn->lb_peer = NULL;
178 }
179
180 if (lb_conn->smlc_loc_req)
181 osmo_fsm_inst_term(lb_conn->smlc_loc_req->fi, OSMO_FSM_TERM_REGULAR, NULL);
182
183 if (lb_conn->smlc_subscr)
184 smlc_subscr_put(lb_conn->smlc_subscr, SMLC_SUBSCR_USE_LB_CONN);
185
186 llist_del(&lb_conn->entry);
187 talloc_free(lb_conn);
188}
189
190/* Same as lb_conn_close() but without sending any SCCP messages (e.g. after RESET) */
191void lb_conn_discard(struct lb_conn *lb_conn)
192{
193 if (!lb_conn)
194 return;
195 /* Make sure to drop dead and don't dispatch things like DISCONNECT requests on SCCP. */
196 lb_conn->lb_peer = NULL;
197 lb_conn_close(lb_conn);
198}