blob: b1f1a56dd3daaa5a91bf094567677fbef414c62d [file] [log] [blame]
Jacob Erlbeckb492d392014-06-02 10:49:01 +02001/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */
2
3/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
4 *
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 Affero General Public License as published by
9 * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
16 *
17 * 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/>.
19 *
20 */
21
22#include <errno.h>
23#include <stdint.h>
24
25#include <osmocom/core/msgb.h>
26#include <osmocom/core/linuxlist.h>
27#include <osmocom/core/timer.h>
28#include <osmocom/core/talloc.h>
29#include <osmocom/gprs/gprs_bssgp.h>
30
31#include <openbsc/gsm_data.h>
32#include <openbsc/debug.h>
33#include <openbsc/gprs_sgsn.h>
34#include <openbsc/gprs_gmm.h>
35#include <openbsc/gprs_llc.h>
36#include <openbsc/crc24.h>
37
38static const struct value_string llc_cmd_strs[] = {
39 { GPRS_LLC_NULL, "NULL" },
40 { GPRS_LLC_RR, "RR" },
41 { GPRS_LLC_ACK, "ACK" },
42 { GPRS_LLC_RNR, "RNR" },
43 { GPRS_LLC_SACK, "SACK" },
44 { GPRS_LLC_DM, "DM" },
45 { GPRS_LLC_DISC, "DISC" },
46 { GPRS_LLC_UA, "UA" },
47 { GPRS_LLC_SABM, "SABM" },
48 { GPRS_LLC_FRMR, "FRMR" },
49 { GPRS_LLC_XID, "XID" },
50 { GPRS_LLC_UI, "UI" },
51 { 0, NULL }
52};
53
54#define LLC_ALLOC_SIZE 16384
55#define UI_HDR_LEN 3
56#define N202 4
57#define CRC24_LENGTH 3
58
59int gprs_llc_fcs(uint8_t *data, unsigned int len)
60{
61 uint32_t fcs_calc;
62
63 fcs_calc = crc24_calc(INIT_CRC24, data, len);
64 fcs_calc = ~fcs_calc;
65 fcs_calc &= 0xffffff;
66
67 return fcs_calc;
68}
69
70void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph)
71{
72 DEBUGP(DLLC, "LLC SAPI=%u %c %c FCS=0x%06x",
73 gph->sapi, gph->is_cmd ? 'C' : 'R', gph->ack_req ? 'A' : ' ',
74 gph->fcs);
75
76 if (gph->cmd)
77 DEBUGPC(DLLC, "CMD=%s ", get_value_string(llc_cmd_strs, gph->cmd));
78
79 if (gph->data)
80 DEBUGPC(DLLC, "DATA ");
81
82 DEBUGPC(DLLC, "\n");
83}
84
85/* parse a GPRS LLC header, also check for invalid frames */
86int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp,
87 uint8_t *llc_hdr, int len)
88{
89 uint8_t *ctrl = llc_hdr+1;
90
91 if (len <= CRC24_LENGTH)
92 return -EIO;
93
94 ghp->crc_length = len - CRC24_LENGTH;
95
96 ghp->ack_req = 0;
97
98 /* Section 5.5: FCS */
99 ghp->fcs = *(llc_hdr + len - 3);
100 ghp->fcs |= *(llc_hdr + len - 2) << 8;
101 ghp->fcs |= *(llc_hdr + len - 1) << 16;
102
103 /* Section 6.2.1: invalid PD field */
104 if (llc_hdr[0] & 0x80)
105 return -EIO;
106
107 /* This only works for the MS->SGSN direction */
108 if (llc_hdr[0] & 0x40)
109 ghp->is_cmd = 0;
110 else
111 ghp->is_cmd = 1;
112
113 ghp->sapi = llc_hdr[0] & 0xf;
114
115 /* Section 6.2.3: check for reserved SAPI */
116 switch (ghp->sapi) {
117 case 0:
118 case 4:
119 case 6:
120 case 0xa:
121 case 0xc:
122 case 0xd:
123 case 0xf:
124 return -EINVAL;
125 }
126
127 if ((ctrl[0] & 0x80) == 0) {
128 /* I (Information transfer + Supervisory) format */
129 uint8_t k;
130
131 ghp->data = ctrl + 3;
132
133 if (ctrl[0] & 0x40)
134 ghp->ack_req = 1;
135
136 ghp->seq_tx = (ctrl[0] & 0x1f) << 4;
137 ghp->seq_tx |= (ctrl[1] >> 4);
138
139 ghp->seq_rx = (ctrl[1] & 0x7) << 6;
140 ghp->seq_rx |= (ctrl[2] >> 2);
141
142 switch (ctrl[2] & 0x03) {
143 case 0:
144 ghp->cmd = GPRS_LLC_RR;
145 break;
146 case 1:
147 ghp->cmd = GPRS_LLC_ACK;
148 break;
149 case 2:
150 ghp->cmd = GPRS_LLC_RNR;
151 break;
152 case 3:
153 ghp->cmd = GPRS_LLC_SACK;
154 k = ctrl[3] & 0x1f;
155 ghp->data += 1 + k;
156 break;
157 }
158 ghp->data_len = (llc_hdr + len - 3) - ghp->data;
159 } else if ((ctrl[0] & 0xc0) == 0x80) {
160 /* S (Supervisory) format */
161 ghp->data = NULL;
162 ghp->data_len = 0;
163
164 if (ctrl[0] & 0x20)
165 ghp->ack_req = 1;
166 ghp->seq_rx = (ctrl[0] & 0x7) << 6;
167 ghp->seq_rx |= (ctrl[1] >> 2);
168
169 switch (ctrl[1] & 0x03) {
170 case 0:
171 ghp->cmd = GPRS_LLC_RR;
172 break;
173 case 1:
174 ghp->cmd = GPRS_LLC_ACK;
175 break;
176 case 2:
177 ghp->cmd = GPRS_LLC_RNR;
178 break;
179 case 3:
180 ghp->cmd = GPRS_LLC_SACK;
181 break;
182 }
183 } else if ((ctrl[0] & 0xe0) == 0xc0) {
184 /* UI (Unconfirmed Inforamtion) format */
185 ghp->cmd = GPRS_LLC_UI;
186 ghp->data = ctrl + 2;
187 ghp->data_len = (llc_hdr + len - 3) - ghp->data;
188
189 ghp->seq_tx = (ctrl[0] & 0x7) << 6;
190 ghp->seq_tx |= (ctrl[1] >> 2);
191 if (ctrl[1] & 0x02) {
192 ghp->is_encrypted = 1;
193 /* FIXME: encryption */
194 }
195 if (ctrl[1] & 0x01) {
196 /* FCS over hdr + all inf fields */
197 } else {
198 /* FCS over hdr + N202 octets (4) */
199 if (ghp->crc_length > UI_HDR_LEN + N202)
200 ghp->crc_length = UI_HDR_LEN + N202;
201 }
202 } else {
203 /* U (Unnumbered) format: 1 1 1 P/F M4 M3 M2 M1 */
204 ghp->data = NULL;
205 ghp->data_len = 0;
206
207 switch (ctrl[0] & 0xf) {
208 case GPRS_LLC_U_NULL_CMD:
209 ghp->cmd = GPRS_LLC_NULL;
210 break;
211 case GPRS_LLC_U_DM_RESP:
212 ghp->cmd = GPRS_LLC_DM;
213 break;
214 case GPRS_LLC_U_DISC_CMD:
215 ghp->cmd = GPRS_LLC_DISC;
216 break;
217 case GPRS_LLC_U_UA_RESP:
218 ghp->cmd = GPRS_LLC_UA;
219 break;
220 case GPRS_LLC_U_SABM_CMD:
221 ghp->cmd = GPRS_LLC_SABM;
222 break;
223 case GPRS_LLC_U_FRMR_RESP:
224 ghp->cmd = GPRS_LLC_FRMR;
225 break;
226 case GPRS_LLC_U_XID:
227 ghp->cmd = GPRS_LLC_XID;
228 ghp->data = ctrl + 1;
229 ghp->data_len = (llc_hdr + len - 3) - ghp->data;
230 break;
231 default:
232 return -EIO;
233 }
234 }
235
236 /* FIXME: parse sack frame */
237 if (ghp->cmd == GPRS_LLC_SACK) {
238 LOGP(DLLC, LOGL_NOTICE, "Unsupported SACK frame\n");
239 return -EIO;
240 }
241
242 return 0;
243}