llc: implement LLC PDU codec based on code from osmo-sgsn.git
osmo-sgsn.git 13ccbc1e6120fe78f6f9f950d7242090920ca41b Change-Id: I61d7e2e6d0a8f2cdfc2113e637e447dc428cc70d
This commit is contained in:
parent
bae1ec285b
commit
c1ca5406bc
|
@ -86,6 +86,7 @@ AC_CONFIG_FILES([libosmo-csn1.pc
|
|||
src/llc/Makefile
|
||||
src/rlcmac/Makefile
|
||||
tests/Makefile
|
||||
tests/llc_pdu_codec/Makefile
|
||||
tests/ts_44_018/Makefile
|
||||
tests/ts_44_060/Makefile
|
||||
Makefile
|
||||
|
|
|
@ -130,4 +130,36 @@ struct osmo_gprs_llc_params {
|
|||
uint16_t kU;
|
||||
};
|
||||
|
||||
#define OSMO_GPRS_LLC_PDU_F_CMD_RSP (1 << 0) /* 6.2.2 Commmand/Response bit (C/R) */
|
||||
#define OSMO_GPRS_LLC_PDU_F_FOLL_FIN (1 << 1) /* 6.3.5.1 Poll/Final bit (P/F) */
|
||||
#define OSMO_GPRS_LLC_PDU_F_ACK_REQ (1 << 2) /* 6.3.5.2 Acknowledgement request bit (A) */
|
||||
#define OSMO_GPRS_LLC_PDU_F_MAC_PRES (1 << 3) /* 6.3.5.2a Integrity Protection bit (IP) */
|
||||
#define OSMO_GPRS_LLC_PDU_F_ENC_MODE (1 << 4) /* 6.3.5.5.1 Encryption mode bit (E) */
|
||||
#define OSMO_GPRS_LLC_PDU_F_PROT_MODE (1 << 5) /* 6.3.5.5.2 Protected Mode bit (PM) */
|
||||
|
||||
struct osmo_gprs_llc_pdu_decoded {
|
||||
enum osmo_gprs_llc_sapi sapi;
|
||||
enum osmo_gprs_llc_frame_fmt fmt;
|
||||
enum osmo_gprs_llc_frame_func func;
|
||||
uint32_t flags; /* see OSMO_GPRS_LLC_PDU_F_* above */
|
||||
uint32_t seq_rx; /* 6.3.5.4.5 Receive sequence number N(R) */
|
||||
uint32_t seq_tx; /* 6.3.5.4.3 Send sequence number N(S) */
|
||||
uint32_t fcs; /* 5.5 Frame Check Sequence (FCS) field */
|
||||
uint32_t mac; /* 5.5a Message Authentication Code (MAC) field */
|
||||
struct {
|
||||
uint8_t len; /* Indicates the number of octets in the bitmap */
|
||||
uint8_t r[32]; /* The R(n) bitmap */
|
||||
} sack; /* 6.3.5.4.6 SACK bitmap R(n) */
|
||||
size_t data_len;
|
||||
const uint8_t *data;
|
||||
};
|
||||
|
||||
void osmo_gprs_llc_pdu_hdr_dump_buf(char *buf, size_t buf_size,
|
||||
const struct osmo_gprs_llc_pdu_decoded *pdu);
|
||||
const char *osmo_gprs_llc_pdu_hdr_dump(const struct osmo_gprs_llc_pdu_decoded *pdu);
|
||||
|
||||
int osmo_gprs_llc_pdu_decode(struct osmo_gprs_llc_pdu_decoded *pdu,
|
||||
const uint8_t *data, size_t data_len);
|
||||
int osmo_gprs_llc_pdu_encode(struct msgb *msg, const struct osmo_gprs_llc_pdu_decoded *pdu);
|
||||
|
||||
uint32_t osmo_gprs_llc_fcs(const uint8_t *data, size_t len);
|
||||
|
|
|
@ -20,10 +20,22 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/gprs/llc/llc.h>
|
||||
|
||||
/* TODO: make logging category configurable */
|
||||
#define DLLC DLGLOBAL
|
||||
|
||||
#define UI_HDR_LEN 3
|
||||
#define N202 4
|
||||
#define CRC24_LENGTH 3
|
||||
|
||||
const struct value_string osmo_gprs_llc_frame_fmt_names[] = {
|
||||
{ OSMO_GPRS_LLC_FMT_I, "I" },
|
||||
{ OSMO_GPRS_LLC_FMT_S, "U" },
|
||||
|
@ -64,3 +76,410 @@ uint32_t osmo_gprs_llc_fcs(const uint8_t *data, size_t len)
|
|||
|
||||
return fcs_calc;
|
||||
}
|
||||
|
||||
void osmo_gprs_llc_pdu_hdr_dump_buf(char *buf, size_t buf_size,
|
||||
const struct osmo_gprs_llc_pdu_decoded *pdu)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "SAPI=%u, %s func=%s C/R=%c",
|
||||
pdu->sapi, /* TODO: print value_string */
|
||||
osmo_gprs_llc_frame_fmt_name(pdu->fmt),
|
||||
osmo_gprs_llc_frame_func_name(pdu->func),
|
||||
pdu->flags & OSMO_GPRS_LLC_PDU_F_CMD_RSP ? '1' : '0');
|
||||
|
||||
switch (pdu->fmt) {
|
||||
case OSMO_GPRS_LLC_FMT_I:
|
||||
OSMO_STRBUF_PRINTF(sb, " A=%c N(R)=%u N(S)=%u",
|
||||
pdu->flags & OSMO_GPRS_LLC_PDU_F_ACK_REQ ? '1' : '0',
|
||||
pdu->seq_rx, pdu->seq_tx);
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FMT_S:
|
||||
OSMO_STRBUF_PRINTF(sb, " A=%c N(R)=%u",
|
||||
pdu->flags & OSMO_GPRS_LLC_PDU_F_ACK_REQ ? '1' : '0',
|
||||
pdu->seq_rx);
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FMT_UI:
|
||||
OSMO_STRBUF_PRINTF(sb, " PM=%c E=%c IP=%c N(U)=%u",
|
||||
pdu->flags & OSMO_GPRS_LLC_PDU_F_PROT_MODE ? '1' : '0',
|
||||
pdu->flags & OSMO_GPRS_LLC_PDU_F_ENC_MODE ? '1' : '0',
|
||||
pdu->flags & OSMO_GPRS_LLC_PDU_F_MAC_PRES ? '1' : '0',
|
||||
pdu->seq_tx);
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FMT_U:
|
||||
OSMO_STRBUF_PRINTF(sb, " P/F=%c",
|
||||
pdu->flags & OSMO_GPRS_LLC_PDU_F_FOLL_FIN ? '1' : '0');
|
||||
break;
|
||||
}
|
||||
|
||||
if (pdu->flags & OSMO_GPRS_LLC_PDU_F_MAC_PRES)
|
||||
OSMO_STRBUF_PRINTF(sb, " MAC=%08x", pdu->mac);
|
||||
OSMO_STRBUF_PRINTF(sb, " FCS=%06x", pdu->fcs);
|
||||
}
|
||||
|
||||
const char *osmo_gprs_llc_pdu_hdr_dump(const struct osmo_gprs_llc_pdu_decoded *pdu)
|
||||
{
|
||||
static __thread char buf[256];
|
||||
osmo_gprs_llc_pdu_hdr_dump_buf(&buf[0], sizeof(buf), pdu);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* 6.4.1 Unnumbered (U) frames */
|
||||
#define GPRS_LLC_U_NULL_CMD 0x00
|
||||
#define GPRS_LLC_U_DM_RESP 0x01
|
||||
#define GPRS_LLC_U_DISC_CMD 0x04
|
||||
#define GPRS_LLC_U_UA_RESP 0x06
|
||||
#define GPRS_LLC_U_SABM_CMD 0x07
|
||||
#define GPRS_LLC_U_FRMR_RESP 0x08
|
||||
#define GPRS_LLC_U_XID 0x0b
|
||||
|
||||
int osmo_gprs_llc_pdu_encode(struct msgb *msg, const struct osmo_gprs_llc_pdu_decoded *pdu)
|
||||
{
|
||||
uint8_t *addr = msgb_put(msg, 1);
|
||||
uint8_t *ctrl = NULL;
|
||||
size_t crc_len;
|
||||
uint32_t fcs;
|
||||
|
||||
/* 6.2.3 Service Access Point Identifier (SAPI) */
|
||||
addr[0] = pdu->sapi & 0x0f;
|
||||
|
||||
/* 6.2.2 Commmand/Response bit (C/R) */
|
||||
if (pdu->flags & OSMO_GPRS_LLC_PDU_F_CMD_RSP)
|
||||
addr[0] |= (1 << 6);
|
||||
|
||||
switch (pdu->fmt) {
|
||||
case OSMO_GPRS_LLC_FMT_I:
|
||||
ctrl = msgb_put(msg, 3);
|
||||
|
||||
ctrl[0] = 0x00; /* 0xxxxxxx */
|
||||
ctrl[1] = ctrl[2] = 0x00;
|
||||
|
||||
if (pdu->flags & OSMO_GPRS_LLC_PDU_F_ACK_REQ)
|
||||
ctrl[0] |= (1 << 6);
|
||||
|
||||
ctrl[0] |= (pdu->seq_tx >> 4) & 0x1f;
|
||||
ctrl[1] |= (pdu->seq_tx & 0x0f) << 4;
|
||||
|
||||
ctrl[1] |= (pdu->seq_rx >> 6) & 0x07;
|
||||
ctrl[2] |= (pdu->seq_rx & 0x3f) << 2;
|
||||
|
||||
ctrl[2] |= (pdu->func - OSMO_GPRS_LLC_FUNC_RR) & 0x03;
|
||||
|
||||
if (pdu->func == OSMO_GPRS_LLC_FUNC_SACK) {
|
||||
if (pdu->sack.len == 0)
|
||||
return -EINVAL;
|
||||
msgb_put_u8(msg, pdu->sack.len - 1);
|
||||
memcpy(msgb_put(msg, pdu->sack.len),
|
||||
&pdu->sack.r[0], pdu->sack.len);
|
||||
}
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FMT_S:
|
||||
ctrl = msgb_put(msg, 2);
|
||||
|
||||
ctrl[0] = 0x80; /* 10xxxxxx */
|
||||
ctrl[1] = 0x00;
|
||||
|
||||
if (pdu->flags & OSMO_GPRS_LLC_PDU_F_ACK_REQ)
|
||||
ctrl[1] |= 0x20;
|
||||
|
||||
ctrl[0] |= (pdu->seq_rx >> 6) & 0x07;
|
||||
ctrl[1] |= (pdu->seq_rx & 0x3f) << 2;
|
||||
|
||||
ctrl[1] |= (pdu->func - OSMO_GPRS_LLC_FUNC_RR) & 0x03;
|
||||
|
||||
if (pdu->func == OSMO_GPRS_LLC_FUNC_SACK) {
|
||||
if (pdu->sack.len == 0)
|
||||
return -EINVAL;
|
||||
memcpy(msgb_put(msg, pdu->sack.len),
|
||||
&pdu->sack.r[0], pdu->sack.len);
|
||||
}
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FMT_UI:
|
||||
ctrl = msgb_put(msg, 2);
|
||||
|
||||
ctrl[0] = 0xc0; /* 110xxxxx */
|
||||
ctrl[1] = 0x00;
|
||||
|
||||
if (pdu->flags & OSMO_GPRS_LLC_PDU_F_MAC_PRES)
|
||||
ctrl[0] |= (1 << 4);
|
||||
|
||||
ctrl[0] |= (pdu->seq_tx >> 6) & 0x07;
|
||||
ctrl[1] |= (pdu->seq_tx & 0x3f) << 2;
|
||||
|
||||
if (pdu->flags & OSMO_GPRS_LLC_PDU_F_ENC_MODE)
|
||||
ctrl[1] |= (1 << 1);
|
||||
if (pdu->flags & OSMO_GPRS_LLC_PDU_F_PROT_MODE)
|
||||
ctrl[1] |= (1 << 0);
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FMT_U:
|
||||
ctrl = msgb_put(msg, 1);
|
||||
|
||||
ctrl[0] = 0xe0; /* 111xxxxx */
|
||||
|
||||
if (pdu->flags & OSMO_GPRS_LLC_PDU_F_FOLL_FIN)
|
||||
ctrl[0] |= (1 << 4);
|
||||
|
||||
switch (pdu->func) {
|
||||
case OSMO_GPRS_LLC_FUNC_NULL:
|
||||
ctrl[0] |= GPRS_LLC_U_NULL_CMD;
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FUNC_DM:
|
||||
ctrl[0] |= GPRS_LLC_U_DM_RESP;
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FUNC_DISC:
|
||||
ctrl[0] |= GPRS_LLC_U_DISC_CMD;
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FUNC_UA:
|
||||
ctrl[0] |= GPRS_LLC_U_UA_RESP;
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FUNC_SABM:
|
||||
ctrl[0] |= GPRS_LLC_U_SABM_CMD;
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FUNC_FRMR:
|
||||
ctrl[0] |= GPRS_LLC_U_FRMR_RESP;
|
||||
break;
|
||||
case OSMO_GPRS_LLC_FUNC_XID:
|
||||
ctrl[0] |= GPRS_LLC_U_XID;
|
||||
break;
|
||||
default:
|
||||
LOGP(DLLC, LOGL_ERROR,
|
||||
"Unknown UI func=0x%02x\n", pdu->func);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (pdu->data_len > 0) {
|
||||
uint8_t *data = msgb_put(msg, pdu->data_len);
|
||||
memcpy(data, pdu->data, pdu->data_len);
|
||||
}
|
||||
|
||||
/* 5.5a Message Authentication Code (MAC) field */
|
||||
if (pdu->flags & OSMO_GPRS_LLC_PDU_F_MAC_PRES) {
|
||||
/* TODO: calculate MAC (see 3GPP TS 43.020) */
|
||||
LOGP(DLLC, LOGL_ERROR,
|
||||
"Message Authentication Code (MAC) is not implemented\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* 5.5 Frame Check Sequence (FCS) field */
|
||||
crc_len = msg->tail - addr;
|
||||
if (~pdu->flags & OSMO_GPRS_LLC_PDU_F_PROT_MODE)
|
||||
crc_len = OSMO_MIN(crc_len, UI_HDR_LEN + N202);
|
||||
fcs = osmo_gprs_llc_fcs(addr, crc_len);
|
||||
|
||||
msgb_put_u8(msg, fcs & 0xff);
|
||||
msgb_put_u8(msg, (fcs >> 8) & 0xff);
|
||||
msgb_put_u8(msg, (fcs >> 16) & 0xff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_gprs_llc_pdu_decode(struct osmo_gprs_llc_pdu_decoded *pdu,
|
||||
const uint8_t *data, size_t data_len)
|
||||
{
|
||||
const uint8_t *addr = &data[0];
|
||||
const uint8_t *ctrl = &data[1];
|
||||
|
||||
#define check_len(len, text) \
|
||||
do { \
|
||||
if (data_len < (len)) { \
|
||||
LOGP(DLLC, LOGL_ERROR, "Failed to parse LLC PDU: %s\n", text); \
|
||||
return -EINVAL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* 5.5 Frame Check Sequence (FCS) field */
|
||||
check_len(CRC24_LENGTH, "missing Frame Check Sequence (FCS) field");
|
||||
pdu->fcs = data[data_len - 3];
|
||||
pdu->fcs |= data[data_len - 2] << 8;
|
||||
pdu->fcs |= data[data_len - 1] << 16;
|
||||
data_len -= CRC24_LENGTH;
|
||||
|
||||
/* 6.2.0 Address field format */
|
||||
check_len(1, "missing Address field");
|
||||
data_len -= 1;
|
||||
|
||||
/* Initial assumption: FCS covers hdr + all inf fields */
|
||||
pdu->flags |= OSMO_GPRS_LLC_PDU_F_PROT_MODE;
|
||||
|
||||
/* 6.2.1 Protocol Discriminator bit (PD): shall be 0 */
|
||||
if (*addr & 0x80) {
|
||||
LOGP(DLLC, LOGL_ERROR, "Protocol Discriminator shall be 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 6.2.2 Commmand/Response bit (C/R) */
|
||||
if (*addr & 0x40)
|
||||
pdu->flags |= OSMO_GPRS_LLC_PDU_F_CMD_RSP;
|
||||
|
||||
/* 6.2.3 Service Access Point Identifier (SAPI) */
|
||||
pdu->sapi = *addr & 0x0f;
|
||||
|
||||
/* Check for reserved SAPI */
|
||||
switch (*addr & 0x0f) {
|
||||
case 0x00:
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
case 0x0a:
|
||||
case 0x0c:
|
||||
case 0x0d:
|
||||
case 0x0f:
|
||||
LOGP(DLLC, LOGL_ERROR, "Unknown SAPI=%u\n", pdu->sapi);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* U format has the shortest control field length=1 */
|
||||
check_len(1, "missing Control field");
|
||||
|
||||
/* 6.3.0 Control field formats */
|
||||
if ((ctrl[0] & 0x80) == 0) {
|
||||
/* 6.3.1 Information transfer format - I */
|
||||
pdu->fmt = OSMO_GPRS_LLC_FMT_I;
|
||||
|
||||
check_len(3, "I format Control field is too short");
|
||||
data_len -= 3;
|
||||
|
||||
pdu->data = ctrl + 3;
|
||||
/* pdu->data_len is set below */
|
||||
|
||||
if (ctrl[0] & 0x40)
|
||||
pdu->flags |= OSMO_GPRS_LLC_PDU_F_ACK_REQ;
|
||||
|
||||
pdu->seq_tx = (ctrl[0] & 0x1f) << 4;
|
||||
pdu->seq_tx |= (ctrl[1] >> 4);
|
||||
|
||||
pdu->seq_rx = (ctrl[1] & 0x7) << 6;
|
||||
pdu->seq_rx |= (ctrl[2] >> 2);
|
||||
|
||||
switch (ctrl[2] & 0x03) {
|
||||
case 0:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_RR;
|
||||
break;
|
||||
case 1:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_ACK;
|
||||
break;
|
||||
case 2:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_RNR;
|
||||
break;
|
||||
case 3:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_SACK;
|
||||
check_len(1, "I func=SACK is too short");
|
||||
pdu->sack.len = (ctrl[3] & 0x1f) + 1; /* 1 .. 32 */
|
||||
/* The R(n) bitmask takes len=(K + 1) octets */
|
||||
check_len(pdu->sack.len, "I func=SACK is too short");
|
||||
memcpy(&pdu->sack.r[0], ctrl + 4, pdu->sack.len);
|
||||
pdu->data += 1 + pdu->sack.len;
|
||||
data_len -= 1 + pdu->sack.len;
|
||||
break;
|
||||
}
|
||||
pdu->data_len = data_len;
|
||||
} else if ((ctrl[0] & 0xc0) == 0x80) {
|
||||
/* 6.3.2 Supervisory format - S */
|
||||
pdu->fmt = OSMO_GPRS_LLC_FMT_S;
|
||||
|
||||
check_len(2, "S format Control field is too short");
|
||||
data_len -= 2;
|
||||
|
||||
pdu->data = NULL;
|
||||
pdu->data_len = 0;
|
||||
|
||||
if (ctrl[0] & 0x20)
|
||||
pdu->flags |= OSMO_GPRS_LLC_PDU_F_ACK_REQ;
|
||||
|
||||
pdu->seq_rx = (ctrl[0] & 0x7) << 6;
|
||||
pdu->seq_rx |= (ctrl[1] >> 2);
|
||||
|
||||
switch (ctrl[1] & 0x03) {
|
||||
case 0:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_RR;
|
||||
break;
|
||||
case 1:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_ACK;
|
||||
break;
|
||||
case 2:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_RNR;
|
||||
break;
|
||||
case 3:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_SACK;
|
||||
/* The R(n) bitmask takes all remaining octets */
|
||||
check_len(1, "S func=SACK is too short");
|
||||
pdu->sack.len = data_len; /* 1 .. 32 */
|
||||
memcpy(&pdu->sack.r[0], ctrl + 2, pdu->sack.len);
|
||||
break;
|
||||
}
|
||||
} else if ((ctrl[0] & 0xe0) == 0xc0) {
|
||||
/* 6.3.3 Unconfirmed Information format - UI */
|
||||
pdu->fmt = OSMO_GPRS_LLC_FMT_UI;
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_UI;
|
||||
|
||||
check_len(2, "UI format Control field is too short");
|
||||
data_len -= 2;
|
||||
|
||||
pdu->data = ctrl + 2;
|
||||
pdu->data_len = data_len;
|
||||
|
||||
pdu->seq_tx = (ctrl[0] & 0x7) << 6;
|
||||
pdu->seq_tx |= (ctrl[1] >> 2);
|
||||
|
||||
if (ctrl[0] & 0x10) {
|
||||
check_len(sizeof(pdu->mac), "missing MAC field");
|
||||
pdu->data_len -= sizeof(pdu->mac);
|
||||
data_len -= sizeof(pdu->mac);
|
||||
|
||||
pdu->mac = osmo_load32le(&pdu->data[data_len]);
|
||||
pdu->flags |= OSMO_GPRS_LLC_PDU_F_MAC_PRES;
|
||||
}
|
||||
|
||||
if (ctrl[1] & 0x02)
|
||||
pdu->flags |= OSMO_GPRS_LLC_PDU_F_ENC_MODE;
|
||||
|
||||
if (~ctrl[1] & 0x01) /* FCS covers hdr + N202 octets */
|
||||
pdu->flags &= ~OSMO_GPRS_LLC_PDU_F_PROT_MODE;
|
||||
} else {
|
||||
/* 6.3.4 Unnumbered format - U */
|
||||
pdu->fmt = OSMO_GPRS_LLC_FMT_U;
|
||||
|
||||
check_len(1, "U format Control field is too short");
|
||||
data_len -= 1;
|
||||
|
||||
pdu->data = NULL;
|
||||
pdu->data_len = 0;
|
||||
|
||||
if (ctrl[0] & 0x10)
|
||||
pdu->flags |= OSMO_GPRS_LLC_PDU_F_FOLL_FIN;
|
||||
|
||||
switch (ctrl[0] & 0x0f) {
|
||||
case GPRS_LLC_U_NULL_CMD:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_NULL;
|
||||
break;
|
||||
case GPRS_LLC_U_DM_RESP:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_DM;
|
||||
break;
|
||||
case GPRS_LLC_U_DISC_CMD:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_DISC;
|
||||
break;
|
||||
case GPRS_LLC_U_UA_RESP:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_UA;
|
||||
break;
|
||||
case GPRS_LLC_U_SABM_CMD:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_SABM;
|
||||
break;
|
||||
case GPRS_LLC_U_FRMR_RESP:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_FRMR;
|
||||
break;
|
||||
case GPRS_LLC_U_XID:
|
||||
pdu->func = OSMO_GPRS_LLC_FUNC_XID;
|
||||
pdu->data = ctrl + 1;
|
||||
pdu->data_len = data_len;
|
||||
break;
|
||||
default:
|
||||
LOGP(DLLC, LOGL_ERROR, "Unknown U func=0x%02x\n", ctrl[0] & 0x0f);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
#undef check_len
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
SUBDIRS = \
|
||||
llc_pdu_codec \
|
||||
ts_44_018 \
|
||||
ts_44_060 \
|
||||
$(NULL)
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
-I$(top_srcdir)/include/ \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
-no-install \
|
||||
$(NULL)
|
||||
|
||||
check_PROGRAMS = \
|
||||
pdu_codec_test \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
pdu_codec_test.ok \
|
||||
pdu_codec_test.err \
|
||||
$(NULL)
|
||||
|
||||
pdu_codec_test_SOURCES = pdu_codec_test.c
|
||||
pdu_codec_test_LDADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(top_builddir)/src/llc/libosmo-gprs-llc.la \
|
||||
$(NULL)
|
|
@ -0,0 +1,102 @@
|
|||
/* LLC PDU codec tests
|
||||
*
|
||||
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/gprs/llc/llc.h>
|
||||
|
||||
static void *tall_ctx = NULL;
|
||||
|
||||
static void test_pdu_dec_enc(void)
|
||||
{
|
||||
static const char *testData[] = {
|
||||
/* SAPI=1 (GMM), UI func=UI C/R=0 PM=0 N(U)=4 */
|
||||
"01c010080509afe633",
|
||||
/* SAPI=1 (GMM), UI func=UI C/R=1 PM=1 N(U)=0 */
|
||||
"41c001081502de8e9a",
|
||||
/* SAPI=1 (GMM), U func=NULL C/R=0 P/F=0 */
|
||||
"01e01ca2b3",
|
||||
/* SAPI=3 (SNDCP3), U func=XID C/R=1 P/F=1 */
|
||||
"43fb01001601f41a05df8c7c4e",
|
||||
/* SAPI=3 (SNDCP3), U func=XID C/R=1 P/F=1 */
|
||||
"03fb1604d216f984",
|
||||
};
|
||||
|
||||
struct msgb *msg = msgb_alloc(1024, "LLC-PDU");
|
||||
OSMO_ASSERT(msg != NULL);
|
||||
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(testData); i++) {
|
||||
struct osmo_gprs_llc_pdu_decoded hdr = { 0 };
|
||||
uint8_t pdu[256];
|
||||
size_t pdu_len;
|
||||
int rc;
|
||||
|
||||
printf("%s(): decoding testData[%u] = %s\n", __func__, i, testData[i]);
|
||||
|
||||
rc = osmo_hexparse(testData[i], &pdu[0], sizeof(pdu));
|
||||
pdu_len = strlen(testData[i]) / 2;
|
||||
OSMO_ASSERT(rc == pdu_len);
|
||||
|
||||
rc = osmo_gprs_llc_pdu_decode(&hdr, &pdu[0], pdu_len);
|
||||
printf(" osmo_gprs_llc_pdu_decode() returns %d\n", rc);
|
||||
printf(" osmo_gprs_llc_pdu_hdr_dump(): %s\n", osmo_gprs_llc_pdu_hdr_dump(&hdr));
|
||||
if (hdr.data_len > 0) {
|
||||
printf(" hdr.data[] (len=%zu): %s\n", hdr.data_len,
|
||||
osmo_hexdump_nospc(hdr.data, hdr.data_len));
|
||||
}
|
||||
|
||||
printf("%s(): encoding decoded testData[%u]\n", __func__, i);
|
||||
|
||||
msgb_reset(msg);
|
||||
rc = osmo_gprs_llc_pdu_encode(msg, &hdr);
|
||||
printf(" osmo_gprs_llc_pdu_encode() returns %d\n", rc);
|
||||
printf(" osmo_gprs_llc_pdu_encode(): %s\n", osmo_hexdump_nospc(msg->data, msg->len));
|
||||
printf(" memcmp() returns %d\n", memcmp(&pdu, msg->data, pdu_len));
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static const struct log_info_cat test_log_categories[] = { };
|
||||
static const struct log_info test_log_info = {
|
||||
.cat = test_log_categories,
|
||||
.num_cat = ARRAY_SIZE(test_log_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
tall_ctx = talloc_named_const(NULL, 1, __FILE__);
|
||||
|
||||
osmo_init_logging2(tall_ctx, &test_log_info);
|
||||
log_parse_category_mask(osmo_stderr_target, "DLGLOBAL,1:");
|
||||
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||
log_set_print_category(osmo_stderr_target, 1);
|
||||
log_set_print_level(osmo_stderr_target, 1);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
|
||||
test_pdu_dec_enc();
|
||||
|
||||
talloc_free(tall_ctx);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
test_pdu_dec_enc(): decoding testData[0] = 01c010080509afe633
|
||||
osmo_gprs_llc_pdu_decode() returns 0
|
||||
osmo_gprs_llc_pdu_hdr_dump(): SAPI=1, UI func=UI C/R=0 PM=0 E=0 IP=0 N(U)=4 FCS=33e6af
|
||||
hdr.data[] (len=3): 080509
|
||||
test_pdu_dec_enc(): encoding decoded testData[0]
|
||||
osmo_gprs_llc_pdu_encode() returns 0
|
||||
osmo_gprs_llc_pdu_encode(): 01c010080509afe633
|
||||
memcmp() returns 0
|
||||
test_pdu_dec_enc(): decoding testData[1] = 41c001081502de8e9a
|
||||
osmo_gprs_llc_pdu_decode() returns 0
|
||||
osmo_gprs_llc_pdu_hdr_dump(): SAPI=1, UI func=UI C/R=1 PM=1 E=0 IP=0 N(U)=0 FCS=9a8ede
|
||||
hdr.data[] (len=3): 081502
|
||||
test_pdu_dec_enc(): encoding decoded testData[1]
|
||||
osmo_gprs_llc_pdu_encode() returns 0
|
||||
osmo_gprs_llc_pdu_encode(): 41c001081502de8e9a
|
||||
memcmp() returns 0
|
||||
test_pdu_dec_enc(): decoding testData[2] = 01e01ca2b3
|
||||
osmo_gprs_llc_pdu_decode() returns 0
|
||||
osmo_gprs_llc_pdu_hdr_dump(): SAPI=1, U func=NULL C/R=0 P/F=0 FCS=b3a21c
|
||||
test_pdu_dec_enc(): encoding decoded testData[2]
|
||||
osmo_gprs_llc_pdu_encode() returns 0
|
||||
osmo_gprs_llc_pdu_encode(): 01e01ca2b3
|
||||
memcmp() returns 0
|
||||
test_pdu_dec_enc(): decoding testData[3] = 43fb01001601f41a05df8c7c4e
|
||||
osmo_gprs_llc_pdu_decode() returns 0
|
||||
osmo_gprs_llc_pdu_hdr_dump(): SAPI=3, U func=XID C/R=1 P/F=1 FCS=4e7c8c
|
||||
hdr.data[] (len=8): 01001601f41a05df
|
||||
test_pdu_dec_enc(): encoding decoded testData[3]
|
||||
osmo_gprs_llc_pdu_encode() returns 0
|
||||
osmo_gprs_llc_pdu_encode(): 43fb01001601f41a05df8c7c4e
|
||||
memcmp() returns 0
|
||||
test_pdu_dec_enc(): decoding testData[4] = 03fb1604d216f984
|
||||
osmo_gprs_llc_pdu_decode() returns 0
|
||||
osmo_gprs_llc_pdu_hdr_dump(): SAPI=3, U func=XID C/R=0 P/F=1 FCS=84f916
|
||||
hdr.data[] (len=3): 1604d2
|
||||
test_pdu_dec_enc(): encoding decoded testData[4]
|
||||
osmo_gprs_llc_pdu_encode() returns 0
|
||||
osmo_gprs_llc_pdu_encode(): 03fb1604d216f984
|
||||
memcmp() returns 0
|
||||
|
|
@ -1,6 +1,13 @@
|
|||
AT_INIT
|
||||
AT_BANNER([Regression tests])
|
||||
|
||||
AT_SETUP([llc/pdu_codec])
|
||||
AT_KEYWORDS([llc pdu codec])
|
||||
cat $abs_srcdir/llc_pdu_codec/pdu_codec_test.ok > expout
|
||||
cat $abs_srcdir/llc_pdu_codec/pdu_codec_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/llc_pdu_codec/pdu_codec_test], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([rlcmac/ts_44_018])
|
||||
AT_KEYWORDS([rlcmac ts_44_018])
|
||||
cat $abs_srcdir/ts_44_018/ts_44_018_test.ok > expout
|
||||
|
|
Loading…
Reference in New Issue