gsm0808: Import unaligned mem access fix from on-waves/bsc-master
[osmocom-bb.git] / src / gsm0808.c
1 /* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org>
2  * (C) 2009,2010 by On-Waves
3  * All Rights Reserved
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  */
20
21 #include <osmocore/gsm0808.h>
22 #include <osmocore/protocol/gsm_08_08.h>
23 #include <osmocore/gsm48.h>
24
25 #include <arpa/inet.h>
26
27 #define BSSMAP_MSG_SIZE 512
28 #define BSSMAP_MSG_HEADROOM 128
29
30 static void put_data_16(uint8_t *data, const uint16_t val)
31 {
32         memcpy(data, &val, sizeof(val));
33 }
34
35 struct msgb *gsm0808_create_layer3(struct msgb *msg_l3, uint16_t nc, uint16_t cc, int lac, uint16_t _ci)
36 {
37         uint8_t *data;
38         uint8_t *ci;
39         struct msgb* msg;
40         struct gsm48_loc_area_id *lai;
41
42         msg  = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
43                                    "bssmap cmpl l3");
44         if (!msg)
45                 return NULL;
46
47         /* create the bssmap header */
48         msg->l3h = msgb_put(msg, 2);
49         msg->l3h[0] = 0x0;
50
51         /* create layer 3 header */
52         data = msgb_put(msg, 1);
53         data[0] = BSS_MAP_MSG_COMPLETE_LAYER_3;
54
55         /* create the cell header */
56         data = msgb_put(msg, 3);
57         data[0] = GSM0808_IE_CELL_IDENTIFIER;
58         data[1] = 1 + sizeof(*lai) + 2;
59         data[2] = CELL_IDENT_WHOLE_GLOBAL;
60
61         lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai));
62         gsm48_generate_lai(lai, cc, nc, lac);
63
64         ci = msgb_put(msg, 2);
65         put_data_16(ci, htons(_ci));
66
67         /* copy the layer3 data */
68         data = msgb_put(msg, msgb_l3len(msg_l3) + 2);
69         data[0] = GSM0808_IE_LAYER_3_INFORMATION;
70         data[1] = msgb_l3len(msg_l3);
71         memcpy(&data[2], msg_l3->l3h, data[1]);
72
73         /* update the size */
74         msg->l3h[1] = msgb_l3len(msg) - 2;
75
76         return msg;
77 }
78
79 struct msgb *gsm0808_create_reset(void)
80 {
81         struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
82                                                "bssmap: reset");
83         if (!msg)
84                 return NULL;
85
86         msg->l3h = msgb_put(msg, 6);
87         msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
88         msg->l3h[1] = 0x04;
89         msg->l3h[2] = 0x30;
90         msg->l3h[3] = 0x04;
91         msg->l3h[4] = 0x01;
92         msg->l3h[5] = 0x20;
93         return msg;
94 }
95
96 struct msgb *gsm0808_create_clear_complete(void)
97 {
98         struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
99                                                "bssmap: clear complete");
100         if (!msg)
101                 return NULL;
102
103         msg->l3h = msgb_put(msg, 3);
104         msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
105         msg->l3h[1] = 1;
106         msg->l3h[2] = BSS_MAP_MSG_CLEAR_COMPLETE;
107
108         return msg;
109 }
110
111 struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id)
112 {
113         struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
114                                                "cipher-complete");
115         if (!msg)
116                 return NULL;
117
118         /* send response with BSS override for A5/1... cheating */
119         msg->l3h = msgb_put(msg, 3);
120         msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
121         msg->l3h[1] = 0xff;
122         msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_COMPLETE;
123
124         /* include layer3 in case we have at least two octets */
125         if (layer3 && msgb_l3len(layer3) > 2) {
126                 msg->l4h = msgb_put(msg, msgb_l3len(layer3) + 2);
127                 msg->l4h[0] = GSM0808_IE_LAYER_3_MESSAGE_CONTENTS;
128                 msg->l4h[1] = msgb_l3len(layer3);
129                 memcpy(&msg->l4h[2], layer3->l3h, msgb_l3len(layer3));
130         }
131
132         /* and the optional BSS message */
133         msg->l4h = msgb_put(msg, 2);
134         msg->l4h[0] = GSM0808_IE_CHOSEN_ENCR_ALG;
135         msg->l4h[1] = alg_id;
136
137         /* update the size */
138         msg->l3h[1] = msgb_l3len(msg) - 2;
139         return msg;
140 }
141
142 struct msgb *gsm0808_create_cipher_reject(uint8_t cause)
143 {
144         struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
145                                                "bssmap: clear complete");
146         if (!msg)
147                 return NULL;
148
149         msg->l3h = msgb_put(msg, 3);
150         msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
151         msg->l3h[1] = 2;
152         msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_REJECT;
153         msg->l3h[3] = cause;
154
155         return msg;
156 }
157
158 struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark_data, uint8_t length)
159 {
160         struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
161                                                "classmark-update");
162         if (!msg)
163                 return NULL;
164
165         msg->l3h = msgb_put(msg, 3);
166         msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
167         msg->l3h[1] = 0xff;
168         msg->l3h[2] = BSS_MAP_MSG_CLASSMARK_UPDATE;
169
170         msg->l4h = msgb_put(msg, length);
171         memcpy(msg->l4h, classmark_data, length);
172
173         /* update the size */
174         msg->l3h[1] = msgb_l3len(msg) - 2;
175         return msg;
176 }
177
178 struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
179 {
180         struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
181                                                "bssmap: sapi 'n' reject");
182         if (!msg)
183                 return NULL;
184
185         msg->l3h = msgb_put(msg, 5);
186         msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
187         msg->l3h[1] = 3;
188         msg->l3h[2] = BSS_MAP_MSG_SAPI_N_REJECT;
189         msg->l3h[3] = link_id;
190         msg->l3h[4] = GSM0808_CAUSE_BSS_NOT_EQUIPPED;
191
192         return msg;
193 }
194
195 struct msgb *gsm0808_create_assignment_completed(struct gsm_lchan *lchan, uint8_t rr_cause,
196                                                  uint8_t chosen_channel, uint8_t encr_alg_id,
197                                                  uint8_t speech_mode)
198 {
199         uint8_t *data;
200
201         struct msgb *msg = msgb_alloc(35, "bssmap: ass compl");
202         if (!msg)
203                 return NULL;
204
205         msg->l3h = msgb_put(msg, 3);
206         msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
207         msg->l3h[1] = 0xff;
208         msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_COMPLETE;
209
210         /* write 3.2.2.22 */
211         data = msgb_put(msg, 2);
212         data[0] = GSM0808_IE_RR_CAUSE;
213         data[1] = rr_cause;
214
215         /* write cirtcuit identity  code 3.2.2.2 */
216         /* write cell identifier 3.2.2.17 */
217         /* write chosen channel 3.2.2.33 when BTS picked it */
218         data = msgb_put(msg, 2);
219         data[0] = GSM0808_IE_CHOSEN_CHANNEL;
220         data[1] = chosen_channel;
221
222         /* write chosen encryption algorithm 3.2.2.44 */
223         data = msgb_put(msg, 2);
224         data[0] = GSM0808_IE_CHOSEN_ENCR_ALG;
225         data[1] = encr_alg_id;
226
227         /* write circuit pool 3.2.2.45 */
228         /* write speech version chosen: 3.2.2.51 when BTS picked it */
229         if (speech_mode != 0) {
230                 data = msgb_put(msg, 2);
231                 data[0] = GSM0808_IE_SPEECH_VERSION;
232                 data[1] = speech_mode;
233         }
234
235         /* write LSA identifier 3.2.2.15 */
236
237
238         /* update the size */
239         msg->l3h[1] = msgb_l3len(msg) - 2;
240         return msg;
241 }
242
243 struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause)
244 {
245         uint8_t *data;
246         struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
247                                                "bssmap: ass fail");
248         if (!msg)
249                 return NULL;
250
251         msg->l3h = msgb_put(msg, 6);
252         msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
253         msg->l3h[1] = 0xff;
254         msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_FAILURE;
255         msg->l3h[3] = GSM0808_IE_CAUSE;
256         msg->l3h[4] = 1;
257         msg->l3h[5] = cause;
258
259         /* RR cause 3.2.2.22 */
260         if (rr_cause) {
261                 data = msgb_put(msg, 2);
262                 data[0] = GSM0808_IE_RR_CAUSE;
263                 data[1] = *rr_cause;
264         }
265
266         /* Circuit pool 3.22.45 */
267         /* Circuit pool list 3.2.2.46 */
268
269         /* update the size */
270         msg->l3h[1] = msgb_l3len(msg) - 2;
271         return msg;
272 }
273
274 void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id)
275 {
276         uint8_t *hh = msgb_push(msg, 3);
277         hh[0] = BSSAP_MSG_DTAP;
278         hh[1] = link_id;
279         hh[2] = msg->len - 3;
280 }
281
282 static const struct tlv_definition bss_att_tlvdef = {
283         .def = {
284                 [GSM0808_IE_IMSI]                   = { TLV_TYPE_TLV },
285                 [GSM0808_IE_TMSI]                   = { TLV_TYPE_TLV },
286                 [GSM0808_IE_CELL_IDENTIFIER_LIST]   = { TLV_TYPE_TLV },
287                 [GSM0808_IE_CHANNEL_NEEDED]         = { TLV_TYPE_TV },
288                 [GSM0808_IE_EMLPP_PRIORITY]         = { TLV_TYPE_TV },
289                 [GSM0808_IE_CHANNEL_TYPE]           = { TLV_TYPE_TLV },
290                 [GSM0808_IE_PRIORITY]               = { TLV_TYPE_TLV },
291                 [GSM0808_IE_CIRCUIT_IDENTITY_CODE]  = { TLV_TYPE_TV },
292                 [GSM0808_IE_DOWNLINK_DTX_FLAG]      = { TLV_TYPE_TV },
293                 [GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV },
294                 [GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV },
295                 [GSM0808_IE_GROUP_CALL_REFERENCE]   = { TLV_TYPE_TLV },
296                 [GSM0808_IE_TALKER_FLAG]            = { TLV_TYPE_T },
297                 [GSM0808_IE_CONFIG_EVO_INDI]        = { TLV_TYPE_TV },
298                 [GSM0808_IE_LSA_ACCESS_CTRL_SUPPR]  = { TLV_TYPE_TV },
299                 [GSM0808_IE_SERVICE_HANDOVER]       = { TLV_TYPE_TV},
300                 [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV },
301                 [GSM0808_IE_CIPHER_RESPONSE_MODE]   = { TLV_TYPE_TV },
302                 [GSM0808_IE_CELL_IDENTIFIER]        = { TLV_TYPE_TLV },
303                 [GSM0808_IE_CHOSEN_CHANNEL]         = { TLV_TYPE_TV },
304                 [GSM0808_IE_LAYER_3_INFORMATION]    = { TLV_TYPE_TLV },
305         },
306 };
307
308 const struct tlv_definition *gsm0808_att_tlvdef()
309 {
310         return &bss_att_tlvdef;
311 }