blob: 986ab446879bb69d993945566848563b51d14f32 [file] [log] [blame]
Neels Hofmeyr72992152020-09-19 02:36:08 +02001/*
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/linuxlist.h>
24#include <osmocom/core/logging.h>
25#include <osmocom/core/fsm.h>
26#include <osmocom/gsm/bssmap_le.h>
27#include <osmocom/sigtran/sccp_helpers.h>
28
29#include <osmocom/smlc/smlc_data.h>
30#include <osmocom/smlc/sccp_lb_inst.h>
31#include <osmocom/smlc/lb_peer.h>
32
33static struct osmo_fsm lb_peer_fsm;
34
35static __attribute__((constructor)) void lb_peer_init()
36{
37 OSMO_ASSERT( osmo_fsm_register(&lb_peer_fsm) == 0);
38}
39
40static struct lb_peer *lb_peer_alloc(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr)
41{
42 struct lb_peer *lbp;
43 struct osmo_fsm_inst *fi;
44
45 fi = osmo_fsm_inst_alloc(&lb_peer_fsm, sli, NULL, LOGL_DEBUG, NULL);
46 OSMO_ASSERT(fi);
47
48 osmo_fsm_inst_update_id(fi, osmo_sccp_addr_to_id_c(OTC_SELECT, osmo_sccp_get_ss7(sli->sccp), peer_addr));
49
50 lbp = talloc_zero(fi, struct lb_peer);
51 OSMO_ASSERT(lbp);
52 *lbp = (struct lb_peer){
53 .fi = fi,
54 .sli = sli,
55 .peer_addr = *peer_addr,
56 };
57 fi->priv = lbp;
58
59 llist_add(&lbp->entry, &sli->lb_peers);
60
61 return lbp;
62}
63
64struct lb_peer *lb_peer_find_or_create(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr)
65{
66 struct lb_peer *lbp = lb_peer_find(sli, peer_addr);
67 if (lbp)
68 return lbp;
69 return lb_peer_alloc(sli, peer_addr);
70}
71
72struct lb_peer *lb_peer_find(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr)
73{
74 struct lb_peer *lbp;
75 llist_for_each_entry(lbp, &sli->lb_peers, entry) {
76 if (osmo_sccp_addr_ri_cmp(peer_addr, &lbp->peer_addr))
77 continue;
78 return lbp;
79 }
80 return NULL;
81}
82
83static const struct osmo_tdef_state_timeout lb_peer_fsm_timeouts[32] = {
84 [LB_PEER_ST_WAIT_RX_RESET_ACK] = { .T = -13 },
85 [LB_PEER_ST_DISCARDING] = { .T = -14 },
86};
87
88#define lb_peer_state_chg(LB_PEER, NEXT_STATE) \
89 osmo_tdef_fsm_inst_state_chg((LB_PEER)->fi, NEXT_STATE, lb_peer_fsm_timeouts, g_smlc_tdefs, 5)
90
91void lb_peer_discard_all_conns(struct lb_peer *lbp)
92{
93 struct lb_conn *lb_conn, *next;
94
95 lb_peer_for_each_lb_conn_safe(lb_conn, next, lbp) {
96 lb_conn_discard(lb_conn);
97 }
98}
99
100/* Drop all SCCP connections for this lb_peer, respond with RESET ACKNOWLEDGE and move to READY state. */
101static void lb_peer_rx_reset(struct lb_peer *lbp, struct msgb *msg)
102{
103 struct msgb *resp;
104 struct bssap_le_pdu reset_ack = {
105 .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
106 .bssmap_le = {
107 .msg_type = BSSMAP_LE_MSGT_RESET_ACK,
108 },
109 };
110
111 lb_peer_discard_all_conns(lbp);
112
113 resp = osmo_bssap_le_enc(&reset_ack);
114 if (!resp) {
115 LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to compose RESET ACKNOWLEDGE message\n");
116 lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);
117 return;
118 }
119
120 if (sccp_lb_down_l2_cl(lbp->sli, &lbp->peer_addr, resp)) {
121 LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to send RESET ACKNOWLEDGE message\n");
122 lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);
123 msgb_free(msg);
124 return;
125 }
126
127 LOG_LB_PEER(lbp, LOGL_INFO, "Sent RESET ACKNOWLEDGE\n");
128
129 /* sccp_lb_down_l2_cl() doesn't free msgb */
130 msgb_free(resp);
131
132 lb_peer_state_chg(lbp, LB_PEER_ST_READY);
133}
134
135static void lb_peer_rx_reset_ack(struct lb_peer *lbp, struct msgb* msg)
136{
137 lb_peer_state_chg(lbp, LB_PEER_ST_READY);
138}
139
140void lb_peer_reset(struct lb_peer *lbp)
141{
142 struct bssap_le_pdu reset = {
143 .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
144 .bssmap_le = {
145 .msg_type = BSSMAP_LE_MSGT_RESET,
146 .reset = GSM0808_CAUSE_EQUIPMENT_FAILURE,
147 },
148 };
149 struct msgb *msg;
150 int rc;
151
152 lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET_ACK);
153 lb_peer_discard_all_conns(lbp);
154
155 msg = osmo_bssap_le_enc(&reset);
156 if (!msg) {
157 LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to compose RESET message\n");
158 lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);
159 return;
160 }
161
162 rc = sccp_lb_down_l2_cl(lbp->sli, &lbp->peer_addr, msg);
163 msgb_free(msg);
164 if (rc) {
165 LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to send RESET message\n");
166 lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);
167 }
168}
169
170void lb_peer_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
171{
172 struct lb_peer *lbp = fi->priv;
173 struct lb_peer_ev_ctx *ctx = data;
174 struct msgb *msg = ctx->msg;
175 enum bssmap_le_msgt msg_type;
176
177 switch (event) {
178 case LB_PEER_EV_MSG_UP_CL:
179 msg_type = osmo_bssmap_le_msgt(msgb_l2(msg), msgb_l2len(msg));
180 switch (msg_type) {
181 case BSSMAP_LE_MSGT_RESET:
182 osmo_fsm_inst_dispatch(fi, LB_PEER_EV_RX_RESET, msg);
183 return;
184 case BSSMAP_LE_MSGT_RESET_ACK:
185 osmo_fsm_inst_dispatch(fi, LB_PEER_EV_RX_RESET_ACK, msg);
186 return;
187 default:
188 LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled ConnectionLess message received: %s\n",
189 osmo_bssmap_le_msgt_name(msg_type));
190 return;
191 }
192
193 default:
194 LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));
195 return;
196 }
197}
198
199void lb_peer_st_wait_rx_reset(struct osmo_fsm_inst *fi, uint32_t event, void *data)
200{
201 struct lb_peer *lbp = fi->priv;
202 struct lb_peer_ev_ctx *ctx;
203 struct msgb *msg;
204
205 switch (event) {
206
207 case LB_PEER_EV_MSG_UP_CO:
208 case LB_PEER_EV_MSG_UP_CO_INITIAL:
209 ctx = data;
210 OSMO_ASSERT(ctx);
211 LOG_LB_PEER(lbp, LOGL_ERROR, "Receiving CO message on Lb peer that has not done a proper RESET yet."
212 " Disconnecting on incoming message, sending RESET to Lb peer.\n");
213 /* No valid RESET procedure has happened here yet. Usually, we're expecting the Lb peer (BSC,
214 * RNC) to first send a RESET message before sending Connection Oriented messages. So if we're
215 * getting a CO message, likely we've just restarted or something. Send a RESET to the peer. */
216
217 lb_peer_disconnect(lbp->sli, ctx->conn_id);
218
219 lb_peer_reset(lbp);
220 return;
221
222 case LB_PEER_EV_RX_RESET:
223 msg = (struct msgb*)data;
224 lb_peer_rx_reset(lbp, msg);
225 return;
226
227 default:
228 LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));
229 return;
230 }
231}
232
233void lb_peer_st_wait_rx_reset_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
234{
235 struct lb_peer *lbp = fi->priv;
236 struct lb_peer_ev_ctx *ctx;
237 struct msgb *msg;
238
239 switch (event) {
240
241 case LB_PEER_EV_RX_RESET_ACK:
242 msg = (struct msgb*)data;
243 lb_peer_rx_reset_ack(lbp, msg);
244 return;
245
246 case LB_PEER_EV_MSG_UP_CO:
247 case LB_PEER_EV_MSG_UP_CO_INITIAL:
248 ctx = data;
249 OSMO_ASSERT(ctx);
250 LOG_LB_PEER(lbp, LOGL_ERROR, "Receiving CO message on Lb peer that has not done a proper RESET yet."
251 " Disconnecting on incoming message, sending RESET to Lb peer.\n");
252 sccp_lb_disconnect(lbp->sli, ctx->conn_id, 0);
253 /* No valid RESET procedure has happened here yet. */
254 lb_peer_reset(lbp);
255 return;
256
257 case LB_PEER_EV_RX_RESET:
258 msg = (struct msgb*)data;
259 lb_peer_rx_reset(lbp, msg);
260 return;
261
262 default:
263 LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));
264 return;
265 }
266}
267
268void lb_peer_st_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
269{
270 struct lb_peer *lbp = fi->priv;
271 struct lb_peer_ev_ctx *ctx;
272 struct lb_conn *lb_conn;
273 struct msgb *msg;
274
275 switch (event) {
276
277 case LB_PEER_EV_MSG_UP_CO_INITIAL:
278 ctx = data;
279 OSMO_ASSERT(ctx);
280 OSMO_ASSERT(!ctx->lb_conn);
281 OSMO_ASSERT(ctx->msg);
282
283 lb_conn = lb_conn_create_incoming(lbp, ctx->conn_id, __func__);
284 if (!lb_conn) {
285 LOG_LB_PEER(lbp, LOGL_ERROR, "Cannot allocate lb_conn\n");
286 return;
287 }
288
289 lb_conn_rx(lb_conn, ctx->msg, true);
290 lb_conn_put(lb_conn, __func__);
291 return;
292
293 case LB_PEER_EV_MSG_UP_CO:
294 ctx = data;
295 OSMO_ASSERT(ctx);
296 OSMO_ASSERT(ctx->lb_conn);
297 OSMO_ASSERT(ctx->msg);
298
299 lb_conn_rx(ctx->lb_conn, ctx->msg, false);
300 return;
301
302 case LB_PEER_EV_MSG_DOWN_CO_INITIAL:
303 ctx = data;
304 OSMO_ASSERT(ctx);
305 OSMO_ASSERT(ctx->msg);
306 sccp_lb_down_l2_co_initial(lbp->sli, &lbp->peer_addr, ctx->conn_id, ctx->msg);
307 return;
308
309 case LB_PEER_EV_MSG_DOWN_CO:
310 ctx = data;
311 OSMO_ASSERT(ctx);
312 OSMO_ASSERT(ctx->msg);
313 sccp_lb_down_l2_co(lbp->sli, ctx->conn_id, ctx->msg);
314 return;
315
316 case LB_PEER_EV_MSG_DOWN_CL:
317 OSMO_ASSERT(data);
318 sccp_lb_down_l2_cl(lbp->sli, &lbp->peer_addr, (struct msgb*)data);
319 return;
320
321 case LB_PEER_EV_RX_RESET:
322 msg = (struct msgb*)data;
323 lb_peer_rx_reset(lbp, msg);
324 return;
325
326 default:
327 LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));
328 return;
329 }
330}
331
332static int lb_peer_fsm_timer_cb(struct osmo_fsm_inst *fi)
333{
334 struct lb_peer *lbp = fi->priv;
335 lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);
336 return 0;
337}
338
339void lb_peer_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
340{
341 struct lb_peer *lbp = fi->priv;
342 lb_peer_discard_all_conns(lbp);
343 llist_del(&lbp->entry);
344}
345
346static const struct value_string lb_peer_fsm_event_names[] = {
347 OSMO_VALUE_STRING(LB_PEER_EV_MSG_UP_CL),
348 OSMO_VALUE_STRING(LB_PEER_EV_MSG_UP_CO_INITIAL),
349 OSMO_VALUE_STRING(LB_PEER_EV_MSG_UP_CO),
350 OSMO_VALUE_STRING(LB_PEER_EV_MSG_DOWN_CL),
351 OSMO_VALUE_STRING(LB_PEER_EV_MSG_DOWN_CO_INITIAL),
352 OSMO_VALUE_STRING(LB_PEER_EV_MSG_DOWN_CO),
353 OSMO_VALUE_STRING(LB_PEER_EV_RX_RESET),
354 OSMO_VALUE_STRING(LB_PEER_EV_RX_RESET_ACK),
355 OSMO_VALUE_STRING(LB_PEER_EV_CONNECTION_SUCCESS),
356 OSMO_VALUE_STRING(LB_PEER_EV_CONNECTION_TIMEOUT),
357 {}
358};
359
360#define S(x) (1 << (x))
361
362static const struct osmo_fsm_state lb_peer_fsm_states[] = {
363 [LB_PEER_ST_WAIT_RX_RESET] = {
364 .name = "WAIT_RX_RESET",
365 .action = lb_peer_st_wait_rx_reset,
366 .in_event_mask = 0
367 | S(LB_PEER_EV_RX_RESET)
368 | S(LB_PEER_EV_MSG_UP_CO_INITIAL)
369 | S(LB_PEER_EV_MSG_UP_CO)
370 | S(LB_PEER_EV_CONNECTION_TIMEOUT)
371 ,
372 .out_state_mask = 0
373 | S(LB_PEER_ST_WAIT_RX_RESET)
374 | S(LB_PEER_ST_WAIT_RX_RESET_ACK)
375 | S(LB_PEER_ST_READY)
376 | S(LB_PEER_ST_DISCARDING)
377 ,
378 },
379 [LB_PEER_ST_WAIT_RX_RESET_ACK] = {
380 .name = "WAIT_RX_RESET_ACK",
381 .action = lb_peer_st_wait_rx_reset_ack,
382 .in_event_mask = 0
383 | S(LB_PEER_EV_RX_RESET)
384 | S(LB_PEER_EV_RX_RESET_ACK)
385 | S(LB_PEER_EV_MSG_UP_CO_INITIAL)
386 | S(LB_PEER_EV_MSG_UP_CO)
387 | S(LB_PEER_EV_CONNECTION_TIMEOUT)
388 ,
389 .out_state_mask = 0
390 | S(LB_PEER_ST_WAIT_RX_RESET)
391 | S(LB_PEER_ST_WAIT_RX_RESET_ACK)
392 | S(LB_PEER_ST_READY)
393 | S(LB_PEER_ST_DISCARDING)
394 ,
395 },
396 [LB_PEER_ST_READY] = {
397 .name = "READY",
398 .action = lb_peer_st_ready,
399 .in_event_mask = 0
400 | S(LB_PEER_EV_RX_RESET)
401 | S(LB_PEER_EV_MSG_UP_CO_INITIAL)
402 | S(LB_PEER_EV_MSG_UP_CO)
403 | S(LB_PEER_EV_MSG_DOWN_CO_INITIAL)
404 | S(LB_PEER_EV_MSG_DOWN_CO)
405 | S(LB_PEER_EV_MSG_DOWN_CL)
406 ,
407 .out_state_mask = 0
408 | S(LB_PEER_ST_WAIT_RX_RESET)
409 | S(LB_PEER_ST_WAIT_RX_RESET_ACK)
410 | S(LB_PEER_ST_READY)
411 | S(LB_PEER_ST_DISCARDING)
412 ,
413 },
414 [LB_PEER_ST_DISCARDING] = {
415 .name = "DISCARDING",
416 },
417};
418
419static struct osmo_fsm lb_peer_fsm = {
420 .name = "lb_peer",
421 .states = lb_peer_fsm_states,
422 .num_states = ARRAY_SIZE(lb_peer_fsm_states),
423 .log_subsys = DLB,
424 .event_names = lb_peer_fsm_event_names,
425 .timer_cb = lb_peer_fsm_timer_cb,
426 .cleanup = lb_peer_fsm_cleanup,
427 .allstate_action = lb_peer_allstate_action,
428 .allstate_event_mask = 0
429 | S(LB_PEER_EV_MSG_UP_CL)
430 ,
431};
432
433int lb_peer_up_l2(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *calling_addr, bool co, uint32_t conn_id,
434 struct msgb *l2)
435{
436 struct lb_peer *lb_peer = NULL;
437 uint32_t event;
438 struct lb_peer_ev_ctx ctx = {
439 .conn_id = conn_id,
440 .msg = l2,
441 };
442
443 if (co) {
444 struct lb_conn *lb_conn;
445 llist_for_each_entry(lb_conn, &sli->lb_conns, entry) {
446 if (lb_conn->sccp_conn_id == conn_id) {
447 lb_peer = lb_conn->lb_peer;
448 ctx.lb_conn = lb_conn;
449 break;
450 }
451 }
452
453 if (lb_peer && calling_addr) {
454 LOG_SCCP_LB_CO(sli, calling_addr, conn_id, LOGL_ERROR,
455 "Connection-Oriented Initial message for already existing conn_id."
456 " Dropping message.\n");
457 return -EINVAL;
458 }
459
460 if (!lb_peer && !calling_addr) {
461 LOG_SCCP_LB_CO(sli, calling_addr, conn_id, LOGL_ERROR,
462 "Connection-Oriented non-Initial message for unknown conn_id %u."
463 " Dropping message.\n", conn_id);
464 return -EINVAL;
465 }
466 }
467
468 if (calling_addr) {
469 lb_peer = lb_peer_find_or_create(sli, calling_addr);
470 if (!lb_peer) {
471 LOG_SCCP_LB_CL(sli, calling_addr, LOGL_ERROR, "Cannot register Lb peer\n");
472 return -EIO;
473 }
474 }
475
476 OSMO_ASSERT(lb_peer && lb_peer->fi);
477
478 if (co)
479 event = calling_addr ? LB_PEER_EV_MSG_UP_CO_INITIAL : LB_PEER_EV_MSG_UP_CO;
480 else
481 event = LB_PEER_EV_MSG_UP_CL;
482
483 return osmo_fsm_inst_dispatch(lb_peer->fi, event, &ctx);
484}
485
486void lb_peer_disconnect(struct sccp_lb_inst *sli, uint32_t conn_id)
487{
488 struct lb_conn *lb_conn;
489 llist_for_each_entry(lb_conn, &sli->lb_conns, entry) {
490 if (lb_conn->sccp_conn_id == conn_id) {
491 lb_conn_discard(lb_conn);
492 return;
493 }
494 }
495}