aboutsummaryrefslogtreecommitdiffstats
path: root/src/libmsc/ran_msg.c
blob: 46816a961b7878435dc7102147e0a2b93e67abed (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
/* Common bits for RAN message handling */
/*
 * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
 * All Rights Reserved
 *
 * Author: Neels Hofmeyr
 *
 * SPDX-License-Identifier: GPL-2.0+
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <osmocom/core/utils.h>

#include <osmocom/msc/ran_msg.h>

const struct value_string ran_msg_type_names[] = {
	{ RAN_MSG_NONE, "NONE" },
	{ RAN_MSG_COMPL_L3, "COMPL_L3" },
	{ RAN_MSG_DTAP, "DTAP" },
	{ RAN_MSG_CLEAR_COMMAND, "CLEAR_COMMAND" },
	{ RAN_MSG_CLEAR_REQUEST, "CLEAR_REQUEST" },
	{ RAN_MSG_CLEAR_COMPLETE, "CLEAR_COMPLETE" },
	{ RAN_MSG_CLASSMARK_REQUEST, "CLASSMARK_REQUEST" },
	{ RAN_MSG_CLASSMARK_UPDATE, "CLASSMARK_UPDATE" },
	{ RAN_MSG_CIPHER_MODE_COMMAND, "CIPHER_MODE_COMMAND" },
	{ RAN_MSG_CIPHER_MODE_COMPLETE, "CIPHER_MODE_COMPLETE" },
	{ RAN_MSG_CIPHER_MODE_REJECT, "CIPHER_MODE_REJECT" },
	{ RAN_MSG_COMMON_ID, "COMMON_ID" },
	{ RAN_MSG_ASSIGNMENT_COMMAND, "ASSIGNMENT_COMMAND" },
	{ RAN_MSG_ASSIGNMENT_COMPLETE, "ASSIGNMENT_COMPLETE" },
	{ RAN_MSG_ASSIGNMENT_FAILURE, "ASSIGNMENT_FAILURE" },
	{ RAN_MSG_SAPI_N_REJECT, "SAPI_N_REJECT" },
	{ RAN_MSG_LCLS_STATUS, "LCLS_STATUS" },
	{ RAN_MSG_LCLS_BREAK_REQ, "LCLS_BREAK_REQ" },
	{ RAN_MSG_HANDOVER_COMMAND, "HANDOVER_COMMAND" },
	{ RAN_MSG_HANDOVER_SUCCEEDED, "HANDOVER_SUCCEEDED" },
	{ RAN_MSG_HANDOVER_PERFORMED, "HANDOVER_PERFORMED" },
	{ RAN_MSG_HANDOVER_REQUIRED, "HANDOVER_REQUIRED" },
	{ RAN_MSG_HANDOVER_REQUIRED_REJECT, "HANDOVER_REQUIRED_REJECT" },
	{ RAN_MSG_HANDOVER_REQUEST, "HANDOVER_REQUEST" },
	{ RAN_MSG_HANDOVER_REQUEST_ACK, "HANDOVER_REQUEST_ACK" },
	{ RAN_MSG_HANDOVER_DETECT, "HANDOVER_DETECT" },
	{ RAN_MSG_HANDOVER_COMPLETE, "HANDOVER_COMPLETE" },
	{ RAN_MSG_HANDOVER_FAILURE, "HANDOVER_FAILURE" },
	{}
};

/* extract the N(SD) and return the modulo value for a R99 message */
static uint8_t ran_dec_dtap_undup_determine_nsd_ret_modulo_r99(uint8_t pdisc, uint8_t msg_type, uint8_t *n_sd)
{
	switch (pdisc) {
	case GSM48_PDISC_MM:
	case GSM48_PDISC_CC:
	case GSM48_PDISC_NC_SS:
		*n_sd = (msg_type >> 6) & 0x3;
		return 4;
	case GSM48_PDISC_GROUP_CC:
	case GSM48_PDISC_BCAST_CC:
	case GSM48_PDISC_LOC:
		*n_sd = (msg_type >> 6) & 0x1;
		return 2;
	default:
		/* no sequence number, we cannot detect dups */
		return 0;
	}
}

/* extract the N(SD) and return the modulo value for a R98 message */
static uint8_t gsm0407_determine_nsd_ret_modulo_r98(uint8_t pdisc, uint8_t msg_type, uint8_t *n_sd)
{
	switch (pdisc) {
	case GSM48_PDISC_MM:
	case GSM48_PDISC_CC:
	case GSM48_PDISC_NC_SS:
	case GSM48_PDISC_GROUP_CC:
	case GSM48_PDISC_BCAST_CC:
	case GSM48_PDISC_LOC:
		*n_sd = (msg_type >> 6) & 0x1;
		return 2;
	default:
		/* no sequence number, we cannot detect dups */
		return 0;
	}
}

/* TS 24.007 11.2.3.2.3 Message Type Octet / Duplicate Detection.
 * (Not static for unit testing). */
int ran_dec_dtap_undup_pdisc_ctr_bin(uint8_t pdisc)
{
	switch (pdisc) {
	case GSM48_PDISC_MM:
	case GSM48_PDISC_CC:
	case GSM48_PDISC_NC_SS:
		return 0;
	case GSM48_PDISC_GROUP_CC:
		return 1;
	case GSM48_PDISC_BCAST_CC:
		return 2;
	case GSM48_PDISC_LOC:
		return 3;
	default:
		return -1;
	}
}

/* TS 24.007 11.2.3.2 Message Type Octet / Duplicate Detection */
bool ran_dec_dtap_undup_is_duplicate(struct osmo_fsm_inst *log_fi, uint8_t *n_sd_next, bool is_r99, struct msgb *l3)
{
	struct gsm48_hdr *gh;
	uint8_t pdisc;
	uint8_t n_sd, modulo;
	int bin;

	gh = msgb_l3(l3);
	pdisc = gsm48_hdr_pdisc(gh);

	if (is_r99) {
		modulo = ran_dec_dtap_undup_determine_nsd_ret_modulo_r99(pdisc, gh->msg_type, &n_sd);
	} else { /* pre R99 */
		modulo = gsm0407_determine_nsd_ret_modulo_r98(pdisc, gh->msg_type, &n_sd);
	}
	if (modulo == 0)
		return false;
	bin = ran_dec_dtap_undup_pdisc_ctr_bin(pdisc);
	if (bin < 0)
		return false;

	OSMO_ASSERT(bin >= 0 && bin < 4);
	if (n_sd != n_sd_next[bin]) {
		/* not what we expected: duplicate */
		LOGPFSML(log_fi, LOGL_NOTICE, "Duplicate DTAP: bin=%d, expected n_sd == %u, got %u\n",
			 bin, n_sd_next[bin], n_sd);
		return true;
	} else {
		/* as expected: no dup; update expected counter for next message */
		n_sd_next[bin] = (n_sd + 1) % modulo;
		return false;
	}
}

/* convenience: RAN decode implementations can call this to dispatch the decode_cb with a decoded ran_msg. */
int ran_decoded(struct ran_dec *ran_dec, struct ran_msg *ran_msg)
{
	if (!ran_dec->decode_cb)
		return -1;
	return ran_dec->decode_cb(ran_dec->caller_fi, ran_dec->caller_data, ran_msg);
}