blob: 3f74547a426ab652564cd08cf4c5f32029963b8f [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 *
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +01007 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080010 * (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
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +010015 * GNU Affero General Public License for more details.
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080016 *
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +010017 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080019 *
20 */
21
22#include <bss_patch.h>
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080023#include <cellmgr_debug.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080024
25#include <string.h>
26
Holger Hans Peter Freythere66c7c12010-08-04 18:51:16 +080027#include <osmocore/gsm0808.h>
28#include <osmocore/protocol/gsm_08_08.h>
29
Holger Hans Peter Freythercf381e22010-08-04 18:39:26 +080030#include <osmocom/sccp/sccp.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080031
32#include <arpa/inet.h>
33
34static void patch_ass_rqst(struct msgb *msg, int length)
35{
36 struct tlv_parsed tp;
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080037 uint8_t *data, audio;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080038 int len;
39
40 tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, length - 1, 0, 0);
41 len = TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE);
42 if (len < 3)
43 return;
44
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080045 data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080046 /* no speech... ignore */
47 if ((data[0] & 0xf) != 0x1)
48 return;
49
50 /* blindly assign */
51 data[1] = GSM0808_SPEECH_FULL_PREF;
52 audio = GSM0808_PERM_FR2;
53 if (len > 3)
54 audio |= 0x80;
55 data[2] = audio;
56}
57
58static void patch_ass_cmpl(struct msgb *msg, int length)
59{
60 struct tlv_parsed tp;
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080061 uint8_t *data;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080062
Holger Hans Peter Freytherd25dfce2010-10-27 15:59:33 +020063 if (length == 1) {
64 LOGP(DMSC, LOGL_ERROR, "Hacking the Assignment Complete.\n");
65 msgb_v_put(msg, 0x21);
66 msgb_v_put(msg, 0x09);
67 msgb_v_put(msg, 0x2c);
68 msgb_v_put(msg, 0x02);
69 msgb_v_put(msg, 0x40);
70 msgb_v_put(msg, 0x25);
71 msg->l3h[-1] = 7;
72 msg->l3h[-3] = 9;
73 return;
74 }
75
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080076 tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, length - 1, 0, 0);
77 if (!TLVP_PRESENT(&tp, GSM0808_IE_CHOSEN_CHANNEL)) {
78 LOGP(DMSC, LOGL_ERROR, "Chosen Channel not in the MSG.\n");
79 return;
80 }
81
82 if (!TLVP_PRESENT(&tp, GSM0808_IE_SPEECH_VERSION)) {
83 LOGP(DMSC, LOGL_ERROR, "Speech version not in the MSG.\n");
84 return;
85 }
86
87 /* claim to have a TCH/H with no mode indication */
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080088 data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHOSEN_CHANNEL);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080089 data[0] = 0x09;
90
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080091 data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_SPEECH_VERSION);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080092 data[0] = GSM0808_PERM_HR3;
93}
94
95int bss_patch_filter_msg(struct msgb *msg, struct sccp_parse_result *sccp)
96{
97 int type;
98 memset(sccp, 0, sizeof(*sccp));
99 if (sccp_parse_header(msg, sccp) != 0) {
100 LOGP(DMSC, LOGL_ERROR, "Failed to parse SCCP header.\n");
101 return -1;
102 }
103
104 type = sccp_determine_msg_type(msg);
105 switch (type) {
106 case SCCP_MSG_TYPE_CR:
107 if (msg->l3h)
108 break;
109 return 0;
110 break;
111 case SCCP_MSG_TYPE_CC:
112 case SCCP_MSG_TYPE_CREF:
113 return 0;
114 break;
115 case SCCP_MSG_TYPE_RLC:
116 return BSS_FILTER_RLC;
117 break;
118 case SCCP_MSG_TYPE_RLSD:
119 return BSS_FILTER_RLSD;
120 break;
121 }
122
123 if (msgb_l3len(msg) < sccp->data_len) {
124 LOGP(DMSC, LOGL_ERROR, "Less space than there should be.\n");
125 return -1;
126 }
127
128 if (!msg->l3h || msgb_l3len(msg) < 3) {
129 return -1;
130 }
131
132 if (msg->l3h[0] != 0) {
133 return -1;
134 }
135
136 if (msgb_l3len(msg) < 2 + msg->l3h[1]) {
137 return -1;
138 }
139
140 switch (msg->l3h[2]) {
141 case BSS_MAP_MSG_ASSIGMENT_RQST:
142 msg->l3h = &msg->l3h[2];
143 patch_ass_rqst(msg, sccp->data_len - 2);
144 break;
145 case BSS_MAP_MSG_ASSIGMENT_COMPLETE:
146 msg->l3h = &msg->l3h[2];
147 patch_ass_cmpl(msg, sccp->data_len - 2);
148 break;
149 case BSS_MAP_MSG_RESET:
150 return BSS_FILTER_RESET;
151 break;
152 case BSS_MAP_MSG_RESET_ACKNOWLEDGE:
153 return BSS_FILTER_RESET_ACK;
154 break;
155 case BSS_MAP_MSG_CLEAR_COMPLETE:
156 return BSS_FILTER_CLEAR_COMPL;
157 break;
158 }
159
160 return 0;
161}
162
163static void create_cr(struct msgb *target, struct msgb *inpt, struct sccp_parse_result *sccp)
164{
Holger Hans Peter Freyther9ed3e1b2010-07-31 05:22:56 +0800165 static const uint32_t optional_offset =
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800166 offsetof(struct sccp_connection_request, optional_start);
167
168 unsigned int optional_length, optional_start;
169 struct sccp_connection_request *cr, *in_cr;
170
171 target->l2h = msgb_put(target, sizeof(*cr));
172 cr = (struct sccp_connection_request *) target->l2h;
173 in_cr = (struct sccp_connection_request *) inpt->l2h;
174
175 cr->type = in_cr->type;
176 cr->proto_class = in_cr->proto_class;
177 cr->source_local_reference = in_cr->source_local_reference;
178 cr->variable_called = 2;
179 cr->optional_start = 4;
180
181 /* called address */
182 target->l3h = msgb_put(target, 1 + 2);
183 target->l3h[0] = 2;
184 target->l3h[1] = 0x42;
185 target->l3h[2] = 254;
186
187 /*
188 * We need to keep the complete optional data. The SCCP parse result
189 * is only pointing to the data payload.
190 */
191 optional_start = in_cr->optional_start + optional_offset;
192 optional_length = msgb_l2len(inpt) - optional_start;
193 if (optional_start + optional_length <= msgb_l2len(inpt)) {
194 target->l3h = msgb_put(target, optional_length);
195 memcpy(target->l3h, inpt->l2h + optional_start, msgb_l3len(target));
196 } else {
197 LOGP(DINP, LOGL_ERROR, "Input should at least have a byte of data.\n");
198 }
199}
200
201/*
202 * Generate a simple UDT msg. FIXME: Merge it with the SCCP code
203 */
204static void create_udt(struct msgb *target, struct msgb *inpt, struct sccp_parse_result *sccp)
205{
206 struct sccp_data_unitdata *udt, *in_udt;
207
208 target->l2h = msgb_put(target, sizeof(*udt));
209 udt = (struct sccp_data_unitdata *) target->l2h;
210 in_udt = (struct sccp_data_unitdata *) inpt->l2h;
211
212 udt->type = in_udt->type;
213 udt->proto_class = in_udt->proto_class;
214 udt->variable_called = 3;
215 udt->variable_calling = 5;
216 udt->variable_data = 7;
217
218 target->l3h = msgb_put(target, 1 + 2);
219 target->l3h[0] = 2;
220 target->l3h[1] = 0x42;
221 target->l3h[2] = 254;
222
223 target->l3h = msgb_put(target, 1 + 2);
224 target->l3h[0] = 2;
225 target->l3h[1] = 0x42;
226 target->l3h[2] = 254;
227
228 target->l3h = msgb_put(target, sccp->data_len + 1);
229 target->l3h[0] = sccp->data_len;
230 memcpy(&target->l3h[1], inpt->l3h, msgb_l3len(target) - 1);
231}
232
233void bss_rewrite_header_for_msc(int rc, struct msgb *target, struct msgb *inpt, struct sccp_parse_result *sccp)
234{
235
236 switch (inpt->l2h[0]) {
237 case SCCP_MSG_TYPE_CR:
238 if (rc >= 0)
239 create_cr(target, inpt, sccp);
240 else
241 target->l2h = msgb_put(target, 0);
242 break;
243 case SCCP_MSG_TYPE_UDT:
244 if (rc >= 0)
245 create_udt(target, inpt, sccp);
246 else
247 target->l2h = msgb_put(target, 0);
248 break;
249 default:
250 target->l2h = msgb_put(target, msgb_l2len(inpt));
251 memcpy(target->l2h, inpt->l2h, msgb_l2len(target));
252 break;
253 }
254}
255
256/* it is asssumed that the SCCP stack checked the size */
Holger Hans Peter Freyther9ed3e1b2010-07-31 05:22:56 +0800257static int patch_address(uint32_t offset, int pc, struct msgb *msg)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800258{
259 struct sccp_called_party_address *party;
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +0800260 uint8_t *the_pc;
261 uint8_t pc_low, pc_high;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800262
263 party = (struct sccp_called_party_address *)(msg->l2h + offset + 1);
264 the_pc = &party->data[0];
265
266 pc_low = pc & 0xff;
267 pc_high = (pc >> 8) & 0xff;
268 the_pc[0] = pc_low;
269 the_pc[1] = pc_high;
270
271 return 0;
272}
273
274int bss_rewrite_header_to_bsc(struct msgb *msg, int opc, int dpc)
275{
Holger Hans Peter Freyther9ed3e1b2010-07-31 05:22:56 +0800276 static const uint32_t called_offset =
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800277 offsetof(struct sccp_data_unitdata, variable_called);
Holger Hans Peter Freyther9ed3e1b2010-07-31 05:22:56 +0800278 static const uint32_t calling_offset =
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800279 offsetof(struct sccp_data_unitdata, variable_calling);
280
281 struct sccp_data_unitdata *udt;
282 struct sccp_parse_result sccp;
283
284 memset(&sccp, 0, sizeof(sccp));
285 if (sccp_parse_header(msg, &sccp) != 0) {
286 LOGP(DMSC, LOGL_ERROR, "Failed to parse SCCP header.\n");
287 return -1;
288 }
289
290 /* For now the MSC only sends the PC in UDT */
291 if (msg->l2h[0] != SCCP_MSG_TYPE_UDT)
292 return 0;
293
294 /* sanity checking */
295 if (sccp.called.address.point_code_indicator != 1) {
296 LOGP(DMSC, LOGL_ERROR, "MSC didn't send a PC in called address\n");
297 return -1;
298 }
299
300 if (sccp.calling.address.point_code_indicator != 1) {
301 LOGP(DMSC, LOGL_ERROR, "MSC didn't send a PC in calling address\n");
302 return -1;
303 }
304
305 /* Good thing is we can avoid most of the error checking */
306 udt = (struct sccp_data_unitdata *) msg->l2h;
307 if (patch_address(called_offset + udt->variable_called, dpc, msg) != 0)
308 return -1;
309
310 if (patch_address(calling_offset + udt->variable_calling, opc, msg) != 0)
311 return -1;
312 return 0;
313}