blob: 88280e2f8b9f6484b5e4f8b1edd03003b23217a2 [file] [log] [blame]
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +08001/* Patch GSM 08.08 messages for the network and BS */
2/*
3 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
4 * (C) 2010 by On-Waves
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 */
22
23#include <bss_patch.h>
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080024#include <cellmgr_debug.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080025
26#include <string.h>
27
Holger Hans Peter Freythere66c7c12010-08-04 18:51:16 +080028#include <osmocore/gsm0808.h>
29#include <osmocore/protocol/gsm_08_08.h>
30
Holger Hans Peter Freythercf381e22010-08-04 18:39:26 +080031#include <osmocom/sccp/sccp.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080032
33#include <arpa/inet.h>
34
35static void patch_ass_rqst(struct msgb *msg, int length)
36{
37 struct tlv_parsed tp;
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080038 uint8_t *data, audio;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080039 int len;
40
41 tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, length - 1, 0, 0);
42 len = TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE);
43 if (len < 3)
44 return;
45
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080046 data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080047 /* no speech... ignore */
48 if ((data[0] & 0xf) != 0x1)
49 return;
50
51 /* blindly assign */
52 data[1] = GSM0808_SPEECH_FULL_PREF;
53 audio = GSM0808_PERM_FR2;
54 if (len > 3)
55 audio |= 0x80;
56 data[2] = audio;
57}
58
59static void patch_ass_cmpl(struct msgb *msg, int length)
60{
61 struct tlv_parsed tp;
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080062 uint8_t *data;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080063
64 tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, length - 1, 0, 0);
65 if (!TLVP_PRESENT(&tp, GSM0808_IE_CHOSEN_CHANNEL)) {
66 LOGP(DMSC, LOGL_ERROR, "Chosen Channel not in the MSG.\n");
67 return;
68 }
69
70 if (!TLVP_PRESENT(&tp, GSM0808_IE_SPEECH_VERSION)) {
71 LOGP(DMSC, LOGL_ERROR, "Speech version not in the MSG.\n");
72 return;
73 }
74
75 /* claim to have a TCH/H with no mode indication */
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080076 data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHOSEN_CHANNEL);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080077 data[0] = 0x09;
78
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080079 data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_SPEECH_VERSION);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080080 data[0] = GSM0808_PERM_HR3;
81}
82
83int bss_patch_filter_msg(struct msgb *msg, struct sccp_parse_result *sccp)
84{
85 int type;
86 memset(sccp, 0, sizeof(*sccp));
87 if (sccp_parse_header(msg, sccp) != 0) {
88 LOGP(DMSC, LOGL_ERROR, "Failed to parse SCCP header.\n");
89 return -1;
90 }
91
92 type = sccp_determine_msg_type(msg);
93 switch (type) {
94 case SCCP_MSG_TYPE_CR:
95 if (msg->l3h)
96 break;
97 return 0;
98 break;
99 case SCCP_MSG_TYPE_CC:
100 case SCCP_MSG_TYPE_CREF:
101 return 0;
102 break;
103 case SCCP_MSG_TYPE_RLC:
104 return BSS_FILTER_RLC;
105 break;
106 case SCCP_MSG_TYPE_RLSD:
107 return BSS_FILTER_RLSD;
108 break;
109 }
110
111 if (msgb_l3len(msg) < sccp->data_len) {
112 LOGP(DMSC, LOGL_ERROR, "Less space than there should be.\n");
113 return -1;
114 }
115
116 if (!msg->l3h || msgb_l3len(msg) < 3) {
117 return -1;
118 }
119
120 if (msg->l3h[0] != 0) {
121 return -1;
122 }
123
124 if (msgb_l3len(msg) < 2 + msg->l3h[1]) {
125 return -1;
126 }
127
128 switch (msg->l3h[2]) {
129 case BSS_MAP_MSG_ASSIGMENT_RQST:
130 msg->l3h = &msg->l3h[2];
131 patch_ass_rqst(msg, sccp->data_len - 2);
132 break;
133 case BSS_MAP_MSG_ASSIGMENT_COMPLETE:
134 msg->l3h = &msg->l3h[2];
135 patch_ass_cmpl(msg, sccp->data_len - 2);
136 break;
137 case BSS_MAP_MSG_RESET:
138 return BSS_FILTER_RESET;
139 break;
140 case BSS_MAP_MSG_RESET_ACKNOWLEDGE:
141 return BSS_FILTER_RESET_ACK;
142 break;
143 case BSS_MAP_MSG_CLEAR_COMPLETE:
144 return BSS_FILTER_CLEAR_COMPL;
145 break;
146 }
147
148 return 0;
149}
150
151static void create_cr(struct msgb *target, struct msgb *inpt, struct sccp_parse_result *sccp)
152{
Holger Hans Peter Freyther9ed3e1b2010-07-31 05:22:56 +0800153 static const uint32_t optional_offset =
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800154 offsetof(struct sccp_connection_request, optional_start);
155
156 unsigned int optional_length, optional_start;
157 struct sccp_connection_request *cr, *in_cr;
158
159 target->l2h = msgb_put(target, sizeof(*cr));
160 cr = (struct sccp_connection_request *) target->l2h;
161 in_cr = (struct sccp_connection_request *) inpt->l2h;
162
163 cr->type = in_cr->type;
164 cr->proto_class = in_cr->proto_class;
165 cr->source_local_reference = in_cr->source_local_reference;
166 cr->variable_called = 2;
167 cr->optional_start = 4;
168
169 /* called address */
170 target->l3h = msgb_put(target, 1 + 2);
171 target->l3h[0] = 2;
172 target->l3h[1] = 0x42;
173 target->l3h[2] = 254;
174
175 /*
176 * We need to keep the complete optional data. The SCCP parse result
177 * is only pointing to the data payload.
178 */
179 optional_start = in_cr->optional_start + optional_offset;
180 optional_length = msgb_l2len(inpt) - optional_start;
181 if (optional_start + optional_length <= msgb_l2len(inpt)) {
182 target->l3h = msgb_put(target, optional_length);
183 memcpy(target->l3h, inpt->l2h + optional_start, msgb_l3len(target));
184 } else {
185 LOGP(DINP, LOGL_ERROR, "Input should at least have a byte of data.\n");
186 }
187}
188
189/*
190 * Generate a simple UDT msg. FIXME: Merge it with the SCCP code
191 */
192static void create_udt(struct msgb *target, struct msgb *inpt, struct sccp_parse_result *sccp)
193{
194 struct sccp_data_unitdata *udt, *in_udt;
195
196 target->l2h = msgb_put(target, sizeof(*udt));
197 udt = (struct sccp_data_unitdata *) target->l2h;
198 in_udt = (struct sccp_data_unitdata *) inpt->l2h;
199
200 udt->type = in_udt->type;
201 udt->proto_class = in_udt->proto_class;
202 udt->variable_called = 3;
203 udt->variable_calling = 5;
204 udt->variable_data = 7;
205
206 target->l3h = msgb_put(target, 1 + 2);
207 target->l3h[0] = 2;
208 target->l3h[1] = 0x42;
209 target->l3h[2] = 254;
210
211 target->l3h = msgb_put(target, 1 + 2);
212 target->l3h[0] = 2;
213 target->l3h[1] = 0x42;
214 target->l3h[2] = 254;
215
216 target->l3h = msgb_put(target, sccp->data_len + 1);
217 target->l3h[0] = sccp->data_len;
218 memcpy(&target->l3h[1], inpt->l3h, msgb_l3len(target) - 1);
219}
220
221void bss_rewrite_header_for_msc(int rc, struct msgb *target, struct msgb *inpt, struct sccp_parse_result *sccp)
222{
223
224 switch (inpt->l2h[0]) {
225 case SCCP_MSG_TYPE_CR:
226 if (rc >= 0)
227 create_cr(target, inpt, sccp);
228 else
229 target->l2h = msgb_put(target, 0);
230 break;
231 case SCCP_MSG_TYPE_UDT:
232 if (rc >= 0)
233 create_udt(target, inpt, sccp);
234 else
235 target->l2h = msgb_put(target, 0);
236 break;
237 default:
238 target->l2h = msgb_put(target, msgb_l2len(inpt));
239 memcpy(target->l2h, inpt->l2h, msgb_l2len(target));
240 break;
241 }
242}
243
244/* it is asssumed that the SCCP stack checked the size */
Holger Hans Peter Freyther9ed3e1b2010-07-31 05:22:56 +0800245static int patch_address(uint32_t offset, int pc, struct msgb *msg)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800246{
247 struct sccp_called_party_address *party;
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +0800248 uint8_t *the_pc;
249 uint8_t pc_low, pc_high;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800250
251 party = (struct sccp_called_party_address *)(msg->l2h + offset + 1);
252 the_pc = &party->data[0];
253
254 pc_low = pc & 0xff;
255 pc_high = (pc >> 8) & 0xff;
256 the_pc[0] = pc_low;
257 the_pc[1] = pc_high;
258
259 return 0;
260}
261
262int bss_rewrite_header_to_bsc(struct msgb *msg, int opc, int dpc)
263{
Holger Hans Peter Freyther9ed3e1b2010-07-31 05:22:56 +0800264 static const uint32_t called_offset =
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800265 offsetof(struct sccp_data_unitdata, variable_called);
Holger Hans Peter Freyther9ed3e1b2010-07-31 05:22:56 +0800266 static const uint32_t calling_offset =
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800267 offsetof(struct sccp_data_unitdata, variable_calling);
268
269 struct sccp_data_unitdata *udt;
270 struct sccp_parse_result sccp;
271
272 memset(&sccp, 0, sizeof(sccp));
273 if (sccp_parse_header(msg, &sccp) != 0) {
274 LOGP(DMSC, LOGL_ERROR, "Failed to parse SCCP header.\n");
275 return -1;
276 }
277
278 /* For now the MSC only sends the PC in UDT */
279 if (msg->l2h[0] != SCCP_MSG_TYPE_UDT)
280 return 0;
281
282 /* sanity checking */
283 if (sccp.called.address.point_code_indicator != 1) {
284 LOGP(DMSC, LOGL_ERROR, "MSC didn't send a PC in called address\n");
285 return -1;
286 }
287
288 if (sccp.calling.address.point_code_indicator != 1) {
289 LOGP(DMSC, LOGL_ERROR, "MSC didn't send a PC in calling address\n");
290 return -1;
291 }
292
293 /* Good thing is we can avoid most of the error checking */
294 udt = (struct sccp_data_unitdata *) msg->l2h;
295 if (patch_address(called_offset + udt->variable_called, dpc, msg) != 0)
296 return -1;
297
298 if (patch_address(calling_offset + udt->variable_calling, opc, msg) != 0)
299 return -1;
300 return 0;
301}