blob: 99069b44bb3dea28ac0f3ff603ca961390bd8625 [file] [log] [blame]
Matthew Fredrickson124c80e2010-02-16 22:01:36 +01001
2/*
3 * minimal standalone network-side lap-d implementation
4 * oystein@homelien.no, 2009
5 */
6
7#include <stdio.h>
8#include <string.h>
9#include <assert.h>
10
11#include "lapd.h"
12#include "openbsc/debug.h"
13
Matthew Fredrickson124c80e2010-02-16 22:01:36 +010014typedef enum {
Harald Welte8c911f82011-02-04 20:34:08 +010015 LAPD_TEI_NONE = 0,
Matthew Fredrickson124c80e2010-02-16 22:01:36 +010016
17 LAPD_TEI_ASSIGNED,
18
19 LAPD_TEI_ACTIVE,
20} lapd_tei_state;
21
22const char *lapd_tei_states[] = {
23 "NONE",
24 "ASSIGNED",
25 "ACTIVE",
26};
27
28typedef enum {
Harald Welte8c911f82011-02-04 20:34:08 +010029 LAPD_TYPE_NONE = 0,
Matthew Fredrickson124c80e2010-02-16 22:01:36 +010030
31 LAPD_TYPE_I,
32 LAPD_TYPE_S,
33 LAPD_TYPE_U,
34} lapd_msg_type;
35
36typedef enum {
Harald Welte98d01032011-02-04 20:36:50 +010037 /* commands/responses */
Matthew Fredrickson124c80e2010-02-16 22:01:36 +010038 LAPD_CMD_NONE = 0,
39
40 LAPD_CMD_I,
41 LAPD_CMD_RR,
42 LAPD_CMD_RNR,
43 LAPD_CMD_REJ,
44
45 LAPD_CMD_SABME,
46 LAPD_CMD_DM,
47 LAPD_CMD_UI,
48 LAPD_CMD_DISC,
49 LAPD_CMD_UA,
50 LAPD_CMD_FRMR,
51 LAPD_CMD_XID,
52} lapd_cmd_type;
53
54const char *lapd_cmd_types[] = {
55 "NONE",
56
57 "I",
58 "RR",
59 "RNR",
60 "REJ",
61
62 "SABME",
63 "DM",
64 "UI",
65 "DISC",
66 "UA",
67 "FRMR",
68 "XID",
69
Matthew Fredrickson124c80e2010-02-16 22:01:36 +010070};
71
Harald Welte8c911f82011-02-04 20:34:08 +010072const char *lapd_msg_types = "?ISU";
Harald Welte98d01032011-02-04 20:36:50 +010073const int network_side = 1; /* 0 for user side */
Matthew Fredrickson124c80e2010-02-16 22:01:36 +010074
75typedef struct {
Harald Welte8c911f82011-02-04 20:34:08 +010076 int tei;
77 int sapi;
Harald Welte98d01032011-02-04 20:36:50 +010078 /* A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤ V(S). */
79 int vs; /* next to be transmitted */
80 int va; /* last acked by peer */
81 int vr; /* next expected to be received */
Harald Welte8c911f82011-02-04 20:34:08 +010082 lapd_tei_state state;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +010083} lapd_tei_t;
84
Harald Welte98d01032011-02-04 20:36:50 +010085/* 3.5.2.2 Send state variable V(S)
86 * Each point-to-point data link connection endpoint shall have an associated V(S) when using I frame
87 * commands. V(S) denotes the sequence number of the next I frame to be transmitted. The V(S) can
88 * take on the value 0 through n minus 1. The value of V(S) shall be incremented by 1 with each
89 * successive I frame transmission, and shall not exceed V(A) by more than the maximum number of
90 * outstanding I frames k. The value of k may be in the range of 1 ≤ k ≤ 127.
91 *
92 * 3.5.2.3 Acknowledge state variable V(A)
93 * Each point-to-point data link connection endpoint shall have an associated V(A) when using I frame
94 * commands and supervisory frame commands/responses. V(A) identifies the last I frame that has been
95 * acknowledged by its peer [V(A) − 1 equals the N(S) of the last acknowledged I frame]. V(A) can
96 * take on the value 0 through n minus 1. The value of V(A) shall be updated by the valid N(R) values
97 * received from its peer (see 3.5.2.6). A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤
98 * V(S).
99 *
100 * 3.5.2.5 Receive state variable V(R)
101 * Each point-to-point data link connection endpoint shall have an associated V(R) when using I frame
102 * commands and supervisory frame commands/responses. V(R) denotes the sequence number of the
103 * next in-sequence I frame expected to be received. V(R) can take on the value 0 through n minus 1.
104 * The value of V(R) shall be incremented by one with the receipt of an error-free, in-sequence I frame
105 * whose N(S) equals V(R).
106 */
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100107#define LAPD_NS(teip) (teip->vs)
108#define LAPD_NR(teip) (teip->vr)
109
Harald Welte98d01032011-02-04 20:36:50 +0100110/* 3.5.2.4 Send sequence number N(S)
111 * Only I frames contain N(S), the send sequence number of transmitted I frames. At the time that an in-
112 * sequence I frame is designated for transmission, the value of N(S) is set equal to V(S).
113 *
114 * 3.5.2.6 Receive sequence number N(R)
115 * All I frames and supervisory frames contain N(R), the expected send sequence number of the next
116 * received I frame. At the time that a frame of the above types is designated for transmission, the value
117 * of N(R) is set equal to V(R). N(R) indicates that the data link layer entity transmitting the N(R) has
118 * correctly received all I frames numbered up to and including N(R) − 1.
119 */
Harald Welte8c911f82011-02-04 20:34:08 +0100120void (*lapd_transmit_cb) (uint8_t * data, int len, void *cbdata);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100121
122static lapd_tei_t tei_list[] = {
Harald Welte8c911f82011-02-04 20:34:08 +0100123 {25, 62,},
124 {1, 0,},
125 {-1},
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100126};
127
Harald Weltebdc11182011-02-04 20:38:46 +0100128static lapd_tei_t *teip_from_tei(int tei)
Harald Welte8c911f82011-02-04 20:34:08 +0100129{
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100130 lapd_tei_t *p;
131 for (p = tei_list; p->tei != -1; p++) {
Harald Welte8c911f82011-02-04 20:34:08 +0100132 if (p->tei == tei)
133 return p;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100134 };
135 return NULL;
136};
137
Harald Weltebdc11182011-02-04 20:38:46 +0100138static void lapd_tei_set_state(lapd_tei_t * teip, int newstate)
Harald Welte8c911f82011-02-04 20:34:08 +0100139{
Harald Welte1b2494d2011-02-05 17:16:26 +0100140 DEBUGP(DMI, "state change on tei %d: %s -> %s\n", teip->tei,
Harald Welte8c911f82011-02-04 20:34:08 +0100141 lapd_tei_states[teip->state], lapd_tei_states[newstate]);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100142 teip->state = newstate;
143};
144
Harald Weltebdc11182011-02-04 20:38:46 +0100145static void lapd_tei_receive(uint8_t * data, int len, void *cbdata)
Harald Welte8c911f82011-02-04 20:34:08 +0100146{
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100147 int entity = data[0];
Harald Welte8c911f82011-02-04 20:34:08 +0100148 int ref = data[1];
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100149 int mt = data[3];
Harald Welte8c911f82011-02-04 20:34:08 +0100150 int action = data[4] >> 1;
151 int e = data[4] & 1;
Harald Welte1b2494d2011-02-05 17:16:26 +0100152 DEBUGP(DMI, "tei mgmt: entity %x, ref %x, mt %x, action %x, e %x\n", entity, ref, mt, action, e);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100153
154 switch (mt) {
Harald Welte8c911f82011-02-04 20:34:08 +0100155 case 0x01:{ // identity request
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100156 int tei = action;
Harald Welte9be54032011-02-05 17:29:05 +0100157 uint8_t resp[8];
158 lapd_tei_t *teip;
159
Harald Welte1b2494d2011-02-05 17:16:26 +0100160 DEBUGP(DMI, "tei mgmt: identity request, accepting "
161 "tei %d\n", tei);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100162 memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8);
163 resp[7] = (tei << 1) | 1;
164 lapd_transmit_cb(resp, 8, cbdata);
Harald Welte9be54032011-02-05 17:29:05 +0100165
166 teip = teip_from_tei(tei);
167 if (!teip) {
168 LOGP(DMI, LOGL_NOTICE, "Message for unknown "
169 "TEI %u, LAPD code supports only "
170 "TEI 25, 1, 2\n", tei);
171 return;
172 }
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100173 if (teip->state == LAPD_TEI_NONE)
174 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
Harald Welte8c911f82011-02-04 20:34:08 +0100175 break;
176 }
177 default:
Harald Weltea91e1c82011-02-05 18:33:12 +0100178 LOGP(DMI, LOGL_NOTICE, "tei mgmt: unknown mt %x action %x\n",
179 mt, action);
180 break;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100181 };
182};
183
Harald Welte8c911f82011-02-04 20:34:08 +0100184uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim,
185 void *cbdata)
186{
Harald Welte9be54032011-02-05 17:29:05 +0100187 uint8_t sapi, cr, tei, command;
188 int pf, ns, nr;
189 uint8_t *contents;
190 lapd_tei_t *teip;
191
192 uint8_t resp[8];
193 int l = 0;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100194
195 *ilen = 0;
196 *prim = 0;
197
198 if (len < 2) {
Harald Welte1b2494d2011-02-05 17:16:26 +0100199 DEBUGP(DMI, "len %d < 2\n", len);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100200 return NULL;
201 };
202
203 if ((data[0] & 1) != 0 || (data[1] & 1) != 1) {
Harald Welte1b2494d2011-02-05 17:16:26 +0100204 DEBUGP(DMI, "address field %x/%x not well formed\n", data[0],
Harald Welte8c911f82011-02-04 20:34:08 +0100205 data[1]);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100206 return NULL;
207 };
208
Harald Welte9be54032011-02-05 17:29:05 +0100209 sapi = data[0] >> 2;
210 cr = (data[0] >> 1) & 1;
211 tei = data[1] >> 1;
212 command = network_side ^ cr;
Harald Welte1b2494d2011-02-05 17:16:26 +0100213 //DEBUGP(DMI, " address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100214
215 if (len < 3) {
Harald Welte1b2494d2011-02-05 17:16:26 +0100216 DEBUGP(DMI, "len %d < 3\n", len);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100217 return NULL;
218 };
219
220 lapd_msg_type typ = 0;
221 lapd_cmd_type cmd = 0;
Harald Welte9be54032011-02-05 17:29:05 +0100222 pf = -1;
223 ns = -1;
224 nr = -1;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100225 if ((data[2] & 1) == 0) {
226 typ = LAPD_TYPE_I;
227 assert(len >= 4);
228 ns = data[2] >> 1;
229 nr = data[3] >> 1;
230 pf = data[3] & 1;
231 cmd = LAPD_CMD_I;
232 } else if ((data[2] & 3) == 1) {
233 typ = LAPD_TYPE_S;
234 assert(len >= 4);
235 nr = data[3] >> 1;
236 pf = data[3] & 1;
237 switch (data[2]) {
Harald Welte8c911f82011-02-04 20:34:08 +0100238 case 0x1:
239 cmd = LAPD_CMD_RR;
240 break;
241 case 0x5:
242 cmd = LAPD_CMD_RNR;
243 break;
244 case 0x9:
245 cmd = LAPD_CMD_REJ;
246 break;
247 default:
Harald Weltea91e1c82011-02-05 18:33:12 +0100248 LOGP(DMI, LOGL_ERROR, "unknown LAPD S cmd %x\n", data[2]);
249 return NULL;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100250 };
251 } else if ((data[2] & 3) == 3) {
252 typ = LAPD_TYPE_U;
253 pf = (data[2] >> 4) & 1;
Harald Welte8c911f82011-02-04 20:34:08 +0100254 int val = data[2] & ~(1 << 4);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100255 switch (val) {
Harald Welte8c911f82011-02-04 20:34:08 +0100256 case 0x6f:
257 cmd = LAPD_CMD_SABME;
258 break;
259 case 0x0f:
260 cmd = LAPD_CMD_DM;
261 break;
262 case 0x03:
263 cmd = LAPD_CMD_UI;
264 break;
265 case 0x43:
266 cmd = LAPD_CMD_DISC;
267 break;
268 case 0x63:
269 cmd = LAPD_CMD_UA;
270 break;
271 case 0x87:
272 cmd = LAPD_CMD_FRMR;
273 break;
274 case 0xaf:
275 cmd = LAPD_CMD_XID;
276 break;
277
278 default:
Harald Weltea91e1c82011-02-05 18:33:12 +0100279 LOGP(DMI, LOGL_ERROR, "unknown U cmd %x "
280 "(pf %x data %x)\n", val, pf, data[2]);
281 return NULL;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100282 };
283 };
Harald Welte8c911f82011-02-04 20:34:08 +0100284
Harald Welte9be54032011-02-05 17:29:05 +0100285 contents = &data[4];
Harald Welte8c911f82011-02-04 20:34:08 +0100286 if (typ == LAPD_TYPE_U)
287 contents--;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100288 *ilen = len - (contents - data);
Harald Welte8c911f82011-02-04 20:34:08 +0100289
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100290 if (tei == 127)
291 lapd_tei_receive(contents, *ilen, cbdata);
292
Harald Welte9be54032011-02-05 17:29:05 +0100293 teip = teip_from_tei(tei);
294
Harald Welte1b2494d2011-02-05 17:16:26 +0100295 DEBUGP(DMI, "<- %c %s sapi %x tei %3d cmd %x pf %x ns %3d nr %3d "
296 "ilen %d teip %p vs %d va %d vr %d len %d\n",
Harald Welte8c911f82011-02-04 20:34:08 +0100297 lapd_msg_types[typ], lapd_cmd_types[cmd], sapi, tei, command, pf,
298 ns, nr, *ilen, teip, teip ? teip->vs : -1, teip ? teip->va : -1,
299 teip ? teip->vr : -1, len);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100300
Harald Welte9be54032011-02-05 17:29:05 +0100301 if (!teip) {
302 LOGP(DMI, LOGL_NOTICE, "Unknown TEI %u\n", tei);
303 return NULL;
304 }
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100305
Harald Welte9be54032011-02-05 17:29:05 +0100306 switch (cmd) {
307 case LAPD_CMD_I:
308 if (ns != teip->vr) {
309 DEBUGP(DMI, "ns %d != vr %d\n", ns, teip->vr);
310 if (ns == ((teip->vr - 1) & 0x7f)) {
311 DEBUGP(DMI, "DOUBLE FRAME, ignoring\n");
312 cmd = 0; // ignore
313 } else {
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100314 assert(0);
Harald Welte9be54032011-02-05 17:29:05 +0100315 };
316 } else {
317 //printf("IN SEQUENCE\n");
318 teip->vr = (ns + 1) & 0x7f; // FIXME: hack!
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100319 };
Harald Welte9be54032011-02-05 17:29:05 +0100320
321 break;
322 case LAPD_CMD_UI:
323 break;
324 case LAPD_CMD_SABME:
325 teip->vs = 0;
326 teip->vr = 0;
327 teip->va = 0;
328
329 // ua
330 resp[l++] = data[0];
331 resp[l++] = (tei << 1) | 1;
332 resp[l++] = 0x73;
333 lapd_transmit_cb(resp, l, cbdata);
334 if (teip->state != LAPD_TEI_ACTIVE) {
335 if (teip->state == LAPD_TEI_ASSIGNED) {
336 lapd_tei_set_state(teip,
337 LAPD_TEI_ACTIVE);
338 //printf("ASSIGNED and ACTIVE\n");
339 } else {
340#if 0
341 DEBUGP(DMI, "rr in strange state, send rej\n");
342
343 // rej
344 resp[l++] = (teip-> sapi << 2) | (network_side ? 0 : 2);
345 resp[l++] = (tei << 1) | 1;
346 resp[l++] = 0x09; //rej
347 resp[l++] = ((teip->vr + 1) << 1) | 0;
348 lapd_transmit_cb(resp, l, cbdata);
349 pf = 0; // dont reply
350#endif
351 };
352 };
353
354 *prim = LAPD_MPH_ACTIVATE_IND;
355 break;
356 case LAPD_CMD_RR:
357 teip->va = (nr & 0x7f);
358#if 0
359 if (teip->state != LAPD_TEI_ACTIVE) {
360 if (teip->state == LAPD_TEI_ASSIGNED) {
361 lapd_tei_set_state(teip, LAPD_TEI_ACTIVE);
362 *prim = LAPD_MPH_ACTIVATE_IND;
363 //printf("ASSIGNED and ACTIVE\n");
364 } else {
365#if 0
366 DEBUGP(DMI, "rr in strange " "state, send rej\n");
367
368 // rej
369 resp[l++] = (teip-> sapi << 2) | (network_side ? 0 : 2);
370 resp[l++] = (tei << 1) | 1;
371 resp[l++] = 0x09; //rej
372 resp[l++] =
373 ((teip->vr + 1) << 1) | 0;
374 lapd_transmit_cb(resp, l,
375 cbdata);
376 pf = 0; // dont reply
377#endif
378 };
379 };
380#endif
381 if (pf) {
382 // interrogating us, send rr
383 resp[l++] = data[0];
384 resp[l++] = (tei << 1) | 1;
385 resp[l++] = 0x01; // rr
386 resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
387
388 lapd_transmit_cb(resp, l, cbdata);
389
390 };
391 break;
392 case LAPD_CMD_FRMR:
393 // frame reject
394#if 0
395 if (teip->state == LAPD_TEI_ACTIVE)
396 *prim = LAPD_MPH_DEACTIVATE_IND;
397 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
398#endif
Harald Weltea91e1c82011-02-05 18:33:12 +0100399 LOGP(DMI, LOGL_NOTICE, "frame reject, ignoring\n");
Harald Welte9be54032011-02-05 17:29:05 +0100400 break;
401 case LAPD_CMD_DISC:
402 // disconnect
403 resp[l++] = data[0];
404 resp[l++] = (tei << 1) | 1;
405 resp[l++] = 0x73;
406 lapd_transmit_cb(resp, l, cbdata);
407 lapd_tei_set_state(teip, LAPD_TEI_NONE);
408 break;
409 default:
Harald Weltea91e1c82011-02-05 18:33:12 +0100410 LOGP(DMI, LOGL_NOTICE, "unknown cmd for tei %d (cmd %x)\n",
411 tei, cmd);
412 break;
Harald Welte9be54032011-02-05 17:29:05 +0100413 }
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100414
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100415 if (typ == LAPD_TYPE_I) {
Harald Weltea91e1c82011-02-05 18:33:12 +0100416 /* send rr
417 * Thu Jan 22 19:17:13 2009 <4000> sangoma.c:340 read (62/25) 4: fa 33 01 0a
418 * lapd <- S RR sapi 3e tei 25 cmd 0 pf 0 ns -1 nr 5 ilen 0 teip 0x613800 vs 7 va 5 vr 2 len 4
419 */
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100420
Harald Weltea91e1c82011-02-05 18:33:12 +0100421 /* interrogating us, send rr */
Harald Welte1b2494d2011-02-05 17:16:26 +0100422 DEBUGP(DMI, "Sending RR response\n");
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100423 resp[l++] = data[0];
424 resp[l++] = (tei << 1) | 1;
Harald Welte8c911f82011-02-04 20:34:08 +0100425 resp[l++] = 0x01; // rr
426 resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100427
428 lapd_transmit_cb(resp, l, cbdata);
429
Matthew Fredricksoneb3459e2010-03-15 12:24:39 -0500430 if (cmd != 0) {
431 *prim = LAPD_DL_DATA_IND;
432 return contents;
433 }
434 } else if (tei != 127 && typ == LAPD_TYPE_U && cmd == LAPD_CMD_UI) {
435 *prim = LAPD_DL_UNITDATA_IND;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100436 return contents;
Matthew Fredricksoneb3459e2010-03-15 12:24:39 -0500437 }
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100438
439 return NULL;
440};
441
Harald Welte8c911f82011-02-04 20:34:08 +0100442void lapd_transmit(int tei, uint8_t * data, int len, void *cbdata)
443{
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100444 //printf("lapd_transmit %d, %d\n", tei, len);
445 //hexdump(data, len);
446 lapd_tei_t *teip = teip_from_tei(tei);
447 //printf("teip %p\n", teip);
448
Harald Weltea91e1c82011-02-05 18:33:12 +0100449 /* prepend stuff */
Harald Welte8c911f82011-02-04 20:34:08 +0100450 uint8_t buf[10000];
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100451 memset(buf, 0, sizeof(buf));
Harald Welte8c911f82011-02-04 20:34:08 +0100452 memmove(buf + 4, data, len);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100453 len += 4;
454
455 buf[0] = (teip->sapi << 2) | (network_side ? 2 : 0);
456 buf[1] = (teip->tei << 1) | 1;
457 buf[2] = (LAPD_NS(teip) << 1);
458 buf[3] = (LAPD_NR(teip) << 1) | 0;
459
460 teip->vs = (teip->vs + 1) & 0x7f;
461
462 lapd_transmit_cb(buf, len, cbdata);
463};