aboutsummaryrefslogtreecommitdiffstats
path: root/include/osmocom/msc/sccp_ran.h
blob: f190d91e3291d9ebfb65eec31d23d5ed55972264 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
/* The RAN (Radio Access Network) side of an A- or Iu-connection, which is closely tied to an SCCP connection.
 * (as opposed to the NAS side.)
 *
 * The SCCP connection is located with the MSC-I role, while the MSC-A responsible for subscriber management may be at a
 * remote MSC behind an E-interface connection. In that case we need to forward the L2 messages over the E-interface and
 * the BSSAP or RANAP messages get decoded and interpreted at MSC-A.
 *
 * The life cycle of a DTAP message from RAN to MSC-A -- starting from the bottom left:
 *
 *       ------------------>[ 3GPP TS 24.008 ]------------------->|
 *       ^      (Request)                        (Response)       |
 *       |                                                        v
 *      msc_a_up_l3()                                            msc_a_tx_dtap_to_i(dtap_msgb)
 *       ^                                                        |
 *       |                                                        v
 *      msc_a_ran_decode_cb(struct ran_dec_msg)                  msc_a_ran_enc(struct ran_enc_msg)
 *       ^                ^                    .                  |
 *       |  -Decode NAS-  |                       .  NAS          v
 *       |                |                          .           ran_infra[type]->ran_encode(struct ran_enc_msg)
 *      ran_a_decode_l2()    ran_iu_decode_l2()         .         |                      |
 *       ^                ^                                .      v                      v
 *       |                |                                   .  ran_a_encode()    ran_iu_encode()
 *      ran_infra[type]->ran_dec_l2()                             |                      |
 *       ^                                                        | -Encode BSSAP/RANAP- |
 *       |                                                        v                      v
 *      msc_a_ran_dec()                                           msub_tx_an_apdu(from MSC_ROLE_A to MSC_ROLE_I)
 *       ^                                                        |
 *       |                             MSC-A                      v
 *    . msc_a FSM .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  . msc_a FSM .  .  .  .  .  .  .  .  .  .
 *       ^                                                        |
 *       | MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST      v
 *       | data = an_apdu                                       [possibly
 *       |                                                       via GSUP
 *     [possibly                                                 from remote MSC-A]
 *      via GSUP                                                  |
 *      to remote MSC-A]                                          | MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST
 *       ^                                                        | data = an_apdu
 *       |                                                        v
 *    . msc_i FSM .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  . msc_i FSM .  .  .  .  .  .  .  .  .  .
 *       ^                             MSC-I                      |
 *       | MSC_EV_FROM_RAN_UP_L2                                  V
 *       | data = an_apdu                                        msc_i_down_l2(an_apdu->msg)
 *       |                                                        |
 *      ran_peer FSM                                              V
 *       ^                                                       ran_conn_down_l2_co();
 *       | RAN_PEER_EV_MSG_UP_CO                                  |
 *       | data = struct ran_peer_ev_ctx                          | RAN_PEER_EV_MSG_DOWN_CO
 *       |                                                        | data = struct ran_peer_ev_ctx
 *      ran_peer_up_l2()                                          V
 *      (ran_infa->sccp_ran_ops.up_l2)                           ran_peer FSM
 *       ^    ^                                                   |
 *       |    |                                                   v
 *      sccp_ran_sap_up()                                        sccp_ran_down_l2_co(conn_id, msg)
 *       ^    ^                                                   |    |
 *       |    |                                                   |SCCP|
 *       |SCCP|                                                   v    v
 *       |    |  <------------------------------------------------------
 *      BSC  RNC
 *       |    |
 *      BTS  NodeB
 *       |    |
 *       MS   UE
 *
 * sccp_ran:
 * - handles receiving of SCCP primitives from the SCCP layer.
 * - extracts L2 msg
 * - passes on L2 msg and conn_id by calling sccp_ran_ops.up_l2 == ran_peer_up_l2().
 *
 * On Connection-Oriented *Initial* message
 * ========================================
 *
 * ran_peer_up_l2()
 * - notices an unknown, new osmo_rat_type:conn_id and
 * - first creates an "empty" msub with new local MSC-I and MSC-A roles;
 *   in this case always a *local* MSC-A (never remote on Initial messages).
 * - Passes the L2 msgb containing the BSSAP or RANAP as AN-APDU
 *   in MSC_A_EV_FROM_I_COMPLETE_LAYER_3 to the MSC-A role FSM instance.
 *
 * MSC-A:
 * - Receives MSC_A_EV_FROM_I_COMPLETE_LAYER_3 AN-APDU, notices an_proto indicating BSSAP or RANAP.
 * - Passes L2 message to ran_infra[]->ran_dec_l2(), which decodes the BSSAP or RANAP.
 * - contained information is passed to msc_a_ran_decode_cb().
 * - which msc_a starts Complete-L3 and VLR procedures,
 * - associates msub with a vlr_subscr,
 * - sends DTAP requests back down by calling msc_a_tx_dtap_to_i() (possibly other more specialized tx functions)
 * - according to ran_infra[]->ran_encode(), the ran_enc_msg gets encoded as BSSAP or RANAP.
 * - passes as AN-APDU to MSC-I in MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST signal.
 *
 * MSC-I, receiving AN-APDU from local MSC-A:
 * - feeds L2 msgb to the ran_peer FSM as RAN_PEER_EV_MSG_DOWN_CO, passing the SCCP conn_id.
 *
 * sccp_ran_down_l2_co()
 * - wraps in SCCP prim,
 * - sends down.
 *
 *
 * On (non-Initial) Connection-Oriented DTAP
 * =========================================
 *
 * ran_peer_up_l2()
 * - notices an already known conn_id by looking up a matching osmo_rat_type:ran_conn.
 * - ran_conn already associated with an MSC-I role.
 * - Now forwards AN-APDU like above, only using MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST.
 *
 *
 * MSC-A and MSC-I roles on separate MSC instances
 * ===============================================
 *
 * After inter-MSC handover, the MSC-I and MSC-A roles can be on separate MSC instances, typically physically distant /
 * possibly belonging to a different operator. This will never see Complete-L3.
 * Assuming that both instances are osmo-msc, then:
 *
 * At MSC-B:
 *   initially, via GSUP:
 *   - receives Handover Request from remote MSC-A,
 *   - creates msub with local MSC-T role,
 *   - sets up the ran_conn with a new SCCP conn_id, and waits for the MS/UE to show up.
 *   - (fast-forward to successful Handover)
 *   - MSC-T role becomes MSC-I for the remote MSC-A.
 *
 *   Then for DTAP from the MS:
 *
 *   sccp_ran:
 *   - receives SCCP,
 *   - extracts L2 and passes on to ran_peer_up_l2().
 *
 *   ran_peer_up_l2()
 *   - notices an already known conn_id by looking up a matching ran_conn.
 *   - ran_conn already associated with an MSC-I role and an msub.
 *   - forwards AN-APDU in MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST to the MSC-A role.
 *
 *   At MSC-B, the "MSC-A role" is a *remote* implementation,
 *   meaning there is an msc_a_remote FSM instance in MSC-B's msub:
 *
 *   MSC-A-Remote:
 *   - msc_a_remote receives MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST,
 *   - wraps AN-APDU in GSUP message,
 *   - sends to remote MSC-A.
 *
 * At MSC-A:
 *   Here, msub has a *remote* MSC-I role,
 *   meaning it is an msc_i_remote FSM instance:
 *
 *   MSC-I-Remote:
 *   - msc_i_remote receives and decodes GSUP message,
 *   - passes AN-APDU to MSC-A FSM instance via MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST.
 *
 *   MSC-A role:
 *   - Receives MSC_A_EV_FROM_I_PROCESS_ACCESS_SIGNALLING_REQUEST, notices an_proto indicating BSSAP or RANAP.
 *   - Passes L2 message to ran_infra[]->ran_dec_l2(), which decodes the BSSAP or RANAP.
 *   - contained information is passed to msc_a_ran_decode_cb().
 *   - sends DTAP requests back down by calling msc_a_tx_dtap_to_i() (possibly other more specialized tx functions)
 *   - according to ran_infra[]->ran_encode(), the ran_enc_msg gets encoded as BSSAP or RANAP.
 *   - passes as AN-APDU to MSC-I in MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST signal.
 *
 *   MSC-I-Remote:
 *   - msc_i_remote wraps AN-APDU in GSUP message,
 *   - sends to MSC-B
 *
 * At MSC-B:
 *   MSC-A-Remote:
 *   - msc_a_remote receives GSUP message,
 *   - passes AN-APDU to msc_i in MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST.
 *
 *   MSC-I:
 *   - BSSAP or RANAP is indicated both by the AN-APDU an_proto, as well as the ran_conn state for that subscriber.
 *   - feeds L2 msgb to the ran_peer FSM as RAN_PEER_EV_MSG_DOWN_CO, passing the SCCP conn_id.
 *
 *   sccp_ran_down_l2_co()
 *   - wraps in SCCP prim,
 *   - sends down.
 *
 */

#pragma once

#include <stdint.h>

#include <osmocom/core/tdef.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <osmocom/sigtran/sccp_sap.h>

#include <osmocom/msc/paging.h>

struct msgb;
struct ran_infra;
struct sccp_ran_inst;

#define LOG_SCCP_RAN_CO(sri, peer_addr, conn_id, level, fmt, args...) \
	LOGP((sri) && (sri)->ran? (sri)->ran->log_subsys : DMSC, level, "(%s-%u%s%s) " fmt, \
	     osmo_rat_type_name((sri) && (sri)->ran? (sri)->ran->type : -1), conn_id, \
	     peer_addr ? " from " : "", \
	     peer_addr ? osmo_sccp_inst_addr_name((sri)->sccp, peer_addr) : "", \
	     ## args)

#define LOG_SCCP_RAN_CL_CAT(sri, peer_addr, subsys, level, fmt, args...) \
	LOGP(subsys, level, "(%s%s%s) " fmt, \
	     osmo_rat_type_name((sri) && (sri)->ran? (sri)->ran->type : -1), \
	     peer_addr ? " from " : "", \
	     peer_addr ? osmo_sccp_inst_addr_name((sri)->sccp, peer_addr) : "", \
	     ## args)

#define LOG_SCCP_RAN_CL(sri, peer_addr, level, fmt, args...) \
	LOG_SCCP_RAN_CL_CAT(sri, peer_addr, (sri) && (sri)->ran? (sri)->ran->log_subsys : DMSC, level, fmt, ##args)

#define LOG_SCCP_RAN_CAT(sri, subsys, level, fmt, args...) \
	LOG_SCCP_RAN_CL_CAT(sri, NULL, subsys, level, fmt, ##args)

#define LOG_SCCP_RAN(sri, level, fmt, args...) \
	LOG_SCCP_RAN_CL(sri, NULL, level, fmt, ##args)

extern struct osmo_tdef g_sccp_tdefs[];

enum reset_msg_type {
	SCCP_RAN_MSG_NON_RESET = 0,
	SCCP_RAN_MSG_RESET,
	SCCP_RAN_MSG_RESET_ACK,
};

struct sccp_ran_ops {
	/* Implemented to receive L2 messages (e.g. BSSAP or RANAP passed to ran_peer).
	 * - ConnectionLess messages: co = false, calling_addr != NULL, conn_id == 0;
	 * - ConnectionOriented Initial messages: co = true, calling_addr != NULL;
	 * - ConnectionOriented non-Initial messages: co = true, calling_addr == NULL;
	 */
	int (* up_l2 )(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *calling_addr, bool co, uint32_t conn_id,
		       struct msgb *l2);

	/* Implemented to finally remove a connection state. Last event in a connection-oriented exchange. If the
	 * N-DISCONNECT contained l2 data, it was dispatched via up_l2() before this is called. */
	void (* disconnect )(struct sccp_ran_inst *sri, uint32_t conn_id);

	/* Return whether the given l2_cl message is a RESET, RESET ACKNOWLEDGE, or RESET-unrelated message.
	 * This callback is stored in struct sccp_ran_inst to provide RESET handling to the caller (ran_peer),
	 * it is not used in sccp_ran.c. */
	enum reset_msg_type (* is_reset_msg )(const struct sccp_ran_inst *sri, const struct msgb *l2_cl);

	/* Return a RESET or RESET ACK message for this RAN type.
	 * This callback is stored in struct sccp_ran_inst to provide RESET handling to the caller (ran_peer),
	 * it is not used in sccp_ran.c. */
	struct msgb* (* make_reset_msg )(const struct sccp_ran_inst *sri, enum reset_msg_type);

	/* Return a PAGING message towards the given Cell Identifier, to page for the given TMSI or IMSI.
	 * Page for TMSI if TMSI != GSM_RESERVED_TMSI, otherwise page for IMSI. */
	struct msgb* (* make_paging_msg )(const struct sccp_ran_inst *sri, const struct gsm0808_cell_id *page_cell_id,
					  const char *imsi, uint32_t tmsi, enum paging_cause cause);

	/* Return a human printable name for the msgb */
	const char* (* msg_name )(const struct sccp_ran_inst *sri, const struct msgb *l2);
};

struct sccp_ran_inst {
	struct ran_infra *ran;

	struct osmo_sccp_instance *sccp;
	struct osmo_sccp_user *scu;
	struct osmo_sccp_addr local_sccp_addr;

	struct llist_head ran_peers;
	struct llist_head ran_conns;

	void *user_data;

	/* Compatibility with legacy osmo-hnbgw that was unable to properly handle RESET messages.  Set to 'false' to
	 * require proper RESET procedures, set to 'true' to implicitly put a ran_peer in RAN_PEER_ST_READY upon the
	 * first CO message. Default is false = be strict. */
	bool ignore_missing_reset;
};

struct sccp_ran_inst *sccp_ran_init(void *talloc_ctx, struct osmo_sccp_instance *sccp, enum osmo_sccp_ssn ssn,
				    const char *sccp_user_name, struct ran_infra *ran, void *user_data);

int sccp_ran_down_l2_co_initial(struct sccp_ran_inst *sri,
				const struct osmo_sccp_addr *called_addr,
				uint32_t conn_id, struct msgb *l2);
int sccp_ran_down_l2_co(struct sccp_ran_inst *sri, uint32_t conn_id, struct msgb *l2);
int sccp_ran_down_l2_cl(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *called_addr, struct msgb *l2);

int sccp_ran_disconnect(struct sccp_ran_inst *ran, uint32_t conn_id, uint32_t cause);