llc: Proper separation of public & private APIs, Introduce llc_prim API

Most of the existing (and added) data structures are kept private, since
most of those don't really need to be used outside internal code in the
library.

Most if not all the interaction from upper and lower layers  towards LLC
is now done through the new llc_prim pubic interface. This interface is
based on 3GPP TS 44.064 section 7.1.2.

This commit also implements some of the code paths of the public API by
means on importing LLC code from osmo-sgsn.git commit
57b63875c762a784127a13becd1c2549ca6c5454.
The import of code cannot be done in a separate commit since existing code
in osmo-sgsn.git is low quality and has tons of layer violations in all
directions.
Hence, this commit aims at being an initial point of having some working
LLC stack by means of a few unit tests, but by no means aims to be a
total working implementation. Some code paths are missing; bugs are
expected at this point.

Related: OS#5502
Change-Id: I588eb576b2703262f4ab9566ec362920d8390cfd
This commit is contained in:
Pau Espin 2022-11-25 16:59:32 +01:00 committed by Vadim Yanitskiy
parent f6e4b3be4c
commit 91c87d6907
23 changed files with 2805 additions and 402 deletions

View File

@ -1,3 +1,7 @@
noinst_HEADERS = \
llc_private.h \
$(NULL)
llc_HEADERS = \
llc.h \
llc_prim.h \

View File

@ -33,156 +33,7 @@ static inline const char *osmo_gprs_llc_sapi_name(enum osmo_gprs_llc_sapi val)
return get_value_string(osmo_gprs_llc_sapi_names, val);
}
/* Section 6.3.0 Control field formats */
enum osmo_gprs_llc_frame_fmt {
OSMO_GPRS_LLC_FMT_I, /* 6.3.1 Information transfer format - I */
OSMO_GPRS_LLC_FMT_S, /* 6.3.2 Supervisory format - S */
OSMO_GPRS_LLC_FMT_UI, /* 6.3.3 Unconfirmed Information format - UI */
OSMO_GPRS_LLC_FMT_U, /* 6.3.4 Unnumbered format - U */
};
extern const struct value_string osmo_gprs_llc_frame_fmt_names[];
static inline const char *osmo_gprs_llc_frame_fmt_name(enum osmo_gprs_llc_frame_fmt val)
{
return get_value_string(osmo_gprs_llc_frame_fmt_names, val);
}
/* Section 6.4 Commands and responses */
enum osmo_gprs_llc_frame_func {
/* 6.4.1 Unnumbered (U) frames */
OSMO_GPRS_LLC_FUNC_SABM, /* 6.4.1.1 */
OSMO_GPRS_LLC_FUNC_DISC, /* 6.4.1.2 */
OSMO_GPRS_LLC_FUNC_UA, /* 6.4.1.3 */
OSMO_GPRS_LLC_FUNC_DM, /* 6.4.1.4 */
OSMO_GPRS_LLC_FUNC_FRMR, /* 6.4.1.5 */
OSMO_GPRS_LLC_FUNC_XID, /* 6.4.1.6 */
OSMO_GPRS_LLC_FUNC_NULL, /* 6.4.1.7 */
/* 6.4.2 Unconfirmed Information (UI) frame */
OSMO_GPRS_LLC_FUNC_UI, /* 6.4.2.1 */
OSMO_GPRS_LLC_FUNC_UI_DUMMY, /* 6.4.2.2 */
/* 6.4.3 Combined Information (I) and Supervisory (S) frames */
OSMO_GPRS_LLC_FUNC_RR, /* 6.4.3.1 */
OSMO_GPRS_LLC_FUNC_ACK, /* 6.4.3.2 */
OSMO_GPRS_LLC_FUNC_SACK, /* 6.4.3.3 */
OSMO_GPRS_LLC_FUNC_RNR, /* 6.4.3.4 */
};
extern const struct value_string osmo_gprs_llc_frame_func_names[];
static inline const char *osmo_gprs_llc_frame_func_name(enum osmo_gprs_llc_frame_func val)
{
return get_value_string(osmo_gprs_llc_frame_func_names, val);
}
/* Section 6.4.1.6 / Table 6 */
enum osmo_gprs_llc_xid_type {
OSMO_GPRS_LLC_XID_T_VERSION = 0,
OSMO_GPRS_LLC_XID_T_IOV_UI = 1,
OSMO_GPRS_LLC_XID_T_IOV_I = 2,
OSMO_GPRS_LLC_XID_T_T200 = 3,
OSMO_GPRS_LLC_XID_T_N200 = 4,
OSMO_GPRS_LLC_XID_T_N201_U = 5,
OSMO_GPRS_LLC_XID_T_N201_I = 6,
OSMO_GPRS_LLC_XID_T_mD = 7,
OSMO_GPRS_LLC_XID_T_mU = 8,
OSMO_GPRS_LLC_XID_T_kD = 9,
OSMO_GPRS_LLC_XID_T_kU = 10,
OSMO_GPRS_LLC_XID_T_L3_PAR = 11,
OSMO_GPRS_LLC_XID_T_RESET = 12,
OSMO_GPRS_LLC_XID_T_IIOV_UI = 13,
OSMO_GPRS_LLC_XID_T_IIOV_UI_CNT = 14,
OSMO_GPRS_LLC_XID_T_MAC_IOV_UI = 15,
};
extern const struct value_string osmo_gprs_llc_xid_type_names[];
static inline const char *osmo_gprs_llc_xid_type_name(enum osmo_gprs_llc_xid_type val)
{
return get_value_string(osmo_gprs_llc_xid_type_names, val);
}
struct osmo_gprs_llc_xid_field {
enum osmo_gprs_llc_xid_type type;
/* Fixed-length value */
uint32_t val;
/* Variable-length value */
struct {
const uint8_t *val;
uint8_t val_len;
} var;
};
/* Section 4.5.2 Logical Link States + Annex C.2 */
enum osmo_gprs_llc_lle_state {
OSMO_GPRS_LLC_LLES_UNASSIGNED = 1, /* No TLLI yet */
OSMO_GPRS_LLC_LLES_ASSIGNED_ADM = 2, /* TLLI assigned */
OSMO_GPRS_LLC_LLES_LOCAL_EST = 3, /* Local Establishment */
OSMO_GPRS_LLC_LLES_REMOTE_EST = 4, /* Remote Establishment */
OSMO_GPRS_LLC_LLES_ABM = 5,
OSMO_GPRS_LLC_LLES_LOCAL_REL = 6, /* Local Release */
OSMO_GPRS_LLC_LLES_TIMER_REC = 7, /* Timer Recovery */
};
enum osmo_gprs_llc_llme_state {
OSMO_GPRS_LLC_LLMS_UNASSIGNED = 1, /* No TLLI yet */
OSMO_GPRS_LLC_LLMS_ASSIGNED = 2, /* TLLI assigned */
};
/* Section 8.9.9 LLC layer parameter default values */
struct osmo_gprs_llc_params {
uint16_t iov_i_exp;
uint16_t t200_201;
uint16_t n200;
uint16_t n201_u;
uint16_t n201_i;
uint16_t mD;
uint16_t mU;
uint16_t kD;
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);
bool osmo_gprs_llc_xid_field_is_valid(const struct osmo_gprs_llc_xid_field *field);
int osmo_gprs_llc_xid_decode(struct osmo_gprs_llc_xid_field *fields,
unsigned int max_fields,
const uint8_t *data, size_t data_len);
int osmo_gprs_llc_xid_encode(struct msgb *msg,
const struct osmo_gprs_llc_xid_field *fields,
unsigned int num_fields);
int osmo_gprs_llc_init(enum osmo_gprs_llc_location location);
enum osmo_gprs_llc_log_cat {
OSMO_GPRS_LLC_LOGC_LLC,
@ -190,8 +41,3 @@ enum osmo_gprs_llc_log_cat {
};
void osmo_gprs_llc_set_log_cat(enum osmo_gprs_llc_log_cat logc, int logc_num);
/* TODO: move to llc_private.h */
extern int g_llc_log_cat[_OSMO_GPRS_LLC_LOGC_MAX];
#define LOGLLC(lvl, fmt, args...) LOGP(g_llc_log_cat[OSMO_GPRS_LLC_LOGC_LLC], lvl, fmt, ## args)

View File

@ -7,6 +7,8 @@
#include <osmocom/core/prim.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <osmocom/gprs/llc/llc.h>
/* Section 7.1.0 */
enum osmo_gprs_llc_prim_sap {
@ -39,6 +41,12 @@ enum osmo_gprs_llc_llgmm_prim_type {
OSMO_GPRS_LLC_LLGMM_ASSIGN_UP, /* Req: TLLI */
};
extern const struct value_string osmo_gprs_llc_llgmm_prim_type_names[];
static inline const char *osmo_gprs_llc_llgmm_prim_type_name(enum osmo_gprs_llc_llgmm_prim_type val)
{
return get_value_string(osmo_gprs_llc_llgmm_prim_type_names, val);
}
/* TS 04.65 Section 7.2.2 "Layer 3 - LLE primitives" */
enum osmo_gprs_llc_ll_prim_type {
OSMO_GPRS_LLC_LL_RESET, /* Ind: TLLI */
@ -50,18 +58,37 @@ enum osmo_gprs_llc_ll_prim_type {
OSMO_GPRS_LLC_LL_STATUS, /* Ind: TLLI, Cause */
};
extern const struct value_string osmo_gprs_llc_ll_prim_type_names[];
static inline const char *osmo_gprs_llc_ll_prim_type_name(enum osmo_gprs_llc_ll_prim_type val)
{
return get_value_string(osmo_gprs_llc_ll_prim_type_names, val);
}
/* TS 04.65 Section 7.2.3 "LLE - RLC/MAC primitives" (MS side) */
enum osmo_gprs_llc_grr_prim_type {
OSMO_GPRS_LLC_GRR_DATA, /* Req/Ind: TLLI, LL-PDU, SAPI, Cause, QoS, Radio Prio */
OSMO_GPRS_LLC_GRR_UNITDATA, /* Req/Ind: TLLI, LL-PDU, SAPI, QoS, Radio Prio */
};
extern const struct value_string osmo_gprs_llc_grr_prim_type_names[];
static inline const char *osmo_gprs_llc_grr_prim_type_name(enum osmo_gprs_llc_grr_prim_type val)
{
return get_value_string(osmo_gprs_llc_grr_prim_type_names, val);
}
/* TS 04.65 Section 7.2.4 "LLE - BSSGP primitives" (SGSN side) */
enum osmo_gprs_llc_bssgp_prim_type {
OSMO_GPRS_LLC_BSSGP_DL_UNITDATA, /* Req: TLLI, LL-PDU, Cell Id, QoS, RLC Confirm, SAPI, ... */
OSMO_GPRS_LLC_BSSGP_UL_UNITDATA, /* Ind: TLLI, LL-PDU, Cell Id, edirect attempt, IMSI, V(U) for redirect, ... */
};
extern const struct value_string osmo_gprs_llc_bssgp_prim_type_names[];
static inline const char *osmo_gprs_llc_bssgp_prim_type_name(enum osmo_gprs_llc_bssgp_prim_type val)
{
return get_value_string(osmo_gprs_llc_bssgp_prim_type_names, val);
}
/* Parameters for OSMO_GPRS_LLC_LLGMM_* prims */
struct osmo_gprs_llc_llgmm_prim {
/* Common fields */
@ -108,7 +135,8 @@ struct osmo_gprs_llc_llgmm_prim {
struct osmo_gprs_llc_ll_prim {
/* Common fields */
uint32_t tlli;
/* OSMO_GPRS_LLC_LL_[UNIT]DATA */
enum osmo_gprs_llc_sapi sapi;
/* OSMO_GPRS_LLC_LL_[UNIT]DATA, OSMO_GPRS_LLC_LL_XID */
uint8_t *l3_pdu;
size_t l3_pdu_len;
/* Specific fields */
@ -133,7 +161,7 @@ struct osmo_gprs_llc_ll_prim {
} release_ind;
/* OSMO_GPRS_LLC_LL_XID | { Req, Ind, Rsp, Cnf } */
struct {
/* TODO: XID Req/Neg */
/* XID Req/Neg are encoded as buffer in l3_pdu + l3_pdu_len */
uint16_t n201_i; /* only for Ind & Cnf */
uint16_t n201_u; /* only for Ind & Cnf */
} xid;
@ -194,7 +222,7 @@ struct osmo_gprs_llc_bssgp_prim {
size_t ll_pdu_len;
/* Specific fields */
union {
/* OSMO_GPRS_LLC_BSSGP_UNITDATA | Req */
/* OSMO_GPRS_LLC_BSSGP_DL_UNITDATA | Req */
struct {
uint8_t qos_params[3];
bool rlc_confirm;
@ -205,15 +233,15 @@ struct osmo_gprs_llc_bssgp_prim {
* - GMM cause
* - V(U) for redirect
* - Redirect complete */
} unitdata_req;
/* OSMO_GPRS_LLC_BSSGP_UNITDATA | Ind */
} dl_unitdata_req;
/* OSMO_GPRS_LLC_BSSGP_UL_UNITDATA | Ind */
struct {
uint16_t cell_id;
struct gsm0808_cell_id cell_id;
/* TODO: MOCN specific parameters:
* - Redirect attempt
* - IMSI
* - V(U) for redirect */
} unitdata_ind;
} ul_unitdata_ind;
};
};
@ -226,3 +254,30 @@ struct osmo_gprs_llc_prim {
struct osmo_gprs_llc_bssgp_prim bssgp;
};
};
typedef int (*osmo_gprs_llc_prim_up_cb)(struct osmo_gprs_llc_prim *llc_prim, void *up_user_data);
void osmo_gprs_llc_prim_set_up_cb(osmo_gprs_llc_prim_up_cb up_cb, void *up_user_data);
typedef int (*osmo_gprs_llc_prim_down_cb)(struct osmo_gprs_llc_prim *llc_prim, void *down_user_data);
void osmo_gprs_llc_prim_set_down_cb(osmo_gprs_llc_prim_down_cb down_cb, void *down_user_data);
int osmo_gprs_llc_prim_upper_down(struct osmo_gprs_llc_prim *llc_prim);
int osmo_gprs_llc_prim_lower_up(struct osmo_gprs_llc_prim *llc_prim);
const char *osmo_gprs_llc_prim_name(const struct osmo_gprs_llc_prim *llc_prim);
/* Alloc primitive for LLGMM SAP: */
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_llgm_assign_req(uint32_t tlli);
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_llgm_reset_req(uint32_t tlli);
/* Alloc primitive for LL SAP: */
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_ll_xid_req(uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi,
uint8_t *l3_par, unsigned int l3_par_len);
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_ll_xid_resp(uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi,
uint8_t *l3_par, unsigned int l3_par_len);
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_ll_unitdata_req(uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi,
uint8_t *l3_pdu, size_t l3_pdu_len);
/* Alloc primitive for BSSGP SAP: */
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_bssgp_ul_unitdata_ind(
uint32_t tlli, uint8_t *ll_pdu, size_t ll_pdu_len);

View File

@ -0,0 +1,399 @@
#pragma once
/* 3GPP TS 44.064, private header */
#include <stdint.h>
#include <stddef.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/msgb.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/gprs/llc/llc_prim.h>
#include <osmocom/gprs/llc/llc.h>
extern int g_llc_log_cat[_OSMO_GPRS_LLC_LOGC_MAX];
#define LOGLLC(lvl, fmt, args...) LOGP(g_llc_log_cat[OSMO_GPRS_LLC_LOGC_LLC], lvl, fmt, ## args)
#define GPRS_LLME_RESET_AGE (0)
/* 3GPP TS 44.064 § 8.3 TLLI assignment procedures */
#define TLLI_UNASSIGNED (0xffffffff)
/* GSM 04.08 - 10.5.1.2 */
#define GSM_KEY_SEQ_INVAL 7
#define CRC24_LENGTH 3
#define UI_HDR_LEN 3
#define N202 4
#define msgb_llc_prim(msg) ((struct osmo_gprs_llc_prim *)(msg)->l1h)
/* Section 6.3.0 Control field formats */
enum gprs_llc_frame_fmt {
OSMO_GPRS_LLC_FMT_I, /* 6.3.1 Information transfer format - I */
OSMO_GPRS_LLC_FMT_S, /* 6.3.2 Supervisory format - S */
OSMO_GPRS_LLC_FMT_UI, /* 6.3.3 Unconfirmed Information format - UI */
OSMO_GPRS_LLC_FMT_U, /* 6.3.4 Unnumbered format - U */
};
extern const struct value_string gprs_llc_frame_fmt_names[];
static inline const char *gprs_llc_frame_fmt_name(enum gprs_llc_frame_fmt val)
{
return get_value_string(gprs_llc_frame_fmt_names, val);
}
/* Section 6.4 Commands and responses */
enum gprs_llc_frame_func {
/* 6.4.1 Unnumbered (U) frames */
OSMO_GPRS_LLC_FUNC_SABM, /* 6.4.1.1 */
OSMO_GPRS_LLC_FUNC_DISC, /* 6.4.1.2 */
OSMO_GPRS_LLC_FUNC_UA, /* 6.4.1.3 */
OSMO_GPRS_LLC_FUNC_DM, /* 6.4.1.4 */
OSMO_GPRS_LLC_FUNC_FRMR, /* 6.4.1.5 */
OSMO_GPRS_LLC_FUNC_XID, /* 6.4.1.6 */
OSMO_GPRS_LLC_FUNC_NULL, /* 6.4.1.7 */
/* 6.4.2 Unconfirmed Information (UI) frame */
OSMO_GPRS_LLC_FUNC_UI, /* 6.4.2.1 */
OSMO_GPRS_LLC_FUNC_UI_DUMMY, /* 6.4.2.2 */
/* 6.4.3 Combined Information (I) and Supervisory (S) frames */
OSMO_GPRS_LLC_FUNC_RR, /* 6.4.3.1 */
OSMO_GPRS_LLC_FUNC_ACK, /* 6.4.3.2 */
OSMO_GPRS_LLC_FUNC_SACK, /* 6.4.3.3 */
OSMO_GPRS_LLC_FUNC_RNR, /* 6.4.3.4 */
};
extern const struct value_string gprs_llc_frame_func_names[];
static inline const char *gprs_llc_frame_func_name(enum gprs_llc_frame_func val)
{
return get_value_string(gprs_llc_frame_func_names, val);
}
/* Section 6.4.1.6 / Table 6 */
enum gprs_llc_xid_type {
OSMO_GPRS_LLC_XID_T_VERSION = 0,
OSMO_GPRS_LLC_XID_T_IOV_UI = 1,
OSMO_GPRS_LLC_XID_T_IOV_I = 2,
OSMO_GPRS_LLC_XID_T_T200 = 3,
OSMO_GPRS_LLC_XID_T_N200 = 4,
OSMO_GPRS_LLC_XID_T_N201_U = 5,
OSMO_GPRS_LLC_XID_T_N201_I = 6,
OSMO_GPRS_LLC_XID_T_mD = 7,
OSMO_GPRS_LLC_XID_T_mU = 8,
OSMO_GPRS_LLC_XID_T_kD = 9,
OSMO_GPRS_LLC_XID_T_kU = 10,
OSMO_GPRS_LLC_XID_T_L3_PAR = 11,
OSMO_GPRS_LLC_XID_T_RESET = 12,
OSMO_GPRS_LLC_XID_T_IIOV_UI = 13,
OSMO_GPRS_LLC_XID_T_IIOV_UI_CNT = 14,
OSMO_GPRS_LLC_XID_T_MAC_IOV_UI = 15,
};
extern const struct value_string gprs_llc_xid_type_names[];
static inline const char *gprs_llc_xid_type_name(enum gprs_llc_xid_type val)
{
return get_value_string(gprs_llc_xid_type_names, val);
}
struct gprs_llc_xid_field {
enum gprs_llc_xid_type type;
/* Fixed-length value */
uint32_t val;
/* Variable-length value */
struct {
uint8_t *val;
uint8_t val_len;
} var;
};
/* Section 4.5.2 Logical Link States + Annex C.2 */
enum gprs_llc_lle_state {
OSMO_GPRS_LLC_LLES_UNASSIGNED = 1, /* No TLLI yet */
OSMO_GPRS_LLC_LLES_ASSIGNED_ADM = 2, /* TLLI assigned */
OSMO_GPRS_LLC_LLES_LOCAL_EST = 3, /* Local Establishment */
OSMO_GPRS_LLC_LLES_REMOTE_EST = 4, /* Remote Establishment */
OSMO_GPRS_LLC_LLES_ABM = 5,
OSMO_GPRS_LLC_LLES_LOCAL_REL = 6, /* Local Release */
OSMO_GPRS_LLC_LLES_TIMER_REC = 7, /* Timer Recovery */
};
extern const struct value_string gprs_llc_lle_state_names[];
static inline const char *gprs_llc_lle_state_name(enum gprs_llc_lle_state val)
{
return get_value_string(gprs_llc_lle_state_names, val);
}
enum gprs_llc_llme_state {
OSMO_GPRS_LLC_LLMS_UNASSIGNED = 1, /* No TLLI yet */
OSMO_GPRS_LLC_LLMS_ASSIGNED = 2, /* TLLI assigned */
};
extern const struct value_string gprs_llc_llme_state_names[];
static inline const char *gprs_llc_llme_state_name(enum gprs_llc_llme_state val)
{
return get_value_string(gprs_llc_llme_state_names, val);
}
/* Section 8.9.9 LLC layer parameter default values */
struct gprs_llc_params {
uint16_t iov_i_exp;
uint16_t t200_201;
uint16_t n200;
uint16_t n201_u;
uint16_t n201_i;
uint16_t mD;
uint16_t mU;
uint16_t kD;
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 gprs_llc_pdu_decoded {
enum osmo_gprs_llc_sapi sapi;
enum gprs_llc_frame_fmt fmt;
enum 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;
uint8_t *data;
};
struct gprs_llc_llme;
/* 3GPP TS 44.064 § 4.7.1: Logical Link Entity: One per DLCI (TLLI + SAPI) */
struct gprs_llc_lle {
struct llist_head list;
uint32_t sapi;
struct gprs_llc_llme *llme; /* backpointer to the Logical Link Management Entity */
enum gprs_llc_lle_state state;
struct osmo_timer_list t200;
struct osmo_timer_list t201; /* wait for acknowledgement */
uint16_t v_sent;
uint16_t v_ack;
uint16_t v_recv;
uint16_t vu_send;
uint16_t vu_recv;
/* non-standard LLC state */
uint16_t vu_recv_last;
uint16_t vu_recv_duplicates;
/* Overflow Counter for ABM */
uint32_t oc_i_send;
uint32_t oc_i_recv;
/* Overflow Counter for unconfirmed transfer */
uint32_t oc_ui_send;
uint32_t oc_ui_recv;
unsigned int retrans_ctr;
struct gprs_llc_params params;
/* Copy of the XID fields array we have sent with the last
* network originated XID-Request. Since the phone
* may strip the optional fields in the confirmation
* we need to remeber those fields in order to be
* able to create the compression entity. */
struct gprs_llc_xid_field *xid;
unsigned int xid_len;
/* Copy of last XID-Request fields array received from the peer: We need
to remember it to be able to send an Xid-Response back when SNDCP
provides us with layer3 XID field */
struct gprs_llc_xid_field *rx_xid;
unsigned int rx_xid_len;
};
#define NUM_SAPIS 16
/* 3GPP TS 44.064 § 4.7.3: Logical Link Management Entity: One per TLLI */
struct gprs_llc_llme {
struct llist_head list;
enum gprs_llc_llme_state state;
uint32_t tlli;
uint32_t old_tlli;
/* Crypto parameters */
enum gprs_ciph_algo algo;
uint8_t kc[16];
uint8_t cksn;
/* 3GPP TS 44.064 § 8.9.2: */
uint32_t iov_ui;
/* over which BSSGP BTS ctx do we need to transmit */
uint16_t bvci;
uint16_t nsei;
struct gprs_llc_lle lle[NUM_SAPIS];
/* Compression entities */
struct {
/* In these two list_heads we will store the
* data and protocol compression entities,
* together with their compression states */
struct llist_head *proto;
struct llist_head *data;
} comp;
/* Internal management */
uint32_t age_timestamp;
};
static inline struct gprs_llc_lle *gprs_llc_llme_get_lle(struct gprs_llc_llme *llme,
enum osmo_gprs_llc_sapi sapi) {
OSMO_ASSERT(sapi < NUM_SAPIS);
return &llme->lle[sapi];
}
struct gprs_llc_ctx {
enum osmo_gprs_llc_location location;
osmo_gprs_llc_prim_up_cb llc_up_cb;
void *llc_up_cb_user_data;
osmo_gprs_llc_prim_down_cb llc_down_cb;
void *llc_down_cb_user_data;
struct llist_head llme_list;
};
extern struct gprs_llc_ctx *g_ctx;
/* llc_bssgp.c */
int gprs_llc_prim_lower_up_bssgp(struct osmo_gprs_llc_prim *llc_prim);
struct osmo_gprs_llc_prim *gprs_llc_prim_alloc_bssgp_dl_unitdata_req(
uint32_t tlli, uint8_t *ll_pdu, size_t ll_pdu_len);
/* llc_grr.c */
int gprs_llc_prim_lower_up_grr(struct osmo_gprs_llc_prim *llc_prim);
/* llc_ll.c */
int gprs_llc_prim_ll_upper_down(struct osmo_gprs_llc_prim *llc_prim);
struct osmo_gprs_llc_prim *gprs_llc_prim_alloc_ll_xid_ind(uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi,
uint8_t *l3_par, unsigned int l3_par_len);
struct osmo_gprs_llc_prim *gprs_llc_prim_alloc_ll_xid_cnf(uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi,
uint8_t *l3_par, unsigned int l3_par_len);
struct osmo_gprs_llc_prim *gprs_llc_prim_alloc_ll_unitdata_ind(
uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi,
uint8_t *l3_pdu, size_t l3_pdu_len);
int gprs_llc_lle_submit_prim_ll_xid_ind(struct gprs_llc_lle *lle,
const struct gprs_llc_xid_field *xid_field_request_l3);
int gprs_llc_lle_submit_prim_ll_xid_cnf(struct gprs_llc_lle *lle,
const struct gprs_llc_xid_field *xid_field_response_l3,
const struct gprs_llc_xid_field *xid_field_request_l3);
/* llc_llgmm.c */
int gprs_llc_prim_llgmm_upper_down(struct osmo_gprs_llc_prim *llc_prim);
/* llc.c */
struct gprs_llc_llme *gprs_llc_llme_alloc(uint32_t tlli);
struct gprs_llc_llme *gprs_llc_find_llme_by_tlli(uint32_t tlli);
struct gprs_llc_lle *gprs_llc_find_lle_by_tlli_sapi(uint32_t tlli, uint8_t sapi);
struct gprs_llc_lle *gprs_llc_lle_for_rx_by_tlli_sapi(const uint32_t tlli,
uint8_t sapi, enum gprs_llc_frame_func cmd);
int gprs_llc_lle_hdr_rx(struct gprs_llc_lle *lle, struct gprs_llc_pdu_decoded *pdu_dec);
void gprs_llc_llme_free(struct gprs_llc_llme *llme);
int gprs_llc_lle_tx_xid(const struct gprs_llc_lle *lle, uint8_t *xid_payload, unsigned int xid_payload_len, bool is_cmd);
int gprs_llc_lle_tx_xid_req(struct gprs_llc_lle *lle, uint8_t *l3par, unsigned int l3par_len);
int gprs_llc_lle_tx_xid_resp(struct gprs_llc_lle *lle, uint8_t *l3par, unsigned int l3par_len);
int gprs_llc_lle_tx_ui(struct gprs_llc_lle *lle, uint8_t *l3_pdu, size_t l3_pdu_len, bool encryptable);
/* llc_prim.c: */
struct osmo_gprs_llc_prim *gprs_llc_prim_alloc(enum osmo_gprs_llc_prim_sap sap, unsigned int type,
enum osmo_prim_operation operation,
unsigned int l3_len);
int gprs_llc_prim_handle_unsupported(struct osmo_gprs_llc_prim *llc_prim);
int gprs_llc_prim_call_down_cb(struct osmo_gprs_llc_prim *llc_prim);
int gprs_llc_prim_call_up_cb(struct osmo_gprs_llc_prim *llc_prim);
/* llc_xid.c: */
bool gprs_llc_xid_field_is_valid(const struct gprs_llc_xid_field *field);
int gprs_llc_xid_decode(struct gprs_llc_xid_field *fields,
unsigned int max_fields,
uint8_t *data, size_t data_len);
int gprs_llc_xid_encode(uint8_t *data, size_t data_len,
const struct gprs_llc_xid_field *fields,
unsigned int num_fields);
struct gprs_llc_xid_field *gprs_llc_xid_deepcopy(void *ctx,
const struct gprs_llc_xid_field *src_xid,
size_t src_xid_len);
/* llc_pdu.c: */
int gprs_llc_pdu_decode(struct gprs_llc_pdu_decoded *pdu,
uint8_t *data, size_t data_len);
int gprs_llc_pdu_encode(struct msgb *msg, const struct gprs_llc_pdu_decoded *pdu);
void gprs_llc_pdu_hdr_dump_buf(char *buf, size_t buf_size,
const struct gprs_llc_pdu_decoded *pdu);
const char *gprs_llc_pdu_hdr_dump(const struct gprs_llc_pdu_decoded *pdu);
uint32_t gprs_llc_fcs(const uint8_t *data, size_t len);
/**
* \short Check if N(U) should be considered a retransmit
*
* Implements the range check as of GSM 04.64 8.4.2
* Receipt of unacknowledged information.
*
* @returns Returns 1 if (V(UR)-32) <= N(U) < V(UR)
* @param nu N(U) unconfirmed sequence number of the UI frame
* @param vur V(UR) unconfirmend received state variable
*/
static inline int gprs_llc_is_retransmit(uint16_t nu, uint16_t vur)
{
int delta = (vur - nu) & 0x1ff;
return 0 < delta && delta < 32;
}
/* 6.2.2 Command/Response bit (C/R) */
static inline bool gprs_llc_received_cr_is_cmd(uint8_t cr)
{
if (g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN)
return !cr; /*received from MS */
else
return !!cr; /*received from SGSN */
}
static inline void gprs_llc_encode_is_cmd_as_cr(bool is_cmd, uint32_t *flags)
{
if (g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN)
*flags |= OSMO_GPRS_LLC_PDU_F_CMD_RSP; /*Transmit to MS */
else
*flags &= ~OSMO_GPRS_LLC_PDU_F_CMD_RSP; /* Transmit to SGSN */
}
#define LOGLLME(llme, level, fmt, args...) \
LOGLLC(level, "LLME(%08x/%08x){%s} " fmt, (llme)->old_tlli, \
(llme)->tlli, gprs_llc_llme_state_name((llme)->state), ## args)
#define LOGLLE(lle, level, fmt, args...) \
LOGLLC(level, "LLE(%08x/%08x,%s){%s} " fmt, \
(lle)->llme->old_tlli, (lle)->llme->tlli, \
osmo_gprs_llc_sapi_name((lle)->sapi), \
gprs_llc_lle_state_name((lle)->state), ## args)

View File

@ -25,6 +25,11 @@ lib_LTLIBRARIES = \
libosmo_gprs_llc_la_SOURCES = \
crc24.c \
llc.c \
llc_bssgp.c \
llc_ll.c \
llc_llgmm.c \
llc_grr.c \
llc_pdu.c \
llc_prim.c \
llc_xid.c \

645
src/llc/llc.c Normal file
View File

@ -0,0 +1,645 @@
/* GPRS LLC as per 3GPP TS 44.064 */
/*
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <errno.h>
#include <arpa/inet.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gprs/llc/llc.h>
#include <osmocom/gprs/llc/llc_prim.h>
#include <osmocom/gprs/llc/llc_private.h>
struct gprs_llc_ctx *g_ctx;
const struct value_string gprs_llc_llme_state_names[] = {
{ OSMO_GPRS_LLC_LLMS_UNASSIGNED, "UNASSIGNED" },
{ OSMO_GPRS_LLC_LLMS_ASSIGNED, "ASSIGNED" },
{ 0, NULL }
};
const struct value_string gprs_llc_lle_state_names[] = {
{ OSMO_GPRS_LLC_LLES_UNASSIGNED, "UNASSIGNED" },
{ OSMO_GPRS_LLC_LLES_ASSIGNED_ADM, "ASSIGNED_ADM" },
{ OSMO_GPRS_LLC_LLES_LOCAL_EST, "LOCAL_EST" },
{ OSMO_GPRS_LLC_LLES_REMOTE_EST, "REMOTE_EST" },
{ OSMO_GPRS_LLC_LLES_ABM, "ABM" },
{ OSMO_GPRS_LLC_LLES_LOCAL_REL, "LOCAL_REL" },
{ OSMO_GPRS_LLC_LLES_TIMER_REC, "TIMER_REC" },
{ 0, NULL }
};
/* Section 8.9.9 LLC layer parameter default values */
static const struct gprs_llc_params llc_default_params[NUM_SAPIS] = {
[1] = {
.t200_201 = 5,
.n200 = 3,
.n201_u = 400,
},
[2] = {
.t200_201 = 5,
.n200 = 3,
.n201_u = 270,
},
[3] = {
.iov_i_exp = 27,
.t200_201 = 5,
.n200 = 3,
.n201_u = 500,
.n201_i = 1503,
.mD = 1520,
.mU = 1520,
.kD = 16,
.kU = 16,
},
[5] = {
.iov_i_exp = 27,
.t200_201 = 10,
.n200 = 3,
.n201_u = 500,
.n201_i = 1503,
.mD = 760,
.mU = 760,
.kD = 8,
.kU = 8,
},
[7] = {
.t200_201 = 20,
.n200 = 3,
.n201_u = 270,
},
[8] = {
.t200_201 = 20,
.n200 = 3,
.n201_u = 270,
},
[9] = {
.iov_i_exp = 27,
.t200_201 = 20,
.n200 = 3,
.n201_u = 500,
.n201_i = 1503,
.mD = 380,
.mU = 380,
.kD = 4,
.kU = 4,
},
[11] = {
.iov_i_exp = 27,
.t200_201 = 40,
.n200 = 3,
.n201_u = 500,
.n201_i = 1503,
.mD = 190,
.mU = 190,
.kD = 2,
.kU = 2,
},
};
int osmo_gprs_llc_init(enum osmo_gprs_llc_location location)
{
OSMO_ASSERT(location == OSMO_GPRS_LLC_LOCATION_MS || location == OSMO_GPRS_LLC_LOCATION_SGSN)
if (g_ctx)
talloc_free(g_ctx);
g_ctx = talloc_zero(NULL, struct gprs_llc_ctx);
g_ctx->location = location;
INIT_LLIST_HEAD(&g_ctx->llme_list);
return 0;
}
static void lle_init(struct gprs_llc_llme *llme, uint8_t sapi)
{
struct gprs_llc_lle *lle = &llme->lle[sapi];
lle->llme = llme;
lle->sapi = sapi;
lle->state = OSMO_GPRS_LLC_LLES_UNASSIGNED;
/* Initialize according to parameters */
memcpy(&lle->params, &llc_default_params[sapi], sizeof(lle->params));
}
struct gprs_llc_llme *gprs_llc_llme_alloc(uint32_t tlli)
{
struct gprs_llc_llme *llme;
uint32_t i;
llme = talloc_zero(g_ctx, struct gprs_llc_llme);
if (!llme)
return NULL;
llme->tlli = tlli;
llme->old_tlli = TLLI_UNASSIGNED;
llme->state = OSMO_GPRS_LLC_LLMS_UNASSIGNED;
llme->age_timestamp = GPRS_LLME_RESET_AGE;
llme->cksn = GSM_KEY_SEQ_INVAL;
for (i = 0; i < ARRAY_SIZE(llme->lle); i++)
lle_init(llme, i);
llist_add(&llme->list, &g_ctx->llme_list);
//llme->comp.proto = gprs_sndcp_comp_alloc(llme);
//llme->comp.data = gprs_sndcp_comp_alloc(llme);
return llme;
}
void gprs_llc_llme_free(struct gprs_llc_llme *llme)
{
/* TODO: here we probably need to trigger LLGMM-RESET or LLGMM-SUSPEND towards upper layers! */
//gprs_sndcp_sm_deactivate_ind_by_llme(llme);
//gprs_sndcp_comp_free(llme->comp.proto);
//gprs_sndcp_comp_free(llme->comp.data);
llist_del(&llme->list);
talloc_free(llme);
}
/* lookup LLC Entity based on TLLI */
struct gprs_llc_llme *gprs_llc_find_llme_by_tlli(uint32_t tlli)
{
struct gprs_llc_llme *llme;
llist_for_each_entry(llme, &g_ctx->llme_list, list) {
if (llme->tlli == tlli || llme->old_tlli == tlli)
return llme;
}
return NULL;
}
/* lookup LLC Entity based on DLCI (TLLI+SAPI tuple) */
struct gprs_llc_lle *gprs_llc_find_lle_by_tlli_sapi(uint32_t tlli, uint8_t sapi)
{
struct gprs_llc_llme *llme = gprs_llc_find_llme_by_tlli(tlli);
if (!llme)
return NULL;
return &llme->lle[sapi];
}
static int gprs_llc_lle_tx_dm(const struct gprs_llc_lle *lle)
{
int rc;
struct msgb *msg;
struct gprs_llc_pdu_decoded pdu_dec = {
.sapi = lle->sapi,
.fmt = OSMO_GPRS_LLC_FMT_U,
.func = OSMO_GPRS_LLC_FUNC_DM,
.flags = OSMO_GPRS_LLC_PDU_F_FOLL_FIN,
};
struct osmo_gprs_llc_prim *llc_prim;
/* LLC payload is put directly below: */
llc_prim = gprs_llc_prim_alloc_bssgp_dl_unitdata_req(lle->llme->tlli, NULL, 4096 - sizeof(llc_prim));
msg = llc_prim->oph.msg;
msg->l3h = msg->tail;
rc = gprs_llc_pdu_encode(msg, &pdu_dec);
if (rc < 0) {
LOGLLC(LOGL_NOTICE, "Failed to encode U DM\n");
msgb_free(msg);
return rc;
}
llc_prim->bssgp.ll_pdu = msgb_l3(msg);
llc_prim->bssgp.ll_pdu_len = msgb_l3len(msg);
/* Send BSSGP-DL-UNITDATA.req */
gprs_llc_prim_call_down_cb(llc_prim);
return 0;
}
int gprs_llc_lle_tx_xid(const struct gprs_llc_lle *lle, uint8_t *xid_payload, unsigned int xid_payload_len, bool is_cmd)
{
int rc;
struct msgb *msg;
struct osmo_gprs_llc_prim *llc_prim;
struct gprs_llc_pdu_decoded pdu_dec = {
.sapi = lle->sapi,
.fmt = OSMO_GPRS_LLC_FMT_U,
.func = OSMO_GPRS_LLC_FUNC_XID,
.flags = OSMO_GPRS_LLC_PDU_F_FOLL_FIN,
.data_len = xid_payload_len,
.data = xid_payload,
};
gprs_llc_encode_is_cmd_as_cr(is_cmd, &pdu_dec.flags);
/* LLC payload is put directly below: */
llc_prim = gprs_llc_prim_alloc_bssgp_dl_unitdata_req(lle->llme->tlli, NULL, 4096 - sizeof(llc_prim));
msg = llc_prim->oph.msg;
msg->l3h = msg->tail;
rc = gprs_llc_pdu_encode(msg, &pdu_dec);
if (rc < 0) {
LOGLLC(LOGL_NOTICE, "Failed to encode U DM\n");
msgb_free(msg);
return rc;
}
llc_prim->bssgp.ll_pdu = msgb_l3(msg);
llc_prim->bssgp.ll_pdu_len = msgb_l3len(msg);
/* Send BSSGP-DL-UNITDATA.req */
gprs_llc_prim_call_down_cb(llc_prim);
return 0;
}
/* Transmit a UI frame over the given SAPI:
'encryptable' indicates whether particular message can be encrypted according
to 3GPP TS 24.008 § 4.7.1.2
*/
int gprs_llc_lle_tx_ui(struct gprs_llc_lle *lle, uint8_t *l3_pdu, size_t l3_pdu_len, bool encryptable)
{
struct osmo_gprs_llc_prim *llc_prim;
struct msgb *msg;
int rc;
struct gprs_llc_pdu_decoded pdu_dec = {
.sapi = lle->sapi,
.fmt = OSMO_GPRS_LLC_FMT_UI,
.func = 0,
.flags = OSMO_GPRS_LLC_PDU_F_PROT_MODE,
.seq_tx = lle->vu_send,
.data_len = l3_pdu_len,
.data = l3_pdu,
};
gprs_llc_encode_is_cmd_as_cr(false, &pdu_dec.flags);
//TODO: what to do with:
// oc = lle->oc_ui_send;
if (pdu_dec.data_len > lle->params.n201_u) {
LOGLLE(lle, LOGL_ERROR, "Cannot Tx %zu bytes (N201-U=%u)\n",
pdu_dec.data_len, lle->params.n201_u);
return -EFBIG;
}
if (lle->llme->algo != GPRS_ALGO_GEA0 && encryptable)
pdu_dec.flags |= OSMO_GPRS_LLC_PDU_F_ENC_MODE;
/* TODO: we are probably missing the ciphering enc part, see osmo-sgsn apply_gea() */
/* LLC payload is put directly below: */
llc_prim = gprs_llc_prim_alloc_bssgp_dl_unitdata_req(lle->llme->tlli, NULL, 4096 - sizeof(llc_prim));
msg = llc_prim->oph.msg;
msg->l3h = msg->tail;
rc = gprs_llc_pdu_encode(msg, &pdu_dec);
if (rc < 0) {
LOGLLE(lle, LOGL_NOTICE, "Failed to encode U DM\n");
msgb_free(msg);
return rc;
}
llc_prim->bssgp.ll_pdu = msgb_l3(msg);
llc_prim->bssgp.ll_pdu_len = msgb_l3len(msg);
/* Increment V(U) */
lle->vu_send = (lle->vu_send + 1) % 512;
/* Increment Overflow Counter, if needed */
if ((lle->vu_send + 1) / 512)
lle->oc_ui_send += 512;
/* Send BSSGP-DL-UNITDATA.req */
rc = gprs_llc_prim_call_down_cb(llc_prim);
return rc;
}
/* lookup LLC Entity for RX based on DLCI (TLLI+SAPI tuple) */
struct gprs_llc_lle *gprs_llc_lle_for_rx_by_tlli_sapi(const uint32_t tlli,
uint8_t sapi, enum gprs_llc_frame_func cmd)
{
struct gprs_llc_lle *lle;
/* We already know about this TLLI */
lle = gprs_llc_find_lle_by_tlli_sapi(tlli, sapi);
if (lle)
return lle;
/* Maybe it is a routing area update but we already know this sapi? */
if (gprs_tlli_type(tlli) == TLLI_FOREIGN) {
lle = gprs_llc_find_lle_by_tlli_sapi(tlli, sapi);
if (lle) {
LOGLLC(LOGL_NOTICE,
"LLC RX: Found a local entry for TLLI 0x%08x\n", tlli);
return lle;
}
}
/* 7.2.1.1 LLC belonging to unassigned TLLI+SAPI shall be discarded,
* except UID and XID frames with SAPI=1 */
if (sapi == OSMO_GPRS_LLC_SAPI_GMM &&
(cmd == OSMO_GPRS_LLC_FUNC_XID || cmd == OSMO_GPRS_LLC_FUNC_UI)) {
struct gprs_llc_llme *llme;
/* FIXME: don't use the TLLI but the 0xFFFF unassigned? */
llme = gprs_llc_llme_alloc(tlli);
LOGLLME(llme, LOGL_NOTICE, "LLC RX: unknown TLLI 0x%08x, "
"creating LLME on the fly\n", tlli);
lle = &llme->lle[sapi];
return lle;
}
LOGLLC(LOGL_NOTICE, "unknown TLLI(0x%08x)/SAPI(%d): Silently dropping\n",
tlli, sapi);
return NULL;
}
/* Generate XID message */
static int gprs_llc_lle_generate_xid(struct gprs_llc_lle *lle, uint8_t *bytes, int bytes_len,
const uint8_t *l3par, unsigned int l3par_len)
{
unsigned int xid_fields_len = 3 + (l3par ? 1 : 0);
struct gprs_llc_xid_field *xid_fields =
(struct gprs_llc_xid_field *)talloc_zero_size(lle->llme, sizeof(*xid_fields) * xid_fields_len);
int rc;
OSMO_ASSERT(xid_fields);
xid_fields[0].type = OSMO_GPRS_LLC_XID_T_VERSION;
xid_fields[0].val = 0;
xid_fields[1].type = OSMO_GPRS_LLC_XID_T_N201_U;
xid_fields[1].val = lle->params.n201_u;
xid_fields[2].type = OSMO_GPRS_LLC_XID_T_N201_I;
xid_fields[2].val = lle->params.n201_i;
if (l3par_len > 0) {
xid_fields[3].type = OSMO_GPRS_LLC_XID_T_L3_PAR;
xid_fields[3].var.val_len = l3par_len;
if (l3par_len > 0) {
xid_fields[3].var.val = talloc_size(xid_fields, l3par_len);
memcpy(xid_fields[3].var.val, l3par, l3par_len);
}
} else {
xid_fields_len--;
}
/* Store generated XID for later reference */
talloc_free(lle->xid);
lle->xid = xid_fields;
lle->xid_len = xid_fields_len;
rc = gprs_llc_xid_encode(bytes, bytes_len, lle->xid, lle->xid_len);
return rc;
}
/* Set of LL-XID negotiation (See also: TS 101 351, Section 7.2.2.4) */
int gprs_llc_lle_tx_xid_req(struct gprs_llc_lle *lle, uint8_t *l3par, unsigned int l3par_len)
{
uint8_t xid_bytes[1024];
int xid_bytes_len;
int rc;
/* Generate XID */
xid_bytes_len =
gprs_llc_lle_generate_xid(lle, xid_bytes, sizeof(xid_bytes), l3par, l3par_len);
/* Only perform XID sending if the XID message contains something */
if (xid_bytes_len > 0) {
/* Transmit XID bytes */
LOGLLE(lle, LOGL_NOTICE, "Sending XID type %s (%d bytes) request to MS...\n",
l3par ? "L3-Params" : "NULL", xid_bytes_len);
rc = gprs_llc_lle_tx_xid(lle, xid_bytes, xid_bytes_len, true);
} else {
LOGLLE(lle, LOGL_ERROR,
"XID-Message generation failed, XID not sent!\n");
rc = -EINVAL;
}
return rc;
}
/* Generate XID response from XID-Ind received from peer */
int gprs_llc_lle_tx_xid_resp(struct gprs_llc_lle *lle, uint8_t *l3par, unsigned int l3par_len)
{
uint8_t bytes_response[1024];
unsigned int rc, i;
/* Replace the SNDCP L3 xid_field with response from our upper layer: */
for (i = 0; i < lle->rx_xid_len; i++) {
struct gprs_llc_xid_field *xid_field_l3;
if (lle->rx_xid[i].type != OSMO_GPRS_LLC_XID_T_L3_PAR)
continue;
xid_field_l3 = &lle->rx_xid[i];
xid_field_l3->var.val = l3par;
xid_field_l3->var.val_len = l3par_len;
break;
}
rc = gprs_llc_xid_encode(bytes_response, sizeof(bytes_response),
lle->rx_xid, lle->rx_xid_len);
TALLOC_FREE(lle->rx_xid);
lle->rx_xid_len = 0;
if (rc < 0)
return rc;
rc = gprs_llc_lle_tx_xid(lle, bytes_response, rc, false);
return rc;
}
/* Process an incoming XID indication and generate an appropriate response */
static int gprs_llc_lle_process_xid_ind(struct gprs_llc_lle *lle,
uint8_t *bytes_request,
int bytes_request_len)
{
/* Note: This function computes the response that is sent back to the
* MS when a mobile originated XID is received. */
struct gprs_llc_xid_field xid_fields[16] = { 0 };
unsigned int xid_fields_len;
int rc, i;
struct gprs_llc_xid_field *xid_field_request_l3 = NULL;
/* Parse and analyze XID-Request */
rc = gprs_llc_xid_decode(xid_fields, ARRAY_SIZE(xid_fields),
bytes_request, bytes_request_len);
if (rc < 0) {
LOGLLE(lle, LOGL_ERROR, "Failed decoding XID Fields\n");
return rc;
}
xid_fields_len = rc;
/* FIXME: Check the incoming XID parameters for
* for validity. Currently we just blindly
* accept all XID fields by just echoing them.
* There is a remaining risk of malfunction
* when a MS submits values which defer from
* the default! */
/* Store last received XID-Ind from peer: */
lle->rx_xid = gprs_llc_xid_deepcopy(lle->llme, xid_fields, xid_fields_len);
OSMO_ASSERT(lle->rx_xid);
lle->rx_xid_len = xid_fields_len;
/* Forward SNDCP-XID fields to Layer 3 (SNDCP) */
for (i = 0; i < xid_fields_len; i++) {
if (xid_fields[i].type == OSMO_GPRS_LLC_XID_T_L3_PAR) {
xid_field_request_l3 = &xid_fields[i];
gprs_llc_lle_submit_prim_ll_xid_ind(lle, xid_field_request_l3);
/* delay answer until we get LL-XID.resp from SNDCP: */
return rc;
}
}
rc = gprs_llc_lle_tx_xid_resp(lle, NULL, 0);
return rc;
}
/* Process an incoming XID confirmation. 8.5.3.0 */
static int gprs_llc_lle_process_xid_conf(struct gprs_llc_lle *lle, uint8_t *bytes, int bytes_len)
{
/* Note: This function handles the response of a network originated
* XID-Request. There XID messages reflected by the MS are analyzed
* and processed here. The caller is called by rx_llc_xid(). */
struct gprs_llc_xid_field xid_fields[16] = { 0 };
unsigned int xid_fields_len;
struct gprs_llc_xid_field *xid_field;
struct gprs_llc_xid_field *xid_field_request_l3 = NULL;
unsigned int i;
int rc;
/* Pick layer3 XID from the XID request we have sent last */
if (lle->xid) {
for (i = 0; i < lle->xid_len; i++) {
if (lle->xid[i].type == OSMO_GPRS_LLC_XID_T_L3_PAR)
xid_field_request_l3 = &lle->xid[i];
}
}
/* Parse and analyze XID-Response */
rc = gprs_llc_xid_decode(xid_fields, ARRAY_SIZE(xid_fields),
bytes, bytes_len);
if (rc < 0) {
LOGLLE(lle, LOGL_ERROR, "Failed decoding XID Fields\n");
return rc;
}
xid_fields_len = rc;
for (i = 0; i < xid_fields_len; i++) {
xid_field = &xid_fields[i];
/* Forward SNDCP-XID fields to Layer 3 (SNDCP) */
if (xid_field->type == OSMO_GPRS_LLC_XID_T_L3_PAR && xid_field_request_l3) {
gprs_llc_lle_submit_prim_ll_xid_cnf(lle, xid_field, xid_field_request_l3);
/* TODO: sndcp_sn_xid_conf is basically primitive LL-XID.cnf. See 8.5.3.0 */
} else { /* Process LLC-XID fields: */
/* FIXME: Do something more useful with the
* echoed XID-Information. Currently we
* just ignore the response completely and
* by doing so we blindly accept any changes
* the MS might have done to the our XID
* inquiry. There is a remainig risk of
* malfunction! */
LOGLLE(lle, LOGL_NOTICE,
"Ignoring XID-Field: XID: type %s\n",
gprs_llc_xid_type_name(xid_field->type));
}
}
/* Flush pending XID fields */
TALLOC_FREE(lle->xid);
lle->xid_len = 0;
return 0;
}
/* Dispatch XID indications and responses coming from the MS */
static int gprs_llc_lle_rx_llc_xid(struct gprs_llc_lle *lle, struct gprs_llc_pdu_decoded *pdu_dec)
{
int rc = 0;
/* FIXME: 8.5.3.3: check if XID is invalid */
if (gprs_llc_received_cr_is_cmd(pdu_dec->flags & OSMO_GPRS_LLC_PDU_F_CMD_RSP)) {
LOGLLE(lle, LOGL_NOTICE, "Received XID indication from MS.\n");
rc = gprs_llc_lle_process_xid_ind(lle, pdu_dec->data, pdu_dec->data_len);
} else {
LOGLLE(lle, LOGL_NOTICE, "Received XID confirmation from MS\n");
rc = gprs_llc_lle_process_xid_conf(lle, pdu_dec->data, pdu_dec->data_len);
/* FIXME: if we had sent a XID reset, send
* LLGMM-RESET.conf to GMM */
}
return rc;
}
/* Receive and process decoded LLC PDU from lower layer (GRR/BSSGP): */
int gprs_llc_lle_hdr_rx(struct gprs_llc_lle *lle, struct gprs_llc_pdu_decoded *pdu_dec)
{
const char *llc_pdu_name = gprs_llc_pdu_hdr_dump(pdu_dec);
LOGLLE(lle, LOGL_DEBUG, "Rx %s\n", llc_pdu_name);
switch (pdu_dec->func) {
case OSMO_GPRS_LLC_FUNC_SABM:
case OSMO_GPRS_LLC_FUNC_DISC:
/* send DM to properly signal we don't do ABM */
gprs_llc_lle_tx_dm(lle);
break;
case OSMO_GPRS_LLC_FUNC_XID: /* Section 6.4.1.6 */
gprs_llc_lle_rx_llc_xid(lle, pdu_dec);
break;
case OSMO_GPRS_LLC_FUNC_UI:
if (gprs_llc_is_retransmit(pdu_dec->seq_tx, lle->vu_recv)) {
LOGLLE(lle, LOGL_NOTICE,
"TLLI=%08x dropping UI, N(U=%d) not in window V(URV(UR:%d).\n",
lle->llme ? lle->llme->tlli : -1,
pdu_dec->seq_tx, lle->vu_recv);
/* HACK: non-standard recovery handling. If remote LLE
* is re-transmitting the same sequence number for
* three times, don't discard the frame but pass it on
* and 'learn' the new sequence number */
if (pdu_dec->seq_tx != lle->vu_recv_last) {
lle->vu_recv_last = pdu_dec->seq_tx;
lle->vu_recv_duplicates = 0;
} else {
lle->vu_recv_duplicates++;
if (lle->vu_recv_duplicates < 3)
return -EIO;
LOGLLE(lle, LOGL_NOTICE, "TLLI=%08x recovering "
"N(U=%d) after receiving %u duplicates\n",
lle->llme ? lle->llme->tlli : -1,
pdu_dec->seq_tx, lle->vu_recv_duplicates);
}
}
/* Increment the sequence number that we expect in the next frame */
lle->vu_recv = (pdu_dec->seq_tx + 1) % 512;
/* Increment Overflow Counter */
if ((pdu_dec->seq_tx + 1) / 512)
lle->oc_ui_recv += 512;
break;
case OSMO_GPRS_LLC_FUNC_NULL:
LOGLLE(lle, LOGL_DEBUG, "TLLI=%08x sends us LLC NULL\n", lle->llme ? lle->llme->tlli : -1);
break;
default:
LOGLLE(lle, LOGL_NOTICE, "Unhandled command: %s\n",
gprs_llc_frame_func_name(pdu_dec->func));
break;
}
return 0;
}

261
src/llc/llc_bssgp.c Normal file
View File

@ -0,0 +1,261 @@
/* GPRS LLC as per 3GPP TS 44.064 7.2.4 */
/*
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gprs/llc/llc.h>
#include <osmocom/gprs/llc/llc_prim.h>
#include <osmocom/gprs/llc/llc_private.h>
const struct value_string osmo_gprs_llc_bssgp_prim_type_names[] = {
{ OSMO_GPRS_LLC_BSSGP_DL_UNITDATA, "DL-UNITDATA" },
{ OSMO_GPRS_LLC_BSSGP_UL_UNITDATA, "UL-UNITDATA" },
{ 0, NULL }
};
/********************************
* Primitive allocation:
********************************/
static inline struct osmo_gprs_llc_prim *llc_prim_bssgp_alloc(enum osmo_gprs_llc_bssgp_prim_type type,
enum osmo_prim_operation operation,
unsigned int l3_len)
{
return gprs_llc_prim_alloc(OSMO_GPRS_LLC_SAP_BSSGP, type, operation, l3_len);
}
/* 7.2.4.1 BSSGP-UL-UNITDATA.ind (SGSN):*/
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_bssgp_ul_unitdata_ind(
uint32_t tlli, uint8_t *ll_pdu,
size_t ll_pdu_len)
{
struct osmo_gprs_llc_prim *llc_prim;
llc_prim = llc_prim_bssgp_alloc(OSMO_GPRS_LLC_BSSGP_UL_UNITDATA, PRIM_OP_INDICATION, ll_pdu_len);
llc_prim->bssgp.tlli = tlli;
llc_prim->bssgp.ll_pdu = ll_pdu;
llc_prim->bssgp.ll_pdu_len = ll_pdu_len;
return llc_prim;
}
/* 7.2.4.2 BSSGP-UL-UNITDATA.ind (SGSN):*/
struct osmo_gprs_llc_prim *gprs_llc_prim_alloc_bssgp_dl_unitdata_req(
uint32_t tlli, uint8_t *ll_pdu, size_t ll_pdu_len)
{
struct osmo_gprs_llc_prim *llc_prim;
llc_prim = llc_prim_bssgp_alloc(OSMO_GPRS_LLC_BSSGP_DL_UNITDATA, PRIM_OP_REQUEST, ll_pdu_len);
llc_prim->bssgp.tlli = tlli;
llc_prim->bssgp.ll_pdu = ll_pdu;
llc_prim->bssgp.ll_pdu_len = ll_pdu_len;
return llc_prim;
}
/********************************
* Handling from lower layers:
********************************/
/* encrypt information field + FCS, if needed! */
static int apply_gea(const struct gprs_llc_lle *lle, uint16_t crypt_len, uint16_t nu,
uint32_t oc, uint8_t sapi, uint8_t *fcs, uint8_t *data)
{
uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK];
if (lle->llme->algo == GPRS_ALGO_GEA0)
return -EINVAL;
/* Compute the 'Input' Paraemeter */
uint32_t fcs_calc, iv = gprs_cipher_gen_input_ui(lle->llme->iov_ui, sapi,
nu, oc);
/* Compute gamma that we need to XOR with the data */
int r = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo,
lle->llme->kc, iv,
fcs ? GPRS_CIPH_SGSN2MS : GPRS_CIPH_MS2SGSN);
if (r < 0) {
LOGLLC(LOGL_ERROR, "Error producing %s gamma for UI "
"frame: %d\n", get_value_string(gprs_cipher_names,
lle->llme->algo), r);
return -ENOMSG;
}
if (fcs) {
/* Mark frame as encrypted and update FCS */
data[2] |= 0x02;
fcs_calc = gprs_llc_fcs(data, fcs - data);
fcs[0] = fcs_calc & 0xff;
fcs[1] = (fcs_calc >> 8) & 0xff;
fcs[2] = (fcs_calc >> 16) & 0xff;
data += 3;
}
/* XOR the cipher output with the data */
for (r = 0; r < crypt_len; r++)
*(data + r) ^= cipher_out[r];
return 0;
}
static int gprs_llc_prim_handle_bssgp_ul_unitdata_ind(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
struct gprs_llc_hdr *lh = (struct gprs_llc_hdr *) llc_prim->bssgp.ll_pdu;
struct gprs_llc_pdu_decoded pdu_dec = {0};
const char *llc_pdu_name = NULL;
struct gprs_llc_lle *lle = NULL;
bool drop_cipherable = false;
struct osmo_gprs_llc_prim *llc_prim_tx;
rc = gprs_llc_pdu_decode(&pdu_dec, (uint8_t *)lh, llc_prim->bssgp.ll_pdu_len);
if (rc < 0) {
LOGLLC(LOGL_ERROR, "%s: Error parsing LLC header\n", osmo_gprs_llc_prim_name(llc_prim));
return rc;
}
llc_pdu_name = gprs_llc_pdu_hdr_dump(&pdu_dec);
LOGLLC(LOGL_DEBUG, "Rx %s: %s\n", osmo_gprs_llc_prim_name(llc_prim), llc_pdu_name);
switch (gprs_tlli_type(llc_prim->bssgp.tlli)) {
case TLLI_LOCAL:
case TLLI_FOREIGN:
case TLLI_RANDOM:
case TLLI_AUXILIARY:
break;
default:
LOGLLC(LOGL_ERROR, "%s: Discarding frame with strange TLLI type\n", llc_pdu_name);
return -EINVAL;
}
lle = gprs_llc_lle_for_rx_by_tlli_sapi(llc_prim->bssgp.tlli, pdu_dec.sapi, pdu_dec.func);
if (!lle) {
switch (pdu_dec.sapi) {
case OSMO_GPRS_LLC_SAPI_SNDCP3:
case OSMO_GPRS_LLC_SAPI_SNDCP5:
case OSMO_GPRS_LLC_SAPI_SNDCP9:
case OSMO_GPRS_LLC_SAPI_SNDCP11:
#if 0
/* TODO: probaby send some primitive to the upper layers (GMM) */
/* Ask an upper layer for help. */
return gsm0408_gprs_force_reattach_oldmsg(msg, NULL);
#endif
default:
break;
}
return 0;
}
/* reset age computation */
lle->llme->age_timestamp = GPRS_LLME_RESET_AGE;
/* decrypt information field + FCS, if needed! */
if (pdu_dec.flags & OSMO_GPRS_LLC_PDU_F_ENC_MODE) {
if (lle->llme->algo != GPRS_ALGO_GEA0) {
rc = apply_gea(lle, pdu_dec.data_len + 3, pdu_dec.seq_tx,
lle->oc_ui_recv, lle->sapi, NULL,
(uint8_t *)pdu_dec.data); /*TODO: either copy buffer or remove "const" from pdu_dec field "data" */
if (rc < 0)
return rc;
pdu_dec.fcs = *(pdu_dec.data + pdu_dec.data_len);
pdu_dec.fcs |= *(pdu_dec.data + pdu_dec.data_len + 1) << 8;
pdu_dec.fcs |= *(pdu_dec.data + pdu_dec.data_len + 2) << 16;
} else {
LOGLLME(lle->llme, LOGL_NOTICE, "encrypted frame for LLC that "
"has no KC/Algo! Dropping.\n");
return 0;
}
} else {
if (lle->llme->algo != GPRS_ALGO_GEA0 &&
lle->llme->cksn != GSM_KEY_SEQ_INVAL)
drop_cipherable = true;
}
/* We have to do the FCS check _after_ decryption */
uint16_t crc_length = llc_prim->bssgp.ll_pdu_len - CRC24_LENGTH;
if (~pdu_dec.flags & OSMO_GPRS_LLC_PDU_F_PROT_MODE)
crc_length = OSMO_MIN(crc_length, UI_HDR_LEN + N202);
if (pdu_dec.fcs != gprs_llc_fcs((uint8_t *)lh, crc_length)) {
LOGLLE(lle, LOGL_INFO, "Dropping frame with invalid FCS\n");
return -EIO;
}
/* Receive and Process the actual LLC frame */
rc = gprs_llc_lle_hdr_rx(lle, &pdu_dec);
if (rc < 0)
return rc;
/* pdu_dec.data is only set when we need to send LL_[UNIT]DATA_IND up */
if (pdu_dec.func == OSMO_GPRS_LLC_FUNC_UI && pdu_dec.data && pdu_dec.data_len) {
switch (pdu_dec.sapi) {
case OSMO_GPRS_LLC_SAPI_GMM:
/* send LL-UNITDATA-IND to GMM */
llc_prim_tx = gprs_llc_prim_alloc_ll_unitdata_ind(lle->llme->tlli,
pdu_dec.sapi,
pdu_dec.data,
pdu_dec.data_len);
llc_prim_tx->ll.unitdata_ind.apply_gea = !drop_cipherable; /* TODO: is this correct? */
llc_prim_tx->ll.unitdata_ind.apply_gia = false; /* TODO: how to set this? */
gprs_llc_prim_call_up_cb(llc_prim_tx);
break;
case OSMO_GPRS_LLC_SAPI_SNDCP3:
case OSMO_GPRS_LLC_SAPI_SNDCP5:
case OSMO_GPRS_LLC_SAPI_SNDCP9:
case OSMO_GPRS_LLC_SAPI_SNDCP11:
/* send LL_DATA_IND/LL_UNITDATA_IND to SNDCP */
llc_prim_tx = gprs_llc_prim_alloc_ll_unitdata_ind(lle->llme->tlli,
pdu_dec.sapi,
pdu_dec.data,
pdu_dec.data_len);
llc_prim_tx->ll.unitdata_ind.apply_gea = !drop_cipherable; /* TODO: is this correct? */
llc_prim_tx->ll.unitdata_ind.apply_gia = false; /* TODO: how to set this? */
gprs_llc_prim_call_up_cb(llc_prim_tx);
break;
case OSMO_GPRS_LLC_SAPI_SMS:
/* FIXME */
case OSMO_GPRS_LLC_SAPI_TOM2:
case OSMO_GPRS_LLC_SAPI_TOM8:
/* FIXME: send LL_DATA_IND/LL_UNITDATA_IND to TOM */
default:
LOGLLC(LOGL_NOTICE, "Unsupported SAPI %u\n", pdu_dec.sapi);
rc = -EINVAL;
break;
}
}
return rc;
}
int gprs_llc_prim_lower_up_bssgp(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
switch (OSMO_PRIM_HDR(&llc_prim->oph)) {
case OSMO_PRIM(OSMO_GPRS_LLC_BSSGP_UL_UNITDATA, PRIM_OP_INDICATION):
rc = gprs_llc_prim_handle_bssgp_ul_unitdata_ind(llc_prim);
break;
default:
rc = -ENOTSUP;
}
return rc;
}

69
src/llc/llc_grr.c Normal file
View File

@ -0,0 +1,69 @@
/* GPRS LLC LLGM SAP as per 3GPP TS 44.064 7.2.3 */
/*
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gprs/llc/llc.h>
#include <osmocom/gprs/llc/llc_prim.h>
#include <osmocom/gprs/llc/llc_private.h>
const struct value_string osmo_gprs_llc_grr_prim_type_names[] = {
{ OSMO_GPRS_LLC_GRR_DATA, "DATA" },
{ OSMO_GPRS_LLC_GRR_UNITDATA, "UNITDATA" },
{ 0, NULL }
};
static int llc_prim_handle_grr_data_ind(struct osmo_gprs_llc_prim *llc_prim)
{
int rc = gprs_llc_prim_handle_unsupported(llc_prim);
return rc;
}
static int llc_prim_handle_grr_unitdata_ind(struct osmo_gprs_llc_prim *llc_prim)
{
int rc = gprs_llc_prim_handle_unsupported(llc_prim);
return rc;
}
int gprs_llc_prim_lower_up_grr(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
switch (OSMO_PRIM_HDR(&llc_prim->oph)) {
case OSMO_PRIM(OSMO_GPRS_LLC_GRR_DATA, PRIM_OP_INDICATION):
rc = llc_prim_handle_grr_data_ind(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_GRR_UNITDATA, PRIM_OP_INDICATION):
rc = llc_prim_handle_grr_unitdata_ind(llc_prim);
break;
default:
rc = -ENOTSUP;
}
return rc;
}

266
src/llc/llc_ll.c Normal file
View File

@ -0,0 +1,266 @@
/* GPRS LLC LL SAP as per 3GPP TS 44.064 7.2.2 */
/*
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gprs/llc/llc.h>
#include <osmocom/gprs/llc/llc_prim.h>
#include <osmocom/gprs/llc/llc_private.h>
const struct value_string osmo_gprs_llc_ll_prim_type_names[] = {
{ OSMO_GPRS_LLC_LL_RESET, "RESET" },
{ OSMO_GPRS_LLC_LL_ESTABLISH, "ESTABLISH" },
{ OSMO_GPRS_LLC_LL_RELEASE, "RELEASE" },
{ OSMO_GPRS_LLC_LL_XID, "XID" },
{ OSMO_GPRS_LLC_LL_DATA, "DATA" },
{ OSMO_GPRS_LLC_LL_UNITDATA, "UNITDATA" },
{ OSMO_GPRS_LLC_LL_STATUS, "STATUS" },
{ 0, NULL }
};
/********************************
* Primitive allocation:
********************************/
static inline struct osmo_gprs_llc_prim *llc_prim_ll_alloc(enum osmo_gprs_llc_ll_prim_type type,
enum osmo_prim_operation operation,
unsigned int l3_len)
{
return gprs_llc_prim_alloc(OSMO_GPRS_LLC_SAP_LL, type, operation, l3_len);
}
/* 7.2.2.4 LL-XID.req (MS/SGSN) */
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_ll_xid_req(uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi, uint8_t *l3_par, unsigned int l3_par_len)
{
struct osmo_gprs_llc_prim *llc_prim;
llc_prim = llc_prim_ll_alloc(OSMO_GPRS_LLC_LL_XID, PRIM_OP_REQUEST, l3_par_len);
llc_prim->ll.tlli = tlli;
llc_prim->ll.sapi = ll_sapi;
llc_prim->ll.l3_pdu = l3_par;
llc_prim->ll.l3_pdu_len = l3_par_len;
return llc_prim;
}
/* 7.2.2.4 LL-XID.ind (MS/SGSN) */
struct osmo_gprs_llc_prim *gprs_llc_prim_alloc_ll_xid_ind(uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi, uint8_t *l3_par, unsigned int l3_par_len)
{
struct osmo_gprs_llc_prim *llc_prim;
llc_prim = llc_prim_ll_alloc(OSMO_GPRS_LLC_LL_XID, PRIM_OP_INDICATION, l3_par_len);
llc_prim->ll.tlli = tlli;
llc_prim->ll.sapi = ll_sapi;
llc_prim->ll.l3_pdu = l3_par;
llc_prim->ll.l3_pdu_len = l3_par_len;
return llc_prim;
}
/* 7.2.2.4 LL-XID.resp (MS/SGSN) */
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_ll_xid_resp(uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi, uint8_t *l3_par, unsigned int l3_par_len)
{
struct osmo_gprs_llc_prim *llc_prim;
llc_prim = llc_prim_ll_alloc(OSMO_GPRS_LLC_LL_XID, PRIM_OP_RESPONSE, l3_par_len);
llc_prim->ll.tlli = tlli;
llc_prim->ll.sapi = ll_sapi;
llc_prim->ll.l3_pdu = l3_par;
llc_prim->ll.l3_pdu_len = l3_par_len;
return llc_prim;
}
/* 7.2.2.4 LL-XID.cnf (MS/SGSN) */
struct osmo_gprs_llc_prim *gprs_llc_prim_alloc_ll_xid_cnf(uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi, uint8_t *l3_par, unsigned int l3_par_len)
{
struct osmo_gprs_llc_prim *llc_prim;
llc_prim = llc_prim_ll_alloc(OSMO_GPRS_LLC_LL_XID, PRIM_OP_CONFIRM, l3_par_len);
llc_prim->ll.tlli = tlli;
llc_prim->ll.sapi = ll_sapi;
llc_prim->ll.l3_pdu = l3_par;
llc_prim->ll.l3_pdu_len = l3_par_len;
return llc_prim;
}
/* 7.2.2.6 LL-UNITDATA.req (MS/SGSN):*/
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_ll_unitdata_req(
uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi, uint8_t *l3_pdu, size_t l3_pdu_len)
{
struct osmo_gprs_llc_prim *llc_prim;
llc_prim = llc_prim_ll_alloc(OSMO_GPRS_LLC_LL_UNITDATA, PRIM_OP_REQUEST, l3_pdu_len);
llc_prim->ll.tlli = tlli;
llc_prim->ll.sapi = ll_sapi;
llc_prim->ll.l3_pdu = l3_pdu;
llc_prim->ll.l3_pdu_len = l3_pdu_len;
return llc_prim;
}
/* 7.2.2.6 LL-UNITDATA.ind (MS/SGSN):*/
struct osmo_gprs_llc_prim *gprs_llc_prim_alloc_ll_unitdata_ind(
uint32_t tlli, enum osmo_gprs_llc_sapi ll_sapi, uint8_t *l3_pdu, size_t l3_pdu_len)
{
struct osmo_gprs_llc_prim *llc_prim;
llc_prim = llc_prim_ll_alloc(OSMO_GPRS_LLC_LL_UNITDATA, PRIM_OP_INDICATION, l3_pdu_len);
llc_prim->ll.tlli = tlli;
llc_prim->ll.sapi = ll_sapi;
llc_prim->ll.l3_pdu = l3_pdu;
llc_prim->ll.l3_pdu_len = l3_pdu_len;
return llc_prim;
}
/********************************
* Handling to upper layers:
********************************/
/* Submit LL-XID.ind to upper layers (Figure 17) */
int gprs_llc_lle_submit_prim_ll_xid_ind(struct gprs_llc_lle *lle,
const struct gprs_llc_xid_field *xid_field_request_l3)
{
struct osmo_gprs_llc_prim *llc_prim_tx;
OSMO_ASSERT(xid_field_request_l3);
llc_prim_tx = gprs_llc_prim_alloc_ll_xid_ind(lle->llme->tlli, lle->sapi, NULL, xid_field_request_l3->var.val_len);
OSMO_ASSERT(llc_prim_tx);
llc_prim_tx->ll.l3_pdu_len = xid_field_request_l3->var.val_len;
llc_prim_tx->ll.l3_pdu = msgb_put(llc_prim_tx->oph.msg, llc_prim_tx->ll.l3_pdu_len);
if (llc_prim_tx->ll.l3_pdu_len > 0)
memcpy(llc_prim_tx->ll.l3_pdu, xid_field_request_l3->var.val,
llc_prim_tx->ll.l3_pdu_len);
return gprs_llc_prim_call_up_cb(llc_prim_tx);
}
/* Submit LL-XID.cnf to upper layers */
int gprs_llc_lle_submit_prim_ll_xid_cnf(struct gprs_llc_lle *lle,
const struct gprs_llc_xid_field *xid_field_response_l3,
const struct gprs_llc_xid_field *xid_field_request_l3)
{
struct osmo_gprs_llc_prim *llc_prim_tx;
OSMO_ASSERT(xid_field_response_l3);
llc_prim_tx = gprs_llc_prim_alloc_ll_xid_cnf(lle->llme->tlli, lle->sapi, NULL, xid_field_response_l3->var.val_len);
OSMO_ASSERT(llc_prim_tx);
llc_prim_tx->ll.l3_pdu_len = xid_field_response_l3->var.val_len;
llc_prim_tx->ll.l3_pdu = msgb_put(llc_prim_tx->oph.msg, llc_prim_tx->ll.l3_pdu_len);
if (llc_prim_tx->ll.l3_pdu_len > 0)
memcpy(llc_prim_tx->ll.l3_pdu, xid_field_response_l3->var.val,
llc_prim_tx->ll.l3_pdu_len);
/* TODO: do something with following. Is it actually needed? */
(void)xid_field_request_l3;
return gprs_llc_prim_call_up_cb(llc_prim_tx);
}
/********************************
* Handling from upper layers:
********************************/
/* 7.2.2.4 LL-XID.req (MS/SGSN):*/
static int llc_prim_handle_ll_xid_req(struct osmo_gprs_llc_prim *llc_prim)
{
int rc = 0;
struct gprs_llc_lle *lle;
lle = gprs_llc_find_lle_by_tlli_sapi(llc_prim->ll.tlli, llc_prim->ll.sapi);
if (!lle) {
LOGLLC(LOGL_NOTICE, "Rx %s: Unknown TLLI 0x%08x SAPI 0x%02x\n",
osmo_gprs_llc_prim_name(llc_prim), llc_prim->ll.tlli,
llc_prim->ll.sapi);
rc = -ENOKEY;
goto ret_free;
}
rc = gprs_llc_lle_tx_xid_req(lle, llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len);
ret_free:
msgb_free(llc_prim->oph.msg);
return rc;
}
/* 7.2.2.4 LL-XID.resp (MS/SGSN):*/
static int llc_prim_handle_ll_xid_resp(struct osmo_gprs_llc_prim *llc_prim)
{
int rc = 0;
struct gprs_llc_lle *lle;
lle = gprs_llc_find_lle_by_tlli_sapi(llc_prim->ll.tlli, llc_prim->ll.sapi);
if (!lle) {
LOGLLC(LOGL_NOTICE, "Rx %s: Unknown TLLI 0x%08x SAPI 0x%02x\n",
osmo_gprs_llc_prim_name(llc_prim), llc_prim->ll.tlli,
llc_prim->ll.sapi);
rc = -ENOKEY;
goto ret_free;
}
rc = gprs_llc_lle_tx_xid_resp(lle, llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len);
ret_free:
msgb_free(llc_prim->oph.msg);
return rc;
}
/* 7.2.2.6 LL-UNITDATA.req (MS/SGSN):*/
static int llc_prim_handle_ll_unitdata_req(struct osmo_gprs_llc_prim *llc_prim)
{
int rc = 0;
struct gprs_llc_lle *lle;
lle = gprs_llc_find_lle_by_tlli_sapi(llc_prim->ll.tlli, llc_prim->ll.sapi);
if (!lle) {
struct gprs_llc_llme *llme;
LOGLLC(LOGL_NOTICE, "Rx %s: unknown TLLI 0x%08x, creating LLME on the fly\n",
osmo_gprs_llc_prim_name(llc_prim), llc_prim->ll.tlli);
llme = gprs_llc_llme_alloc(llc_prim->ll.tlli);
lle = gprs_llc_llme_get_lle(llme, llc_prim->ll.sapi);
}
rc = gprs_llc_lle_tx_ui(lle, llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len,
llc_prim->ll.unitdata_req.apply_gea);
msgb_free(llc_prim->oph.msg);
return rc;
}
/* LLC higher layers push LLC primitive down to LLC layer: */
int gprs_llc_prim_ll_upper_down(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
switch (OSMO_PRIM_HDR(&llc_prim->oph)) {
case OSMO_PRIM(OSMO_GPRS_LLC_LL_XID, PRIM_OP_REQUEST):
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_MS ||
g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN);
rc = llc_prim_handle_ll_xid_req(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_LL_XID, PRIM_OP_RESPONSE):
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_MS ||
g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN);
rc = llc_prim_handle_ll_xid_resp(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_LL_UNITDATA, PRIM_OP_REQUEST):
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_MS ||
g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN);
rc = llc_prim_handle_ll_unitdata_req(llc_prim);
break;
/* TODO: others */
default:
rc = gprs_llc_prim_handle_unsupported(llc_prim);
}
return rc;
}

288
src/llc/llc_llgmm.c Normal file
View File

@ -0,0 +1,288 @@
/* GPRS LLC LLGM SAP as per 3GPP TS 44.064 7.2.1 */
/*
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gprs/llc/llc.h>
#include <osmocom/gprs/llc/llc_prim.h>
#include <osmocom/gprs/llc/llc_private.h>
const struct value_string osmo_gprs_llc_llgmm_prim_type_names[] = {
{ OSMO_GPRS_LLC_LLGMM_ASSIGN, "ASSIGN" },
{ OSMO_GPRS_LLC_LLGMM_RESET, "RESET" },
{ OSMO_GPRS_LLC_LLGMM_TRIGGER, "TRIGGER" },
{ OSMO_GPRS_LLC_LLGMM_SUSPEND, "SUSPEND" },
{ OSMO_GPRS_LLC_LLGMM_RESUME, "RESUME" },
{ OSMO_GPRS_LLC_LLGMM_PAGE, "PAGE" },
{ OSMO_GPRS_LLC_LLGMM_IOV, "IOV" },
{ OSMO_GPRS_LLC_LLGMM_STATUS, "STATUS" },
{ OSMO_GPRS_LLC_LLGMM_PSHO, "PSHO" },
{ OSMO_GPRS_LLC_LLGMM_ASSIGN_UP, "ASSIGN-USERPLANE" },
{ 0, NULL }
};
/* Generate XID message that will cause the GMM to reset */
static int gprs_llc_lle_generate_xid_for_gmm_reset(struct gprs_llc_lle *lle,
uint8_t *bytes,
int bytes_len, uint32_t iov_ui)
{
struct gprs_llc_xid_field *fields;
const unsigned int fields_len = 2;
fields = (struct gprs_llc_xid_field *)talloc_zero_size(lle->llme, sizeof(*fields) * fields_len);
OSMO_ASSERT(fields);
/* First XID component must be RESET */
fields[0].type = OSMO_GPRS_LLC_XID_T_RESET;
fields[1].type = OSMO_GPRS_LLC_XID_T_IOV_UI;
fields[1].val = iov_ui;
talloc_free(lle->xid);
lle->xid = fields;
lle->xid_len = fields_len;
return gprs_llc_xid_encode(bytes, bytes_len, fields, fields_len);
}
/********************************
* Primitive allocation:
********************************/
static inline struct osmo_gprs_llc_prim *llc_prim_llgmm_alloc(enum osmo_gprs_llc_llgmm_prim_type type,
enum osmo_prim_operation operation,
unsigned int l3_len)
{
return gprs_llc_prim_alloc(OSMO_GPRS_LLC_SAP_LLGM, type, operation, l3_len);
}
/* 7.2.1.1 LLGMM-ASSIGN.req (MS/SGSN):*/
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_llgm_assign_req(uint32_t tlli)
{
struct osmo_gprs_llc_prim *llc_prim;
llc_prim = llc_prim_llgmm_alloc(OSMO_GPRS_LLC_LLGMM_ASSIGN, PRIM_OP_REQUEST, 0);
llc_prim->llgmm.tlli = tlli;
return llc_prim;
}
/* 7.2.1.2 LLGMM-RESET.req (SGSN):*/
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_llgm_reset_req(uint32_t tlli)
{
struct osmo_gprs_llc_prim *llc_prim;
llc_prim = llc_prim_llgmm_alloc(OSMO_GPRS_LLC_LLGMM_RESET, PRIM_OP_REQUEST, 0);
llc_prim->llgmm.tlli = tlli;
return llc_prim;
}
/* 7.2.1.2 LLGMM-RESET.cnf (SGSN):*/
struct osmo_gprs_llc_prim *osmo_gprs_llc_prim_alloc_llgm_reset_cnf(uint32_t tlli)
{
struct osmo_gprs_llc_prim *llc_prim;
llc_prim = llc_prim_llgmm_alloc(OSMO_GPRS_LLC_LLGMM_RESET, PRIM_OP_CONFIRM, 0);
llc_prim->llgmm.tlli = tlli;
return llc_prim;
}
/********************************
* Handling from upper layers:
********************************/
/* 7.2.1.1 LLGMM-ASSIGN.req (MS/SGSN):*/
static int llc_prim_handle_llgm_assign_req(struct osmo_gprs_llc_prim *llc_prim)
{
uint32_t old_tlli = llc_prim->llgmm.tlli;
uint32_t new_tlli = llc_prim->llgmm.assign_req.tlli_new;
int rc = 0;
bool free = false;
struct gprs_llc_llme *llme;
unsigned int i;
if (old_tlli == TLLI_UNASSIGNED && new_tlli == TLLI_UNASSIGNED) {
LOGLLC(LOGL_NOTICE, "Rx %s: Wrong input: Both old and new TLLI are unassigned!\n",
osmo_gprs_llc_prim_name(llc_prim));
rc = -EINVAL;
goto ret_free;
}
llme = gprs_llc_find_llme_by_tlli(old_tlli != TLLI_UNASSIGNED ? old_tlli : new_tlli);
if (!llme) {
LOGLLC(LOGL_NOTICE, "Rx %s: Unknown TLLI 0x%08x\n",
osmo_gprs_llc_prim_name(llc_prim), old_tlli);
rc = -ENOKEY;
goto ret_free;
}
LOGLLME(llme, LOGL_NOTICE, "LLGM Assign pre (%08x => %08x)\n", old_tlli, new_tlli);
if (old_tlli == TLLI_UNASSIGNED && new_tlli != TLLI_UNASSIGNED) {
/* TLLI Assignment 8.3.1 */
/* New TLLI shall be assigned and used when (re)transmitting LLC frames */
/* If old TLLI != TLLI_UNASSIGNED was assigned to LLME, then TLLI
* old is unassigned. Only TLLI new shall be accepted when
* received from peer. */
if (llme->old_tlli != TLLI_UNASSIGNED) {
llme->old_tlli = TLLI_UNASSIGNED;
llme->tlli = new_tlli;
} else {
/* If TLLI old == TLLI_UNASSIGNED was assigned to LLME, then this is
* TLLI assignmemt according to 8.3.1 */
llme->old_tlli = TLLI_UNASSIGNED;
llme->tlli = new_tlli;
llme->state = OSMO_GPRS_LLC_LLMS_ASSIGNED;
/* 8.5.3.1 For all LLE's */
for (i = 0; i < ARRAY_SIZE(llme->lle); i++) {
struct gprs_llc_lle *l = &llme->lle[i];
l->vu_send = l->vu_recv = 0;
l->retrans_ctr = 0;
l->state = OSMO_GPRS_LLC_LLES_ASSIGNED_ADM;
/* FIXME Set parameters according to table 9 */
}
}
} else if (old_tlli != TLLI_UNASSIGNED && new_tlli != TLLI_UNASSIGNED) {
/* TLLI Change 8.3.2 */
/* Both TLLI Old and TLLI New are assigned; use New when
* (re)transmitting. Accept both Old and New on Rx */
llme->old_tlli = old_tlli;
llme->tlli = new_tlli;
llme->state = OSMO_GPRS_LLC_LLMS_ASSIGNED;
} else if (old_tlli != TLLI_UNASSIGNED && new_tlli == TLLI_UNASSIGNED) {
/* TLLI Unassignment 8.3.3) */
llme->tlli = llme->old_tlli = 0;
llme->state = OSMO_GPRS_LLC_LLMS_ASSIGNED;
for (i = 0; i < ARRAY_SIZE(llme->lle); i++) {
struct gprs_llc_lle *l = &llme->lle[i];
l->state = OSMO_GPRS_LLC_LLES_UNASSIGNED;
}
free = true;
}
LOGLLME(llme, LOGL_NOTICE, "LLGM Assign post (%08x => %08x)\n", old_tlli, new_tlli);
if (free)
gprs_llc_llme_free(llme);
ret_free:
msgb_free(llc_prim->oph.msg);
return rc;
}
/* 7.2.1.2 LLGMM-RESET.req (SGSN):*/
static int llc_prim_handle_llgm_reset_req(struct osmo_gprs_llc_prim *llc_prim)
{
struct gprs_llc_lle *lle;
uint8_t xid_bytes[1024];
int xid_bytes_len;
int rc = 0;
struct gprs_llc_llme *llme = gprs_llc_find_llme_by_tlli(llc_prim->llgmm.tlli);
if (!llme) {
LOGLLC(LOGL_NOTICE, "Rx %s: Unknown TLLI 0x%08x\n",
osmo_gprs_llc_prim_name(llc_prim), llc_prim->llgmm.tlli);
rc = -ENOKEY;
goto ret_free;
}
LOGLLME(llme, LOGL_INFO, "%s\n", osmo_gprs_llc_prim_name(llc_prim));
lle = gprs_llc_llme_get_lle(llme, OSMO_GPRS_LLC_SAPI_GMM);
rc = osmo_get_rand_id((uint8_t *) &llme->iov_ui, 4);
if (rc < 0) {
LOGLLME(llme, LOGL_ERROR, "osmo_get_rand_id() failed for LLC XID reset: %s\n",
strerror(-rc));
goto ret_free;
}
/* Generate XID message */
xid_bytes_len = gprs_llc_lle_generate_xid_for_gmm_reset(lle, xid_bytes, sizeof(xid_bytes),
llme->iov_ui);
if (xid_bytes_len < 0) {
rc = -EINVAL;
goto ret_free;
}
/* Reset some of the LLC parameters. See GSM 04.64, 8.5.3.1 */
lle->vu_recv = 0;
lle->vu_send = 0;
lle->oc_ui_send = 0;
lle->oc_ui_recv = 0;
rc = gprs_llc_lle_tx_xid(lle, xid_bytes, xid_bytes_len, true);
ret_free:
msgb_free(llc_prim->oph.msg);
return rc;
}
/* LLC higher layers push LLC primitive down to LLC layer: */
int gprs_llc_prim_llgmm_upper_down(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
switch (OSMO_PRIM_HDR(&llc_prim->oph)) {
case OSMO_PRIM(OSMO_GPRS_LLC_LLGMM_ASSIGN, PRIM_OP_REQUEST):
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_MS ||
g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN);
rc = llc_prim_handle_llgm_assign_req(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_LLGMM_RESET, PRIM_OP_REQUEST):
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN);
rc = llc_prim_handle_llgm_reset_req(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_LLGMM_TRIGGER, PRIM_OP_REQUEST):
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_MS);
rc = gprs_llc_prim_handle_unsupported(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_LLGMM_SUSPEND, PRIM_OP_REQUEST):
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_MS ||
g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN);
rc = gprs_llc_prim_handle_unsupported(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_LLGMM_RESUME, PRIM_OP_REQUEST):
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_MS ||
g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN);
rc = gprs_llc_prim_handle_unsupported(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_LLGMM_IOV, PRIM_OP_REQUEST):
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN);
rc = gprs_llc_prim_handle_unsupported(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_LLGMM_PSHO, PRIM_OP_REQUEST):
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_MS ||
g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN);
rc = gprs_llc_prim_handle_unsupported(llc_prim);
break;
case OSMO_PRIM(OSMO_GPRS_LLC_LLGMM_ASSIGN_UP, PRIM_OP_REQUEST):
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_MS ||
g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN);
rc = gprs_llc_prim_handle_unsupported(llc_prim);
break;
default:
rc = -ENOTSUP;
msgb_free(llc_prim->oph.msg);
}
return rc;
}

View File

@ -28,12 +28,7 @@
#include <osmocom/core/logging.h>
#include <osmocom/gprs/llc/llc.h>
#define UI_HDR_LEN 3
#define N202 4
#define CRC24_LENGTH 3
extern int g_log_cat;
#include <osmocom/gprs/llc/llc_private.h>
const struct value_string osmo_gprs_llc_sapi_names[] = {
{ OSMO_GPRS_LLC_SAPI_GMM, "GMM" },
@ -47,7 +42,7 @@ const struct value_string osmo_gprs_llc_sapi_names[] = {
{ 0, NULL }
};
const struct value_string osmo_gprs_llc_frame_fmt_names[] = {
const struct value_string gprs_llc_frame_fmt_names[] = {
{ OSMO_GPRS_LLC_FMT_I, "I" },
{ OSMO_GPRS_LLC_FMT_S, "U" },
{ OSMO_GPRS_LLC_FMT_UI, "UI" },
@ -55,7 +50,7 @@ const struct value_string osmo_gprs_llc_frame_fmt_names[] = {
{ 0, NULL }
};
const struct value_string osmo_gprs_llc_frame_func_names[] = {
const struct value_string gprs_llc_frame_func_names[] = {
/* 6.4.1 Unnumbered (U) frames */
{ OSMO_GPRS_LLC_FUNC_SABM, "SABM" },
{ OSMO_GPRS_LLC_FUNC_DISC, "DISC" },
@ -77,7 +72,7 @@ const struct value_string osmo_gprs_llc_frame_func_names[] = {
uint32_t crc24_calc(uint32_t fcs, const uint8_t *data, size_t len);
uint32_t osmo_gprs_llc_fcs(const uint8_t *data, size_t len)
uint32_t gprs_llc_fcs(const uint8_t *data, size_t len)
{
uint32_t fcs_calc;
@ -88,15 +83,15 @@ 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)
void gprs_llc_pdu_hdr_dump_buf(char *buf, size_t buf_size,
const struct gprs_llc_pdu_decoded *pdu)
{
struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
OSMO_STRBUF_PRINTF(sb, "SAPI=%u (%s), %s func=%s C/R=%c",
pdu->sapi, osmo_gprs_llc_sapi_name(pdu->sapi),
osmo_gprs_llc_frame_fmt_name(pdu->fmt),
osmo_gprs_llc_frame_func_name(pdu->func),
gprs_llc_frame_fmt_name(pdu->fmt),
gprs_llc_frame_func_name(pdu->func),
pdu->flags & OSMO_GPRS_LLC_PDU_F_CMD_RSP ? '1' : '0');
switch (pdu->fmt) {
@ -128,10 +123,10 @@ void osmo_gprs_llc_pdu_hdr_dump_buf(char *buf, size_t buf_size,
OSMO_STRBUF_PRINTF(sb, " FCS=%06x", pdu->fcs);
}
const char *osmo_gprs_llc_pdu_hdr_dump(const struct osmo_gprs_llc_pdu_decoded *pdu)
const char *gprs_llc_pdu_hdr_dump(const struct gprs_llc_pdu_decoded *pdu)
{
static __thread char buf[256];
osmo_gprs_llc_pdu_hdr_dump_buf(&buf[0], sizeof(buf), pdu);
gprs_llc_pdu_hdr_dump_buf(&buf[0], sizeof(buf), pdu);
return buf;
}
@ -144,7 +139,7 @@ const char *osmo_gprs_llc_pdu_hdr_dump(const struct osmo_gprs_llc_pdu_decoded *p
#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)
int gprs_llc_pdu_encode(struct msgb *msg, const struct gprs_llc_pdu_decoded *pdu)
{
uint8_t *addr = msgb_put(msg, 1);
uint8_t *ctrl = NULL;
@ -275,7 +270,7 @@ int osmo_gprs_llc_pdu_encode(struct msgb *msg, const struct osmo_gprs_llc_pdu_de
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);
fcs = gprs_llc_fcs(addr, crc_len);
msgb_put_u8(msg, fcs & 0xff);
msgb_put_u8(msg, (fcs >> 8) & 0xff);
@ -284,11 +279,11 @@ int osmo_gprs_llc_pdu_encode(struct msgb *msg, const struct osmo_gprs_llc_pdu_de
return 0;
}
int osmo_gprs_llc_pdu_decode(struct osmo_gprs_llc_pdu_decoded *pdu,
const uint8_t *data, size_t data_len)
int gprs_llc_pdu_decode(struct gprs_llc_pdu_decoded *pdu,
uint8_t *data, size_t data_len)
{
const uint8_t *addr = &data[0];
const uint8_t *ctrl = &data[1];
uint8_t *addr = &data[0];
uint8_t *ctrl = &data[1];
#define check_len(len, text) \
do { \

View File

@ -19,10 +19,18 @@
*
*/
#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>
#include <osmocom/gprs/llc/llc_prim.h>
#include <osmocom/gprs/llc/llc_private.h>
#define LLC_MSGB_HEADROOM 0
const struct value_string osmo_gprs_llc_prim_sap_names[] = {
{ OSMO_GPRS_LLC_SAP_LLGM, "LLGM" },
@ -31,3 +39,174 @@ const struct value_string osmo_gprs_llc_prim_sap_names[] = {
{ OSMO_GPRS_LLC_SAP_BSSGP, "BSSGP" },
{ 0, NULL }
};
const char *osmo_gprs_llc_prim_name(const struct osmo_gprs_llc_prim *llc_prim)
{
static char name_buf[256];
const char *sap = osmo_gprs_llc_prim_sap_name(llc_prim->oph.sap);
const char *op = get_value_string(osmo_prim_op_names, llc_prim->oph.operation);
const char *type;
switch (llc_prim->oph.sap) {
case OSMO_GPRS_LLC_SAP_LLGM:
type = osmo_gprs_llc_llgmm_prim_type_name(llc_prim->oph.primitive);
break;
case OSMO_GPRS_LLC_SAP_LL:
type = osmo_gprs_llc_ll_prim_type_name(llc_prim->oph.primitive);
break;
case OSMO_GPRS_LLC_SAP_GRR:
type = osmo_gprs_llc_grr_prim_type_name(llc_prim->oph.primitive);
break;
case OSMO_GPRS_LLC_SAP_BSSGP:
type = osmo_gprs_llc_bssgp_prim_type_name(llc_prim->oph.primitive);
break;
default:
type = "unsupported-llc-sap";
}
snprintf(name_buf, sizeof(name_buf), "%s-%s.%s", sap, type, op);
return name_buf;
}
static int llc_up_cb_dummy(struct osmo_gprs_llc_prim *llc_prim, void *user_data)
{
LOGLLC(LOGL_INFO, "llc_up_cb_dummy(%s)\n", osmo_gprs_llc_prim_name(llc_prim));
return 0;
}
static int llc_down_cb_dummy(struct osmo_gprs_llc_prim *llc_prim, void *user_data)
{
LOGLLC(LOGL_INFO, "llc_down_cb_dummy(%s)\n", osmo_gprs_llc_prim_name(llc_prim));
return 0;
}
/* Set callback used by LLC layer to push primitives to higher layers in protocol stack */
void osmo_gprs_llc_prim_set_up_cb(osmo_gprs_llc_prim_up_cb up_cb, void *up_user_data)
{
g_ctx->llc_up_cb = up_cb;
g_ctx->llc_up_cb_user_data = up_user_data;
}
/* Set callback used by LLC layer to push primitives to lower layers in protocol stack */
void osmo_gprs_llc_prim_set_down_cb(osmo_gprs_llc_prim_down_cb down_cb, void *down_user_data)
{
g_ctx->llc_down_cb = down_cb;
g_ctx->llc_down_cb_user_data = down_user_data;
}
/********************************
* Primitive allocation:
********************************/
/* allocate a msgb containing a struct osmo_gprs_llc_prim + optional l3 data */
static struct msgb *gprs_llc_prim_msgb_alloc(unsigned int l3_len)
{
const int headroom = LLC_MSGB_HEADROOM;
const int size = headroom + sizeof(struct osmo_gprs_llc_prim) + l3_len;
struct msgb *msg = msgb_alloc_headroom(size, headroom, "llc_prim");
if (!msg)
return NULL;
msg->l1h = msgb_put(msg, sizeof(struct osmo_gprs_llc_prim));
return msg;
}
struct osmo_gprs_llc_prim *gprs_llc_prim_alloc(enum osmo_gprs_llc_prim_sap sap, unsigned int type,
enum osmo_prim_operation operation,
unsigned int l3_len)
{
struct msgb *msg = gprs_llc_prim_msgb_alloc(l3_len);
struct osmo_gprs_llc_prim *llc_prim = msgb_llc_prim(msg);
osmo_prim_init(&llc_prim->oph, sap, type, operation, msg);
return llc_prim;
}
int gprs_llc_prim_handle_unsupported(struct osmo_gprs_llc_prim *llc_prim)
{
LOGLLC(LOGL_ERROR, "Unsupported llc_prim! %s\n", osmo_gprs_llc_prim_name(llc_prim));
msgb_free(llc_prim->oph.msg);
return -ENOTSUP;
}
/********************************
* Handling from/to upper layers:
********************************/
int gprs_llc_prim_call_up_cb(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
if (g_ctx->llc_up_cb)
rc = g_ctx->llc_up_cb(llc_prim, g_ctx->llc_up_cb_user_data);
else
rc = llc_up_cb_dummy(llc_prim, g_ctx->llc_up_cb_user_data);
msgb_free(llc_prim->oph.msg);
return rc;
}
/* LLC higher layers push LLC primitive down to LLC layer: */
int osmo_gprs_llc_prim_upper_down(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
OSMO_ASSERT(g_ctx);
LOGLLC(LOGL_INFO, "Rx from upper layers: %s\n", osmo_gprs_llc_prim_name(llc_prim));
switch (llc_prim->oph.sap) {
case OSMO_GPRS_LLC_SAP_LLGM:
rc = gprs_llc_prim_llgmm_upper_down(llc_prim);
break;
case OSMO_GPRS_LLC_SAP_LL:
rc = gprs_llc_prim_ll_upper_down(llc_prim);
break;
default:
rc = gprs_llc_prim_handle_unsupported(llc_prim);
}
return rc;
}
/********************************
* Handling from/to lower layers:
********************************/
int gprs_llc_prim_call_down_cb(struct osmo_gprs_llc_prim *llc_prim)
{
int rc;
if (g_ctx->llc_down_cb)
rc = g_ctx->llc_down_cb(llc_prim, g_ctx->llc_down_cb_user_data);
else
rc = llc_down_cb_dummy(llc_prim, g_ctx->llc_down_cb_user_data);
msgb_free(llc_prim->oph.msg);
return rc;
}
/* LLC lower layers push LLC primitive up to LLC layer: */
int osmo_gprs_llc_prim_lower_up(struct osmo_gprs_llc_prim *llc_prim)
{
OSMO_ASSERT(g_ctx);
OSMO_ASSERT(llc_prim);
struct msgb *msg = llc_prim->oph.msg;
int rc;
LOGLLC(LOGL_INFO, "Rx from lower layers: %s\n", osmo_gprs_llc_prim_name(llc_prim));
switch (llc_prim->oph.sap) {
case OSMO_GPRS_LLC_SAP_GRR:
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_MS);
rc = gprs_llc_prim_lower_up_grr(llc_prim);
break;
case OSMO_GPRS_LLC_SAP_BSSGP:
OSMO_ASSERT(g_ctx->location == OSMO_GPRS_LLC_LOCATION_SGSN);
rc = gprs_llc_prim_lower_up_bssgp(llc_prim);
break;
default:
rc = -EINVAL;
}
/* Special return value '1' means: do not free */
if (rc != 1)
msgb_free(msg);
return rc;
}

View File

@ -28,8 +28,9 @@
#include <osmocom/core/logging.h>
#include <osmocom/gprs/llc/llc.h>
#include <osmocom/gprs/llc/llc_private.h>
const struct value_string osmo_gprs_llc_xid_type_names[] = {
const struct value_string gprs_llc_xid_type_names[] = {
{ OSMO_GPRS_LLC_XID_T_VERSION, "LLC-Version" },
{ OSMO_GPRS_LLC_XID_T_IOV_UI, "IOV-UI" },
{ OSMO_GPRS_LLC_XID_T_IOV_I, "IOV-I" },
@ -75,7 +76,7 @@ static const struct {
[OSMO_GPRS_LLC_XID_T_MAC_IOV_UI] = { .len = 4, .min = 0, .max = UINT32_MAX },
};
bool osmo_gprs_llc_xid_field_is_valid(const struct osmo_gprs_llc_xid_field *field)
bool gprs_llc_xid_field_is_valid(const struct gprs_llc_xid_field *field)
{
if (field->type >= ARRAY_SIZE(gprs_llc_xid_desc)) {
LOGLLC(LOGL_ERROR,
@ -86,6 +87,14 @@ bool osmo_gprs_llc_xid_field_is_valid(const struct osmo_gprs_llc_xid_field *fiel
if (gprs_llc_xid_desc[field->type].var_len)
return true;
if (field->var.val_len > 0) {
LOGLLC(LOGL_ERROR,
"XID field %s unexpected var.val_len %u > 0, check your code!\n",
gprs_llc_xid_type_name(field->type),
field->var.val_len);
return false;
}
/* For mU and mD, the value range (9 .. 24320) also includes 0 */
if (gprs_llc_xid_desc[field->type].allow_zero && field->val == 0)
return true;
@ -93,7 +102,7 @@ bool osmo_gprs_llc_xid_field_is_valid(const struct osmo_gprs_llc_xid_field *fiel
if (field->val < gprs_llc_xid_desc[field->type].min) {
LOGLLC(LOGL_ERROR,
"XID field %s value=%u < min=%u\n",
osmo_gprs_llc_xid_type_name(field->type),
gprs_llc_xid_type_name(field->type),
field->val, gprs_llc_xid_desc[field->type].min);
return false;
}
@ -101,7 +110,7 @@ bool osmo_gprs_llc_xid_field_is_valid(const struct osmo_gprs_llc_xid_field *fiel
if (field->val > gprs_llc_xid_desc[field->type].max) {
LOGLLC(LOGL_ERROR,
"XID field %s value=%u > max=%u\n",
osmo_gprs_llc_xid_type_name(field->type),
gprs_llc_xid_type_name(field->type),
field->val, gprs_llc_xid_desc[field->type].max);
return false;
}
@ -109,19 +118,24 @@ bool osmo_gprs_llc_xid_field_is_valid(const struct osmo_gprs_llc_xid_field *fiel
return true;
}
int osmo_gprs_llc_xid_encode(struct msgb *msg,
const struct osmo_gprs_llc_xid_field *fields,
int gprs_llc_xid_encode(uint8_t *data, size_t data_len,
const struct gprs_llc_xid_field *fields,
unsigned int num_fields)
{
uint8_t *wptr = data;
OSMO_ASSERT(data);
for (unsigned int i = 0; i < num_fields; i++) {
const struct osmo_gprs_llc_xid_field *field = &fields[i];
const struct gprs_llc_xid_field *field = &fields[i];
uint8_t *hdr, len;
if (!osmo_gprs_llc_xid_field_is_valid(field))
if (!gprs_llc_xid_field_is_valid(field))
return -EINVAL;
/* XID field type */
hdr = msgb_put(msg, 1);
if (wptr - data >= data_len)
return -EINVAL;
hdr = wptr++;
hdr[0] = (field->type & 0x1f) << 2;
/* XID field length */
@ -135,7 +149,9 @@ int osmo_gprs_llc_xid_encode(struct msgb *msg,
if (len < 4) {
hdr[0] |= len;
} else {
msgb_put(msg, 1);
if (wptr - data >= data_len)
return -EINVAL;
wptr++;
hdr[0] |= (1 << 7); /* XL=1 */
hdr[0] |= (len >> 6) & 0x03;
hdr[1] = (len << 2) & 0xff;
@ -143,17 +159,25 @@ int osmo_gprs_llc_xid_encode(struct msgb *msg,
/* XID field value (variable length) */
if (gprs_llc_xid_desc[field->type].var_len) {
memcpy(msgb_put(msg, len), field->var.val, len);
if (wptr + len - data > data_len)
return -EINVAL;
memcpy(wptr, field->var.val, len);
wptr += len;
} else {
if (wptr + len - data > data_len)
return -EINVAL;
switch (len) {
case 1:
msgb_put_u8(msg, field->val);
*wptr = field->val;
wptr++;
break;
case 2:
osmo_store16be(field->val, msgb_put(msg, 2));
osmo_store16be(field->val, wptr);
wptr += 2;
break;
case 4:
osmo_store32be(field->val, msgb_put(msg, 4));
osmo_store32be(field->val, wptr);
wptr += 4;
break;
default:
/* Shall not happen */
@ -162,14 +186,15 @@ int osmo_gprs_llc_xid_encode(struct msgb *msg,
}
}
return 0;
return wptr - data;
}
int osmo_gprs_llc_xid_decode(struct osmo_gprs_llc_xid_field *fields,
unsigned int max_fields,
const uint8_t *data, size_t data_len)
/* returns number of decoded XID fields, negative on error. */
int gprs_llc_xid_decode(struct gprs_llc_xid_field *fields,
unsigned int max_fields,
uint8_t *data, size_t data_len)
{
const uint8_t *ptr = &data[0];
uint8_t *ptr = &data[0];
unsigned int num_fields = 0;
#define check_len(len, text) \
@ -181,7 +206,7 @@ int osmo_gprs_llc_xid_decode(struct osmo_gprs_llc_xid_field *fields,
} while (0)
while (data_len > 0) {
struct osmo_gprs_llc_xid_field *field = &fields[num_fields++];
struct gprs_llc_xid_field *field = &fields[num_fields++];
uint8_t len;
if (num_fields > max_fields) {
@ -222,7 +247,7 @@ int osmo_gprs_llc_xid_decode(struct osmo_gprs_llc_xid_field *fields,
if (len != gprs_llc_xid_desc[field->type].len) {
LOGLLC(LOGL_NOTICE,
"XID field %s has unusual length=%u (expected %u)\n",
osmo_gprs_llc_xid_type_name(field->type),
gprs_llc_xid_type_name(field->type),
len, gprs_llc_xid_desc[field->type].len);
}
@ -242,11 +267,11 @@ int osmo_gprs_llc_xid_decode(struct osmo_gprs_llc_xid_field *fields,
default:
LOGLLC(LOGL_ERROR,
"Failed to parse XID: unsupported field (%s) length=%u\n",
osmo_gprs_llc_xid_type_name(field->type), len);
gprs_llc_xid_type_name(field->type), len);
return -EINVAL;
}
if (!osmo_gprs_llc_xid_field_is_valid(field))
if (!gprs_llc_xid_field_is_valid(field))
return -EINVAL;
}
@ -257,3 +282,26 @@ int osmo_gprs_llc_xid_decode(struct osmo_gprs_llc_xid_field *fields,
return num_fields;
}
/* Return Deep copy of a xid_field array allocated using talloc ctx. */
struct gprs_llc_xid_field *gprs_llc_xid_deepcopy(void *ctx,
const struct gprs_llc_xid_field *src_xid,
size_t src_xid_len)
{
size_t bytes_len = sizeof(*src_xid) * src_xid_len;
struct gprs_llc_xid_field *dst_xid;
unsigned int i;
dst_xid = (struct gprs_llc_xid_field *) talloc_size(ctx, bytes_len);
memcpy(dst_xid, src_xid, bytes_len);
for (i = 0; i < src_xid_len; i++) {
uint8_t *val;
if (dst_xid[i].var.val_len == 0 || dst_xid[i].var.val == NULL)
continue;
val = talloc_size(dst_xid, dst_xid[i].var.val_len);
memcpy(val, dst_xid[i].var.val, dst_xid[i].var.val_len);
dst_xid[i].var.val = val;
}
return dst_xid;
}

View File

@ -10,16 +10,39 @@ AM_LDFLAGS = \
$(NULL)
check_PROGRAMS = \
llc_prim_test \
pdu_codec_test \
xid_codec_test \
$(NULL)
EXTRA_DIST = \
llc_prim_test.err \
llc_prim_test.ok \
pdu_codec_test.ok \
pdu_codec_test.err \
xid_codec_test.ok \
xid_codec_test.err \
$(NULL)
llc_prim_test_SOURCES = llc_prim_test.c
llc_prim_test_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(top_builddir)/src/llc/libosmo-gprs-llc.la \
$(NULL)
pdu_codec_test_SOURCES = pdu_codec_test.c
# libosmo-gprs-llc.a is used below to access non-exported private symbols used in the test:
pdu_codec_test_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(top_builddir)/src/llc/libosmo-gprs-llc.la \
$(LIBOSMOGSM_LIBS) \
$(top_builddir)/src/llc/.libs/libosmo-gprs-llc.a \
$(NULL)
xid_codec_test_SOURCES = xid_codec_test.c
# libosmo-gprs-llc.a is used below to access non-exported private symbols used in the test:
xid_codec_test_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(top_builddir)/src/llc/.libs/libosmo-gprs-llc.a \
$(NULL)

227
tests/llc/llc_prim_test.c Normal file
View File

@ -0,0 +1,227 @@
/* llc_prim tests
*
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau espin Pedrol <pespin@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#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_prim.h>
static void *tall_ctx = NULL;
/* stub to get reproducible output, since llme->iov_ui is printed: */
int osmo_get_rand_id(uint8_t *out, size_t len)
{
memset(out, 0x2b, len);
return 0;
}
int test_llc_prim_up_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data)
{
const char *pdu_name = osmo_gprs_llc_prim_name(llc_prim);
switch (llc_prim->oph.sap) {
case OSMO_GPRS_LLC_SAP_LLGM:
printf("%s(): Rx %s TLLI=0x%08x\n", __func__, pdu_name, llc_prim->llgmm.tlli);
break;
case OSMO_GPRS_LLC_SAP_LL:
printf("%s(): Rx %s TLLI=0x%08x SAPI=%s l3=[%s]\n", __func__, pdu_name,
llc_prim->ll.tlli, osmo_gprs_llc_sapi_name(llc_prim->ll.sapi),
osmo_hexdump(llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len));
break;
default:
printf("%s(): Unexpected Rx %s\n", __func__, pdu_name);
OSMO_ASSERT(0);
}
return 0;
}
int test_llc_prim_down_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data)
{
const char *pdu_name = osmo_gprs_llc_prim_name(llc_prim);
switch (llc_prim->oph.sap) {
case OSMO_GPRS_LLC_SAP_GRR:
printf("%s(): Rx %s l3=[%s]\n", __func__, pdu_name, osmo_hexdump(llc_prim->grr.ll_pdu, llc_prim->grr.ll_pdu_len));
break;
case OSMO_GPRS_LLC_SAP_BSSGP:
printf("%s(): Rx %s TLLI=0x%08x l3=[%s]\n", __func__, pdu_name,
llc_prim->bssgp.tlli, osmo_hexdump(llc_prim->bssgp.ll_pdu, llc_prim->bssgp.ll_pdu_len));
break;
default:
printf("%s(): Unexpected Rx %s\n", __func__, pdu_name);
OSMO_ASSERT(0);
}
return 0;
}
static void test_llc_prim_ms(void)
{
//struct osmo_gprs_llc_prim *llc_prim;
//uint32_t tlli = 0x11223344;
int rc;
printf("==== %s() [start] ====\n", __func__);
rc = osmo_gprs_llc_init(OSMO_GPRS_LLC_LOCATION_MS);
OSMO_ASSERT(rc == 0);
osmo_gprs_llc_prim_set_up_cb(test_llc_prim_up_cb, NULL);
osmo_gprs_llc_prim_set_down_cb(test_llc_prim_down_cb, NULL);
//llc_prim = osmo_gprs_llc_prim_alloc_llgm_reset_req(tlli);
//OSMO_ASSERT(llc_prim);
//rc = osmo_gprs_llc_prim_upper_down(llc_prim);
//OSMO_ASSERT(rc == 0);
printf("==== %s() [end] ====\n", __func__);
}
/**
MS-SGSN LLC (Mobile Station - Serving GPRS Support Node Logical Link Control) SAPI: GPRS Mobility Management
Address field SAPI: LLGMM
0... .... = Protocol Discriminator_bit: OK
.0.. .... = Command/Response bit: DownLink/UpLink = Response/Command
.... 0001 = SAPI: GPRS Mobility Management (1)
Unconfirmed Information format - UI: UI format: 0x6, Spare bits: 0x0, N(U): 0, E bit: non encrypted frame, PM bit: FCS covers only the frame header and first N202 octets of the information field
110. .... .... .... = UI format: 0x6
...0 0... .... .... = Spare bits: 0x0
.... .000 0000 00.. = N(U): 0
.... .... .... ..0. = E bit: non encrypted frame
.... .... .... ...0 = PM bit: FCS covers only the frame header and first N202 octets of the information field
FCS: 0xf218e2 (correct)
GSM A-I/F DTAP - Attach Request
Protocol Discriminator: GPRS mobility management messages (8)
DTAP GPRS Mobility Management Message Type: Attach Request (0x01)
MS Network Capability
Attach Type
Ciphering Key Sequence Number
DRX Parameter
Mobile Identity - IMSI (262420000000017)
Routing Area Identification - Old routing area identification - RAI: 262-42-27780-68
MS Radio Access Capability
*/
static uint8_t pdu_llc_gmm_att_req[] = {
0x01, 0xc0, 0x00, 0x08, 0x01, 0x01, 0xd5, 0x71, 0x00, 0x00, 0x08, 0x29, 0x26,
0x24, 0x00, 0x00, 0x00, 0x00, 0x71, 0x62, 0xf2, 0x24, 0x6c, 0x84, 0x44, 0x04,
0x11, 0xe5, 0x10, 0x00, 0xe2, 0x18, 0xf2
};
/*
GSM A-I/F DTAP - Identity Request
Protocol Discriminator: GPRS mobility management messages (8)
DTAP GPRS Mobility Management Message Type: Identity Request (0x15)
Identity Type 2
Force to Standby
*/
static uint8_t pdu_gmm_id_req[] = { 0x08, 0x15, 0x02 };
static void test_llc_prim_sgsn(void)
{
struct osmo_gprs_llc_prim *llc_prim;
const uint32_t tlli = 0xe1c5d364;
int rc;
struct gsm0808_cell_id cell_id = {
.id_discr = CELL_IDENT_LAC_AND_CI,
.id = {
.lac_and_ci = {
.lac = 0x0304,
.ci = 0x9876,
},
},
};
printf("==== %s() [start] ====\n", __func__);
rc = osmo_gprs_llc_init(OSMO_GPRS_LLC_LOCATION_SGSN);
OSMO_ASSERT(rc == 0);
osmo_gprs_llc_prim_set_up_cb(test_llc_prim_up_cb, NULL);
osmo_gprs_llc_prim_set_down_cb(test_llc_prim_down_cb, NULL);
/* Rx LLC-GMM-Attach-Req at SGSN from MS: */
llc_prim = osmo_gprs_llc_prim_alloc_bssgp_ul_unitdata_ind(tlli, pdu_llc_gmm_att_req, sizeof(pdu_llc_gmm_att_req));
llc_prim->bssgp.ul_unitdata_ind.cell_id = cell_id;
OSMO_ASSERT(llc_prim);
rc = osmo_gprs_llc_prim_lower_up(llc_prim);
OSMO_ASSERT(rc == 0);
/* SGSN wants to submit GMM Id Req: */
llc_prim = osmo_gprs_llc_prim_alloc_ll_unitdata_req(tlli, OSMO_GPRS_LLC_SAPI_GMM, (uint8_t *)pdu_gmm_id_req, sizeof(pdu_gmm_id_req));
OSMO_ASSERT(llc_prim);
rc = osmo_gprs_llc_prim_upper_down(llc_prim);
OSMO_ASSERT(rc == 0);
llc_prim = osmo_gprs_llc_prim_alloc_llgm_assign_req(tlli);
OSMO_ASSERT(llc_prim);
llc_prim->llgmm.assign_req.tlli_new = tlli;
rc = osmo_gprs_llc_prim_upper_down(llc_prim);
OSMO_ASSERT(rc == 0);
llc_prim = osmo_gprs_llc_prim_alloc_llgm_reset_req(tlli);
OSMO_ASSERT(llc_prim);
rc = osmo_gprs_llc_prim_upper_down(llc_prim);
OSMO_ASSERT(rc == 0);
char xid_l3_pars[] = "xid-l3-dummy-buffer";
llc_prim = osmo_gprs_llc_prim_alloc_ll_xid_req(tlli, OSMO_GPRS_LLC_SAPI_SNDCP3, (uint8_t *)xid_l3_pars, sizeof(xid_l3_pars));
OSMO_ASSERT(llc_prim);
rc = osmo_gprs_llc_prim_upper_down(llc_prim);
OSMO_ASSERT(rc == 0);
char sndcp_data[] = "some-sndcp-data";
llc_prim = osmo_gprs_llc_prim_alloc_ll_unitdata_req(tlli, OSMO_GPRS_LLC_SAPI_SNDCP3, (uint8_t *)sndcp_data, sizeof(sndcp_data));
OSMO_ASSERT(llc_prim);
rc = osmo_gprs_llc_prim_upper_down(llc_prim);
OSMO_ASSERT(rc == 0);
printf("==== %s() [end] ====\n", __func__);
}
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_llc_prim_ms();
test_llc_prim_sgsn();
talloc_free(tall_ctx);
}

View File

@ -0,0 +1,13 @@
DLGLOBAL INFO Rx from lower layers: BSSGP-UL-UNITDATA.indication
DLGLOBAL DEBUG Rx BSSGP-UL-UNITDATA.indication: SAPI=1 (GMM), UI func=UI C/R=0 PM=0 E=0 IP=0 N(U)=0 FCS=f218e2
DLGLOBAL NOTICE LLME(ffffffff/e1c5d364){UNASSIGNED} LLC RX: unknown TLLI 0xe1c5d364, creating LLME on the fly
DLGLOBAL DEBUG LLE(ffffffff/e1c5d364,GMM){UNASSIGNED} Rx SAPI=1 (GMM), UI func=UI C/R=0 PM=0 E=0 IP=0 N(U)=0 FCS=f218e2
DLGLOBAL INFO Rx from upper layers: LL-UNITDATA.request
DLGLOBAL INFO Rx from upper layers: LLGM-ASSIGN.request
DLGLOBAL NOTICE LLME(ffffffff/e1c5d364){UNASSIGNED} LLGM Assign pre (e1c5d364 => e1c5d364)
DLGLOBAL NOTICE LLME(e1c5d364/e1c5d364){ASSIGNED} LLGM Assign post (e1c5d364 => e1c5d364)
DLGLOBAL INFO Rx from upper layers: LLGM-RESET.request
DLGLOBAL INFO LLME(e1c5d364/e1c5d364){ASSIGNED} LLGM-RESET.request
DLGLOBAL INFO Rx from upper layers: LL-XID.request
DLGLOBAL NOTICE LLE(e1c5d364/e1c5d364,SNDCP3){UNASSIGNED} Sending XID type L3-Params (30 bytes) request to MS...
DLGLOBAL INFO Rx from upper layers: LL-UNITDATA.request

View File

@ -0,0 +1,9 @@
==== test_llc_prim_ms() [start] ====
==== test_llc_prim_ms() [end] ====
==== test_llc_prim_sgsn() [start] ====
test_llc_prim_up_cb(): Rx LL-UNITDATA.indication TLLI=0xe1c5d364 SAPI=GMM l3=[08 01 01 d5 71 00 00 08 29 26 24 00 00 00 00 71 62 f2 24 6c 84 44 04 11 e5 10 00 ]
test_llc_prim_down_cb(): Rx BSSGP-DL-UNITDATA.request TLLI=0xe1c5d364 l3=[41 c0 01 08 15 02 de 8e 9a ]
test_llc_prim_down_cb(): Rx BSSGP-DL-UNITDATA.request TLLI=0xe1c5d364 l3=[41 fb 30 84 10 2b 2b 2b 2b c7 82 c2 ]
test_llc_prim_down_cb(): Rx BSSGP-DL-UNITDATA.request TLLI=0xe1c5d364 l3=[43 fb 01 00 16 01 f4 1a 05 df ac 50 78 69 64 2d 6c 33 2d 64 75 6d 6d 79 2d 62 75 66 66 65 72 00 3c dd fe ]
test_llc_prim_down_cb(): Rx BSSGP-DL-UNITDATA.request TLLI=0xe1c5d364 l3=[43 c0 01 73 6f 6d 65 2d 73 6e 64 63 70 2d 64 61 74 61 00 8b cc cf ]
==== test_llc_prim_sgsn() [end] ====

View File

@ -23,6 +23,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/gprs/llc/llc.h>
#include <osmocom/gprs/llc/llc_private.h>
static void *tall_ctx = NULL;
@ -45,7 +46,7 @@ static void test_pdu_dec_enc(void)
OSMO_ASSERT(msg != NULL);
for (unsigned int i = 0; i < ARRAY_SIZE(testData); i++) {
struct osmo_gprs_llc_pdu_decoded hdr = { 0 };
struct gprs_llc_pdu_decoded hdr = { 0 };
uint8_t pdu[256];
size_t pdu_len;
int rc;
@ -56,9 +57,9 @@ static void test_pdu_dec_enc(void)
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));
rc = gprs_llc_pdu_decode(&hdr, &pdu[0], pdu_len);
printf(" gprs_llc_pdu_decode() returns %d\n", rc);
printf(" gprs_llc_pdu_hdr_dump(): %s\n", 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));
@ -67,9 +68,9 @@ static void test_pdu_dec_enc(void)
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));
rc = gprs_llc_pdu_encode(msg, &hdr);
printf(" gprs_llc_pdu_encode() returns %d\n", rc);
printf(" gprs_llc_pdu_encode(): %s\n", osmo_hexdump_nospc(msg->data, msg->len));
printf(" memcmp() returns %d\n", memcmp(&pdu, msg->data, pdu_len));
}
@ -77,109 +78,6 @@ static void test_pdu_dec_enc(void)
printf("\n");
}
static void print_xid_fields(const struct osmo_gprs_llc_xid_field *fields,
unsigned int num_fields)
{
for (unsigned int i = 0; i < num_fields; i++) {
const struct osmo_gprs_llc_xid_field *field = &fields[i];
printf(" field[%02d]: type=0x%02x (%s)",
i, field->type, osmo_gprs_llc_xid_type_name(field->type));
if (field->type == OSMO_GPRS_LLC_XID_T_L3_PAR) {
printf(" data[] (len=%u): %s\n", field->var.val_len,
osmo_hexdump_nospc(field->var.val, field->var.val_len));
} else {
printf(" val=%u\n", field->val);
}
}
}
static void test_xid_dec_enc(void)
{
static const char * const testData[] = {
/* TODO: more test vectors */
"25401602082c",
"16020825402c",
"01001601f41a05df",
"01001605f01a05f0ac18112233445566",
"01001601f41a02081e00002200002908",
};
struct msgb *msg = msgb_alloc(1024, "LLC-XID");
OSMO_ASSERT(msg != NULL);
for (unsigned int i = 0; i < ARRAY_SIZE(testData); i++) {
struct osmo_gprs_llc_xid_field fields[16] = { 0 };
uint8_t xid[256];
size_t xid_len;
int rc;
printf("%s(): decoding testData[%u] = %s\n", __func__, i, testData[i]);
rc = osmo_hexparse(testData[i], &xid[0], sizeof(xid));
xid_len = strlen(testData[i]) / 2;
OSMO_ASSERT(rc == xid_len);
rc = osmo_gprs_llc_xid_decode(fields, ARRAY_SIZE(fields),
&xid[0], xid_len);
printf(" osmo_gprs_llc_xid_decode() returns %d\n", rc);
if (rc > 0)
print_xid_fields(&fields[0], rc);
else
continue;
printf("%s(): encoding decoded testData[%u]\n", __func__, i);
msgb_reset(msg);
rc = osmo_gprs_llc_xid_encode(msg, fields, rc);
printf(" osmo_gprs_llc_xid_encode() returns %d\n", rc);
printf(" osmo_gprs_llc_xid_encode(): %s\n", osmo_hexdump_nospc(msg->data, msg->len));
printf(" memcmp() returns %d\n", memcmp(&xid, msg->data, xid_len));
}
msgb_free(msg);
printf("\n");
}
static void test_xid_enc_dec(void)
{
struct osmo_gprs_llc_xid_field dec_fields[16] = { 0 };
int rc;
const uint8_t l3_params[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
const struct osmo_gprs_llc_xid_field test_fields[] = {
{ .type = OSMO_GPRS_LLC_XID_T_VERSION, .val = 0 },
{ .type = OSMO_GPRS_LLC_XID_T_T200, .val = 4095 },
{ .type = OSMO_GPRS_LLC_XID_T_N200, .val = 10 },
{ .type = OSMO_GPRS_LLC_XID_T_IOV_I, .val = 0x42424242 },
{ .type = OSMO_GPRS_LLC_XID_T_IOV_UI, .val = 0xdeadbeef },
{ .type = OSMO_GPRS_LLC_XID_T_L3_PAR,
.var = { .val_len = sizeof(l3_params), .val = &l3_params[0] } },
{ .type = OSMO_GPRS_LLC_XID_T_RESET },
};
struct msgb *msg = msgb_alloc(1024, "LLC-XID");
OSMO_ASSERT(msg != NULL);
printf("%s(): encoding hand-crafted testData\n", __func__);
rc = osmo_gprs_llc_xid_encode(msg, &test_fields[0], ARRAY_SIZE(test_fields));
printf(" osmo_gprs_llc_xid_encode() returns %d\n", rc);
printf(" osmo_gprs_llc_xid_encode(): %s\n", osmo_hexdump_nospc(msg->data, msg->len));
printf("%s(): decoding encoded hand-crafted testData\n", __func__);
rc = osmo_gprs_llc_xid_decode(&dec_fields[0], ARRAY_SIZE(dec_fields),
msg->data, msg->len);
printf(" osmo_gprs_llc_xid_decode() returns %d\n", rc);
if (rc > 0)
print_xid_fields(&dec_fields[0], rc);
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,
@ -200,8 +98,6 @@ int main(int argc, char *argv[])
log_set_use_color(osmo_stderr_target, 0);
test_pdu_dec_enc();
test_xid_dec_enc();
test_xid_enc_dec();
talloc_free(tall_ctx);
}

View File

@ -1,103 +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 (GMM), UI func=UI C/R=0 PM=0 E=0 IP=0 N(U)=4 FCS=33e6af
gprs_llc_pdu_decode() returns 0
gprs_llc_pdu_hdr_dump(): SAPI=1 (GMM), 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
gprs_llc_pdu_encode() returns 0
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 (GMM), UI func=UI C/R=1 PM=1 E=0 IP=0 N(U)=0 FCS=9a8ede
gprs_llc_pdu_decode() returns 0
gprs_llc_pdu_hdr_dump(): SAPI=1 (GMM), 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
gprs_llc_pdu_encode() returns 0
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 (GMM), U func=NULL C/R=0 P/F=0 FCS=b3a21c
gprs_llc_pdu_decode() returns 0
gprs_llc_pdu_hdr_dump(): SAPI=1 (GMM), 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
gprs_llc_pdu_encode() returns 0
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 (SNDCP3), U func=XID C/R=1 P/F=1 FCS=4e7c8c
gprs_llc_pdu_decode() returns 0
gprs_llc_pdu_hdr_dump(): SAPI=3 (SNDCP3), 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
gprs_llc_pdu_encode() returns 0
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 (SNDCP3), U func=XID C/R=0 P/F=1 FCS=84f916
gprs_llc_pdu_decode() returns 0
gprs_llc_pdu_hdr_dump(): SAPI=3 (SNDCP3), 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
gprs_llc_pdu_encode() returns 0
gprs_llc_pdu_encode(): 03fb1604d216f984
memcmp() returns 0
test_xid_dec_enc(): decoding testData[0] = 25401602082c
osmo_gprs_llc_xid_decode() returns 3
field[00]: type=0x09 (kD) val=64
field[01]: type=0x05 (N201-U) val=520
field[02]: type=0x0b (L3-Params) data[] (len=0):
test_xid_dec_enc(): encoding decoded testData[0]
osmo_gprs_llc_xid_encode() returns 0
osmo_gprs_llc_xid_encode(): 25401602082c
memcmp() returns 0
test_xid_dec_enc(): decoding testData[1] = 16020825402c
osmo_gprs_llc_xid_decode() returns 3
field[00]: type=0x05 (N201-U) val=520
field[01]: type=0x09 (kD) val=64
field[02]: type=0x0b (L3-Params) data[] (len=0):
test_xid_dec_enc(): encoding decoded testData[1]
osmo_gprs_llc_xid_encode() returns 0
osmo_gprs_llc_xid_encode(): 16020825402c
memcmp() returns 0
test_xid_dec_enc(): decoding testData[2] = 01001601f41a05df
osmo_gprs_llc_xid_decode() returns 3
field[00]: type=0x00 (LLC-Version) val=0
field[01]: type=0x05 (N201-U) val=500
field[02]: type=0x06 (N201-I) val=1503
test_xid_dec_enc(): encoding decoded testData[2]
osmo_gprs_llc_xid_encode() returns 0
osmo_gprs_llc_xid_encode(): 01001601f41a05df
memcmp() returns 0
test_xid_dec_enc(): decoding testData[3] = 01001605f01a05f0ac18112233445566
osmo_gprs_llc_xid_decode() returns 4
field[00]: type=0x00 (LLC-Version) val=0
field[01]: type=0x05 (N201-U) val=1520
field[02]: type=0x06 (N201-I) val=1520
field[03]: type=0x0b (L3-Params) data[] (len=6): 112233445566
test_xid_dec_enc(): encoding decoded testData[3]
osmo_gprs_llc_xid_encode() returns 0
osmo_gprs_llc_xid_encode(): 01001605f01a05f0ac18112233445566
memcmp() returns 0
test_xid_dec_enc(): decoding testData[4] = 01001601f41a02081e00002200002908
osmo_gprs_llc_xid_decode() returns 6
field[00]: type=0x00 (LLC-Version) val=0
field[01]: type=0x05 (N201-U) val=500
field[02]: type=0x06 (N201-I) val=520
field[03]: type=0x07 (mD) val=0
field[04]: type=0x08 (mU) val=0
field[05]: type=0x0a (kU) val=8
test_xid_dec_enc(): encoding decoded testData[4]
osmo_gprs_llc_xid_encode() returns 0
osmo_gprs_llc_xid_encode(): 01001601f41a02081e00002200002908
memcmp() returns 0
test_xid_enc_dec(): encoding hand-crafted testData
osmo_gprs_llc_xid_encode() returns 0
osmo_gprs_llc_xid_encode(): 01000e0fff110a8810424242428410deadbeefac1811223344556630
test_xid_enc_dec(): decoding encoded hand-crafted testData
osmo_gprs_llc_xid_decode() returns 7
field[00]: type=0x00 (LLC-Version) val=0
field[01]: type=0x03 (T200) val=4095
field[02]: type=0x04 (N200) val=10
field[03]: type=0x02 (IOV-I) val=1111638594
field[04]: type=0x01 (IOV-UI) val=3735928559
field[05]: type=0x0b (L3-Params) data[] (len=6): 112233445566
field[06]: type=0x0c (Reset) val=0

161
tests/llc/xid_codec_test.c Normal file
View File

@ -0,0 +1,161 @@
/* LLC XID 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>
#include <osmocom/gprs/llc/llc_private.h>
static void *tall_ctx = NULL;
static void print_xid_fields(const struct gprs_llc_xid_field *fields,
unsigned int num_fields)
{
for (unsigned int i = 0; i < num_fields; i++) {
const struct gprs_llc_xid_field *field = &fields[i];
printf(" field[%02d]: type=0x%02x (%s)",
i, field->type, gprs_llc_xid_type_name(field->type));
if (field->type == OSMO_GPRS_LLC_XID_T_L3_PAR) {
printf(" data[] (len=%u): %s\n", field->var.val_len,
osmo_hexdump_nospc(field->var.val, field->var.val_len));
} else {
printf(" val=%u\n", field->val);
}
}
}
static void test_xid_dec_enc(void)
{
static const char * const testData[] = {
/* TODO: more test vectors */
"25401602082c",
"16020825402c",
"01001601f41a05df",
"01001605f01a05f0ac18112233445566",
"01001601f41a02081e00002200002908",
};
struct msgb *msg = msgb_alloc(1024, "LLC-XID");
OSMO_ASSERT(msg != NULL);
for (unsigned int i = 0; i < ARRAY_SIZE(testData); i++) {
struct gprs_llc_xid_field fields[16] = { 0 };
uint8_t xid[256];
size_t xid_len;
int rc;
printf("%s(): decoding testData[%u] = %s\n", __func__, i, testData[i]);
rc = osmo_hexparse(testData[i], &xid[0], sizeof(xid));
xid_len = strlen(testData[i]) / 2;
OSMO_ASSERT(rc == xid_len);
rc = gprs_llc_xid_decode(fields, ARRAY_SIZE(fields),
&xid[0], xid_len);
printf(" gprs_llc_xid_decode() returns %d\n", rc);
if (rc > 0)
print_xid_fields(&fields[0], rc);
else
continue;
printf("%s(): encoding decoded testData[%u]\n", __func__, i);
msgb_reset(msg);
rc = gprs_llc_xid_encode(msgb_data(msg), msgb_tailroom(msg), fields, rc);
printf(" gprs_llc_xid_encode() returns %d\n", rc);
if (rc < 0)
continue;
msgb_put(msg, rc);
printf(" gprs_llc_xid_encode(): %s\n", osmo_hexdump_nospc(msg->data, msg->len));
printf(" memcmp() returns %d\n", memcmp(&xid, msg->data, xid_len));
}
msgb_free(msg);
printf("\n");
}
static void test_xid_enc_dec(void)
{
struct gprs_llc_xid_field dec_fields[16] = { 0 };
int rc;
uint8_t l3_params[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
const struct gprs_llc_xid_field test_fields[] = {
{ .type = OSMO_GPRS_LLC_XID_T_VERSION, .val = 0 },
{ .type = OSMO_GPRS_LLC_XID_T_T200, .val = 4095 },
{ .type = OSMO_GPRS_LLC_XID_T_N200, .val = 10 },
{ .type = OSMO_GPRS_LLC_XID_T_IOV_I, .val = 0x42424242 },
{ .type = OSMO_GPRS_LLC_XID_T_IOV_UI, .val = 0xdeadbeef },
{ .type = OSMO_GPRS_LLC_XID_T_L3_PAR,
.var = { .val_len = sizeof(l3_params), .val = &l3_params[0] } },
{ .type = OSMO_GPRS_LLC_XID_T_RESET },
};
struct msgb *msg = msgb_alloc(1024, "LLC-XID");
OSMO_ASSERT(msg != NULL);
printf("%s(): encoding hand-crafted testData\n", __func__);
rc = gprs_llc_xid_encode(msgb_data(msg), msgb_tailroom(msg), &test_fields[0], ARRAY_SIZE(test_fields));
printf(" gprs_llc_xid_encode() returns %d\n", rc);
OSMO_ASSERT(rc > 0);
msgb_put(msg, rc);
printf(" gprs_llc_xid_encode(): %s\n", osmo_hexdump_nospc(msg->data, msg->len));
printf("%s(): decoding encoded hand-crafted testData\n", __func__);
rc = gprs_llc_xid_decode(&dec_fields[0], ARRAY_SIZE(dec_fields),
msg->data, msg->len);
printf(" gprs_llc_xid_decode() returns %d\n", rc);
if (rc > 0)
print_xid_fields(&dec_fields[0], rc);
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_xid_dec_enc();
test_xid_enc_dec();
talloc_free(tall_ctx);
}

View File

View File

@ -0,0 +1,63 @@
test_xid_dec_enc(): decoding testData[0] = 25401602082c
gprs_llc_xid_decode() returns 3
field[00]: type=0x09 (kD) val=64
field[01]: type=0x05 (N201-U) val=520
field[02]: type=0x0b (L3-Params) data[] (len=0):
test_xid_dec_enc(): encoding decoded testData[0]
gprs_llc_xid_encode() returns 6
gprs_llc_xid_encode(): 25401602082c
memcmp() returns 0
test_xid_dec_enc(): decoding testData[1] = 16020825402c
gprs_llc_xid_decode() returns 3
field[00]: type=0x05 (N201-U) val=520
field[01]: type=0x09 (kD) val=64
field[02]: type=0x0b (L3-Params) data[] (len=0):
test_xid_dec_enc(): encoding decoded testData[1]
gprs_llc_xid_encode() returns 6
gprs_llc_xid_encode(): 16020825402c
memcmp() returns 0
test_xid_dec_enc(): decoding testData[2] = 01001601f41a05df
gprs_llc_xid_decode() returns 3
field[00]: type=0x00 (LLC-Version) val=0
field[01]: type=0x05 (N201-U) val=500
field[02]: type=0x06 (N201-I) val=1503
test_xid_dec_enc(): encoding decoded testData[2]
gprs_llc_xid_encode() returns 8
gprs_llc_xid_encode(): 01001601f41a05df
memcmp() returns 0
test_xid_dec_enc(): decoding testData[3] = 01001605f01a05f0ac18112233445566
gprs_llc_xid_decode() returns 4
field[00]: type=0x00 (LLC-Version) val=0
field[01]: type=0x05 (N201-U) val=1520
field[02]: type=0x06 (N201-I) val=1520
field[03]: type=0x0b (L3-Params) data[] (len=6): 112233445566
test_xid_dec_enc(): encoding decoded testData[3]
gprs_llc_xid_encode() returns 16
gprs_llc_xid_encode(): 01001605f01a05f0ac18112233445566
memcmp() returns 0
test_xid_dec_enc(): decoding testData[4] = 01001601f41a02081e00002200002908
gprs_llc_xid_decode() returns 6
field[00]: type=0x00 (LLC-Version) val=0
field[01]: type=0x05 (N201-U) val=500
field[02]: type=0x06 (N201-I) val=520
field[03]: type=0x07 (mD) val=0
field[04]: type=0x08 (mU) val=0
field[05]: type=0x0a (kU) val=8
test_xid_dec_enc(): encoding decoded testData[4]
gprs_llc_xid_encode() returns 16
gprs_llc_xid_encode(): 01001601f41a02081e00002200002908
memcmp() returns 0
test_xid_enc_dec(): encoding hand-crafted testData
gprs_llc_xid_encode() returns 28
gprs_llc_xid_encode(): 01000e0fff110a8810424242428410deadbeefac1811223344556630
test_xid_enc_dec(): decoding encoded hand-crafted testData
gprs_llc_xid_decode() returns 7
field[00]: type=0x00 (LLC-Version) val=0
field[01]: type=0x03 (T200) val=4095
field[02]: type=0x04 (N200) val=10
field[03]: type=0x02 (IOV-I) val=1111638594
field[04]: type=0x01 (IOV-UI) val=3735928559
field[05]: type=0x0b (L3-Params) data[] (len=6): 112233445566
field[06]: type=0x0c (Reset) val=0

View File

@ -1,6 +1,13 @@
AT_INIT
AT_BANNER([Regression tests])
AT_SETUP([llc/llc_prim])
AT_KEYWORDS([llc llc_prim])
cat $abs_srcdir/llc/llc_prim_test.ok > expout
cat $abs_srcdir/llc/llc_prim_test.err > experr
AT_CHECK([$abs_top_builddir/tests/llc/llc_prim_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([llc/pdu_codec])
AT_KEYWORDS([llc pdu codec])
cat $abs_srcdir/llc/pdu_codec_test.ok > expout
@ -8,6 +15,13 @@ cat $abs_srcdir/llc/pdu_codec_test.err > experr
AT_CHECK([$abs_top_builddir/tests/llc/pdu_codec_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([llc/xid_codec])
AT_KEYWORDS([llc xid codec])
cat $abs_srcdir/llc/xid_codec_test.ok > expout
cat $abs_srcdir/llc/xid_codec_test.err > experr
AT_CHECK([$abs_top_builddir/tests/llc/xid_codec_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([rlcmac/ts_44_018])
AT_KEYWORDS([rlcmac ts_44_018])
cat $abs_srcdir/rlcmac/ts_44_018_test.ok > expout