blob: bd52cf82e726d966b57e1ed364473f2d4a42a131 [file] [log] [blame]
Matthew Fredricksonbc6649e2010-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 Fredricksonbc6649e2010-02-16 22:01:36 +010014typedef enum {
Harald Welte30fe6412011-02-04 20:34:08 +010015 LAPD_TEI_NONE = 0,
Matthew Fredricksonbc6649e2010-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 Welte30fe6412011-02-04 20:34:08 +010029 LAPD_TYPE_NONE = 0,
Matthew Fredricksonbc6649e2010-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 Welte7e859bc2011-02-04 20:36:50 +010037 /* commands/responses */
Matthew Fredricksonbc6649e2010-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 Fredricksonbc6649e2010-02-16 22:01:36 +010070};
71
Harald Welte30fe6412011-02-04 20:34:08 +010072const char *lapd_msg_types = "?ISU";
Harald Welte7e859bc2011-02-04 20:36:50 +010073const int network_side = 1; /* 0 for user side */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010074
75typedef struct {
Harald Welte30fe6412011-02-04 20:34:08 +010076 int tei;
77 int sapi;
Harald Welte7e859bc2011-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 Welte30fe6412011-02-04 20:34:08 +010082 lapd_tei_state state;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010083} lapd_tei_t;
84
Harald Welte7e859bc2011-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 Fredricksonbc6649e2010-02-16 22:01:36 +0100107#define LAPD_NS(teip) (teip->vs)
108#define LAPD_NR(teip) (teip->vr)
109
Harald Welte7e859bc2011-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 Welte30fe6412011-02-04 20:34:08 +0100120void (*lapd_transmit_cb) (uint8_t * data, int len, void *cbdata);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100121
122static lapd_tei_t tei_list[] = {
Harald Welte30fe6412011-02-04 20:34:08 +0100123 {25, 62,},
124 {1, 0,},
125 {-1},
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100126};
127
Harald Welted2735292011-02-04 20:38:46 +0100128static lapd_tei_t *teip_from_tei(int tei)
Harald Welte30fe6412011-02-04 20:34:08 +0100129{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100130 lapd_tei_t *p;
131 for (p = tei_list; p->tei != -1; p++) {
Harald Welte30fe6412011-02-04 20:34:08 +0100132 if (p->tei == tei)
133 return p;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100134 };
135 return NULL;
136};
137
Harald Welted2735292011-02-04 20:38:46 +0100138static void lapd_tei_set_state(lapd_tei_t * teip, int newstate)
Harald Welte30fe6412011-02-04 20:34:08 +0100139{
Harald Welte0abc11a2011-02-05 17:16:26 +0100140 DEBUGP(DMI, "state change on tei %d: %s -> %s\n", teip->tei,
Harald Welte30fe6412011-02-04 20:34:08 +0100141 lapd_tei_states[teip->state], lapd_tei_states[newstate]);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100142 teip->state = newstate;
143};
144
Harald Welted2735292011-02-04 20:38:46 +0100145static void lapd_tei_receive(uint8_t * data, int len, void *cbdata)
Harald Welte30fe6412011-02-04 20:34:08 +0100146{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100147 int entity = data[0];
Harald Welte30fe6412011-02-04 20:34:08 +0100148 int ref = data[1];
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100149 int mt = data[3];
Harald Welte30fe6412011-02-04 20:34:08 +0100150 int action = data[4] >> 1;
151 int e = data[4] & 1;
Harald Welte0abc11a2011-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 Fredricksonbc6649e2010-02-16 22:01:36 +0100153
154 switch (mt) {
Harald Welte30fe6412011-02-04 20:34:08 +0100155 case 0x01:{ // identity request
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100156 int tei = action;
Harald Welte716d2a42011-02-05 17:29:05 +0100157 uint8_t resp[8];
158 lapd_tei_t *teip;
159
Harald Welte0abc11a2011-02-05 17:16:26 +0100160 DEBUGP(DMI, "tei mgmt: identity request, accepting "
161 "tei %d\n", tei);
Matthew Fredricksonbc6649e2010-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 Welte716d2a42011-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 Fredricksonbc6649e2010-02-16 22:01:36 +0100173 if (teip->state == LAPD_TEI_NONE)
174 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
Harald Welte30fe6412011-02-04 20:34:08 +0100175 break;
176 }
177 default:
Harald Welte0abc11a2011-02-05 17:16:26 +0100178 DEBUGP(DMI, "tei mgmt: unknown mt %x action %x\n", mt, action);
Harald Welte30fe6412011-02-04 20:34:08 +0100179 assert(0);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100180 };
181};
182
Harald Welte30fe6412011-02-04 20:34:08 +0100183uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim,
184 void *cbdata)
185{
Harald Welte716d2a42011-02-05 17:29:05 +0100186 uint8_t sapi, cr, tei, command;
187 int pf, ns, nr;
188 uint8_t *contents;
189 lapd_tei_t *teip;
190
191 uint8_t resp[8];
192 int l = 0;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100193
194 *ilen = 0;
195 *prim = 0;
196
197 if (len < 2) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100198 DEBUGP(DMI, "len %d < 2\n", len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100199 return NULL;
200 };
201
202 if ((data[0] & 1) != 0 || (data[1] & 1) != 1) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100203 DEBUGP(DMI, "address field %x/%x not well formed\n", data[0],
Harald Welte30fe6412011-02-04 20:34:08 +0100204 data[1]);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100205 return NULL;
206 };
207
Harald Welte716d2a42011-02-05 17:29:05 +0100208 sapi = data[0] >> 2;
209 cr = (data[0] >> 1) & 1;
210 tei = data[1] >> 1;
211 command = network_side ^ cr;
Harald Welte0abc11a2011-02-05 17:16:26 +0100212 //DEBUGP(DMI, " address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100213
214 if (len < 3) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100215 DEBUGP(DMI, "len %d < 3\n", len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100216 return NULL;
217 };
218
219 lapd_msg_type typ = 0;
220 lapd_cmd_type cmd = 0;
Harald Welte716d2a42011-02-05 17:29:05 +0100221 pf = -1;
222 ns = -1;
223 nr = -1;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100224 if ((data[2] & 1) == 0) {
225 typ = LAPD_TYPE_I;
226 assert(len >= 4);
227 ns = data[2] >> 1;
228 nr = data[3] >> 1;
229 pf = data[3] & 1;
230 cmd = LAPD_CMD_I;
231 } else if ((data[2] & 3) == 1) {
232 typ = LAPD_TYPE_S;
233 assert(len >= 4);
234 nr = data[3] >> 1;
235 pf = data[3] & 1;
236 switch (data[2]) {
Harald Welte30fe6412011-02-04 20:34:08 +0100237 case 0x1:
238 cmd = LAPD_CMD_RR;
239 break;
240 case 0x5:
241 cmd = LAPD_CMD_RNR;
242 break;
243 case 0x9:
244 cmd = LAPD_CMD_REJ;
245 break;
246 default:
Harald Welte0abc11a2011-02-05 17:16:26 +0100247 DEBUGP(DMI, "unknown S cmd %x\n", data[2]);
Harald Welte30fe6412011-02-04 20:34:08 +0100248 assert(0);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100249 };
250 } else if ((data[2] & 3) == 3) {
251 typ = LAPD_TYPE_U;
252 pf = (data[2] >> 4) & 1;
Harald Welte30fe6412011-02-04 20:34:08 +0100253 int val = data[2] & ~(1 << 4);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100254 switch (val) {
Harald Welte30fe6412011-02-04 20:34:08 +0100255 case 0x6f:
256 cmd = LAPD_CMD_SABME;
257 break;
258 case 0x0f:
259 cmd = LAPD_CMD_DM;
260 break;
261 case 0x03:
262 cmd = LAPD_CMD_UI;
263 break;
264 case 0x43:
265 cmd = LAPD_CMD_DISC;
266 break;
267 case 0x63:
268 cmd = LAPD_CMD_UA;
269 break;
270 case 0x87:
271 cmd = LAPD_CMD_FRMR;
272 break;
273 case 0xaf:
274 cmd = LAPD_CMD_XID;
275 break;
276
277 default:
Harald Welte0abc11a2011-02-05 17:16:26 +0100278 DEBUGP(DMI, "unknown U cmd %x (pf %x data %x)\n", val,
Harald Welte30fe6412011-02-04 20:34:08 +0100279 pf, data[2]);
280 assert(0);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100281 };
282 };
Harald Welte30fe6412011-02-04 20:34:08 +0100283
Harald Welte716d2a42011-02-05 17:29:05 +0100284 contents = &data[4];
Harald Welte30fe6412011-02-04 20:34:08 +0100285 if (typ == LAPD_TYPE_U)
286 contents--;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100287 *ilen = len - (contents - data);
Harald Welte30fe6412011-02-04 20:34:08 +0100288
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100289 if (tei == 127)
290 lapd_tei_receive(contents, *ilen, cbdata);
291
Harald Welte716d2a42011-02-05 17:29:05 +0100292 teip = teip_from_tei(tei);
293
Harald Welte0abc11a2011-02-05 17:16:26 +0100294 DEBUGP(DMI, "<- %c %s sapi %x tei %3d cmd %x pf %x ns %3d nr %3d "
295 "ilen %d teip %p vs %d va %d vr %d len %d\n",
Harald Welte30fe6412011-02-04 20:34:08 +0100296 lapd_msg_types[typ], lapd_cmd_types[cmd], sapi, tei, command, pf,
297 ns, nr, *ilen, teip, teip ? teip->vs : -1, teip ? teip->va : -1,
298 teip ? teip->vr : -1, len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100299
Harald Welte716d2a42011-02-05 17:29:05 +0100300 if (!teip) {
301 LOGP(DMI, LOGL_NOTICE, "Unknown TEI %u\n", tei);
302 return NULL;
303 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100304
Harald Welte716d2a42011-02-05 17:29:05 +0100305 switch (cmd) {
306 case LAPD_CMD_I:
307 if (ns != teip->vr) {
308 DEBUGP(DMI, "ns %d != vr %d\n", ns, teip->vr);
309 if (ns == ((teip->vr - 1) & 0x7f)) {
310 DEBUGP(DMI, "DOUBLE FRAME, ignoring\n");
311 cmd = 0; // ignore
312 } else {
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100313 assert(0);
Harald Welte716d2a42011-02-05 17:29:05 +0100314 };
315 } else {
316 //printf("IN SEQUENCE\n");
317 teip->vr = (ns + 1) & 0x7f; // FIXME: hack!
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100318 };
Harald Welte716d2a42011-02-05 17:29:05 +0100319
320 break;
321 case LAPD_CMD_UI:
322 break;
323 case LAPD_CMD_SABME:
324 teip->vs = 0;
325 teip->vr = 0;
326 teip->va = 0;
327
328 // ua
329 resp[l++] = data[0];
330 resp[l++] = (tei << 1) | 1;
331 resp[l++] = 0x73;
332 lapd_transmit_cb(resp, l, cbdata);
333 if (teip->state != LAPD_TEI_ACTIVE) {
334 if (teip->state == LAPD_TEI_ASSIGNED) {
335 lapd_tei_set_state(teip,
336 LAPD_TEI_ACTIVE);
337 //printf("ASSIGNED and ACTIVE\n");
338 } else {
339#if 0
340 DEBUGP(DMI, "rr in strange state, send rej\n");
341
342 // rej
343 resp[l++] = (teip-> sapi << 2) | (network_side ? 0 : 2);
344 resp[l++] = (tei << 1) | 1;
345 resp[l++] = 0x09; //rej
346 resp[l++] = ((teip->vr + 1) << 1) | 0;
347 lapd_transmit_cb(resp, l, cbdata);
348 pf = 0; // dont reply
349#endif
350 };
351 };
352
353 *prim = LAPD_MPH_ACTIVATE_IND;
354 break;
355 case LAPD_CMD_RR:
356 teip->va = (nr & 0x7f);
357#if 0
358 if (teip->state != LAPD_TEI_ACTIVE) {
359 if (teip->state == LAPD_TEI_ASSIGNED) {
360 lapd_tei_set_state(teip, LAPD_TEI_ACTIVE);
361 *prim = LAPD_MPH_ACTIVATE_IND;
362 //printf("ASSIGNED and ACTIVE\n");
363 } else {
364#if 0
365 DEBUGP(DMI, "rr in strange " "state, send rej\n");
366
367 // rej
368 resp[l++] = (teip-> sapi << 2) | (network_side ? 0 : 2);
369 resp[l++] = (tei << 1) | 1;
370 resp[l++] = 0x09; //rej
371 resp[l++] =
372 ((teip->vr + 1) << 1) | 0;
373 lapd_transmit_cb(resp, l,
374 cbdata);
375 pf = 0; // dont reply
376#endif
377 };
378 };
379#endif
380 if (pf) {
381 // interrogating us, send rr
382 resp[l++] = data[0];
383 resp[l++] = (tei << 1) | 1;
384 resp[l++] = 0x01; // rr
385 resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
386
387 lapd_transmit_cb(resp, l, cbdata);
388
389 };
390 break;
391 case LAPD_CMD_FRMR:
392 // frame reject
393#if 0
394 if (teip->state == LAPD_TEI_ACTIVE)
395 *prim = LAPD_MPH_DEACTIVATE_IND;
396 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
397#endif
398 DEBUGP(DMI, "frame reject, ignoring\n");
399 assert(0);
400 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:
410 DEBUGP(DMI, "unknown cmd for tei %d (cmd %x)\n", tei, cmd);
411 assert(0);
412 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100413
414 //if ((*prim == 0) && (ilen > 0) && (typ != LAPD_TYPE_S)) {
415 //if (cmd == LAPD_CMD_I) {
416 if (typ == LAPD_TYPE_I) {
417 // send rr
418 // Thu Jan 22 19:17:13 2009 <4000> sangoma.c:340 read (62/25) 4: fa 33 01 0a
419 // 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
420
421 // interrogating us, send rr
Harald Welte0abc11a2011-02-05 17:16:26 +0100422 DEBUGP(DMI, "Sending RR response\n");
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100423 resp[l++] = data[0];
424 resp[l++] = (tei << 1) | 1;
Harald Welte30fe6412011-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 Fredricksonbc6649e2010-02-16 22:01:36 +0100427
428 lapd_transmit_cb(resp, l, cbdata);
429
Matthew Fredrickson69245a02010-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 Fredricksonbc6649e2010-02-16 22:01:36 +0100436 return contents;
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500437 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100438
439 return NULL;
440};
441
Harald Welte30fe6412011-02-04 20:34:08 +0100442void lapd_transmit(int tei, uint8_t * data, int len, void *cbdata)
443{
Matthew Fredricksonbc6649e2010-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
449 // prepend stuff
Harald Welte30fe6412011-02-04 20:34:08 +0100450 uint8_t buf[10000];
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100451 memset(buf, 0, sizeof(buf));
Harald Welte30fe6412011-02-04 20:34:08 +0100452 memmove(buf + 4, data, len);
Matthew Fredricksonbc6649e2010-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};