#pragma once /* 3GPP TS 44.064, private header */ #include #include #include #include #include #include #include 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]; /* Internal management */ uint32_t age_timestamp; /* TS 44.064 § C.2: "In addition, all states should observe the suspended * operation (reception of LLGMM-SUSPEND-REQ) restrictions" */ bool suspended; }; 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_llc_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); struct osmo_gprs_llc_prim *gprs_llc_prim_alloc_grr_unitdata_req( uint32_t tlli, uint8_t *ll_pdu, size_t ll_pdu_len); /* 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_establish_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_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_assign_ind(uint32_t old_tlli, uint32_t new_tlli); 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); int gprs_llc_llme_submit_prim_ll_assign_ind(uint32_t old_tlli, uint32_t new_tlli); /* 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_rx_unitdata_ind(struct gprs_llc_lle *lle, uint8_t *ll_pdu, size_t ll_pdu_len, struct gprs_llc_pdu_decoded *pdu_dec); void gprs_llc_llme_free(struct gprs_llc_llme *llme); int gprs_llc_lle_tx_sabm(struct gprs_llc_lle *lle, uint8_t *l3par, unsigned int l3par_len); 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, uint8_t radio_prio); int gprs_llc_lle_tx_null(const struct gprs_llc_lle *lle); /* 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); void gprs_llc_dump_xid_fields(const struct gprs_llc_xid_field *xid_fields, size_t xid_fields_len, unsigned int logl); /* 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_llc_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_llc_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)