diff --git a/openbsc/include/openbsc/sgsn.h b/openbsc/include/openbsc/sgsn.h index 1ce608a08..b7825685b 100644 --- a/openbsc/include/openbsc/sgsn.h +++ b/openbsc/include/openbsc/sgsn.h @@ -50,4 +50,12 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn, struct tlv_parsed *tp); int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx); +/* gprs_sndcp.c */ + +/* Entry point for the SNSM-ACTIVATE.indication */ +int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi); +/* Called by SNDCP when it has received/re-assembled a N-PDU */ +int sgsn_rx_sndcp_ud_ind(uint32_t tlli, uint8_t nsapi, struct msgb *msg, + uint32_t npdu_len, uint8_t *npdu); + #endif diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index 11b2ac934..30b45f676 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -19,7 +19,7 @@ osmo_gbproxy_SOURCES = gb_proxy.c gb_proxy_main.c gb_proxy_vty.c \ $(top_srcdir)/src/socket.c $(top_srcdir)/src/debug.c osmo_gbproxy_LDADD = libgb.a $(top_builddir)/src/libvty.a -osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c \ +osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c \ sgsn_main.c sgsn_vty.c sgsn_libgtp.c \ $(top_srcdir)/src/socket.c $(top_srcdir)/src/debug.c osmo_sgsn_LDADD = libgb.a $(top_builddir)/src/libvty.a -lgtp diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c index 26730d4c2..cb581f162 100644 --- a/openbsc/src/gprs/gprs_llc.c +++ b/openbsc/src/gprs/gprs_llc.c @@ -340,6 +340,15 @@ static int gprs_llc_hdr_rx(struct gprs_llc_hdr_parsed *gph, gprs_llc_tx_xid(lle, resp); } break; + case GPRS_LLC_UI: + if (gph->seq_tx < lle->vu_recv) { + LOGP(DLLC, "TLLI=%08x dropping UI, vurecv %u <= %u\n", + gph->seq_tx, lle->vu_recv); + return -EIO; + } + /* Increment the sequence number that we expect in the next frame */ + lle->vu_recv = (gph->seq_tx + 1) % 512; + break; } return 0; @@ -583,16 +592,18 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) /* send LL_UNITDATA_IND to GMM */ rc = gsm0408_gprs_rcvmsg(msg, lle->llme); break; - case GPRS_SAPI_TOM2: - case GPRS_SAPI_TOM8: - /* FIXME: send LL_DATA_IND/LL_UNITDATA_IND to TOM */ case GPRS_SAPI_SNDCP3: case GPRS_SAPI_SNDCP5: case GPRS_SAPI_SNDCP9: case GPRS_SAPI_SNDCP11: - /* FIXME: send LL_DATA_IND/LL_UNITDATA_IND to SNDCP */ + /* send LL_DATA_IND/LL_UNITDATA_IND to SNDCP */ + rc = sndcp_llunitdata_ind(msg, lle, llhp.data, llhp.data_len); + break; case GPRS_SAPI_SMS: /* FIXME */ + case GPRS_SAPI_TOM2: + case GPRS_SAPI_TOM8: + /* FIXME: send LL_DATA_IND/LL_UNITDATA_IND to TOM */ default: LOGP(DLLC, LOGL_NOTICE, "Unsupported SAPI %u\n", llhp.sapi); rc = -EINVAL; diff --git a/openbsc/src/gprs/gprs_sndcp.c b/openbsc/src/gprs/gprs_sndcp.c index 99a7638be..ec704e0d0 100644 --- a/openbsc/src/gprs/gprs_sndcp.c +++ b/openbsc/src/gprs/gprs_sndcp.c @@ -32,6 +32,7 @@ #include #include #include +#include /* Chapter 7.2: SN-PDU Formats */ struct sndcp_common_hdr { @@ -44,7 +45,7 @@ struct sndcp_common_hdr { /* octet 2 */ uint8_t pcomp; uint8_t dcomp; -}; +} __attribute__((packed)); struct sndcp_udata_hdr { /* octet 3 */ @@ -52,42 +53,172 @@ struct sndcp_udata_hdr { uint8_t seg_nr:4; /* octet 4 */ uint8_t npdu_low; +} __attribute__((packed)); + +/* See 6.7.1.2 Reassembly */ +enum sndcp_rx_state { + SNDCP_RX_S_FIRST, + SNDCP_RX_S_SUBSEQ, + SNDCP_RX_S_DISCARD, +}; + + +static void *tall_sndcp_ctx; + +/* A fragment queue entry, containing one framgent of a N-PDU */ +struct frag_queue_entry { + struct llist_head list; + uint8_t seg_nr; + uint32_t data_len; + uint8_t data[0]; +}; + +/* A fragment queue header, maintaining list of fragments for one N-PDU */ +struct frag_queue_head { + uint16_t npdu; + + /* linked list of frag_queue_entry: one for each fragment */ + struct llist_head frag_list; + + struct timer_list timer; }; struct sndcp_entity { + struct llist_head list; + + struct gprs_llc_lle *lle; + uint8_t nsapi; + + enum sndcp_rx_state rx_state; + struct frag_queue_head fqueue; }; -/* Entry point for the LL-UNITDATA.indication */ -int sndcp_unitdata_ind(struct msgb *msg, uint8_t sapi, uint8_t *hdr, uint8_t len) -{ - struct sndcp_udata_hdr *suh; - uint16_t npdu; +LLIST_HEAD(sndcp_entities); - if (suh->type == 0) { +#if 0 +static struct frag_queue_entry _find_fqe(struct freg_queue_head *fqh, uint8_t seg_nr) +{ + +} + +static struct frag_queue_head _find_fqh(struct sndcp_entity *sne, uint16_t npdu) +{ + +} + +static int ul_enqueue_fragment(struct sndcp_entity *sne, uint16_t npdu, + uint8_t seg_nr, uint32_t data_len, uint8_t *data) +{ + +} +#endif + +static struct sndcp_entity *sndcp_entity_by_lle(const struct gprs_llc_lle *lle, + uint8_t nsapi) +{ + struct sndcp_entity *sne; + + llist_for_each_entry(sne, &sndcp_entities, list) { + if (sne->lle == lle && sne->nsapi == nsapi) + return sne; + } + return NULL; +} + +static struct sndcp_entity *sndcp_entity_alloc(struct gprs_llc_lle *lle, + uint8_t nsapi) +{ + struct sndcp_entity *sne; + + sne = talloc_zero(tall_sndcp_ctx, struct sndcp_entity); + if (!sne) + return NULL; + + sne->lle = lle; + sne->nsapi = nsapi; + sne->fqueue.timer.data = sne; + //sne->fqueue.timer.cb = FIXME; + sne->rx_state = SNDCP_RX_S_FIRST; + + return sne; +} + +/* Entry point for the SNSM-ACTIVATE.indication */ +int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) +{ + if (sndcp_entity_by_lle(lle, nsapi)) + return -EEXIST; + + if (!sndcp_entity_alloc(lle, nsapi)) + return -ENOMEM; + + return 0; +} + +/* Section 5.1.2.17 LL-UNITDATA.ind */ +int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t *hdr, uint8_t len) +{ + struct sndcp_entity *sne; + struct sndcp_common_hdr *sch = (struct sndcp_common_hdr *)hdr; + struct sndcp_udata_hdr *suh; + uint8_t *comp, *npdu; + uint16_t npdu_num; + int npdu_len; + + if (sch->type == 0) { LOGP(DGPRS, LOGL_ERROR, "SN-DATA PDU at unitdata_ind() function\n"); return -EINVAL; } - npdu = (suh->npdu_high << 8) | suh->npdu_low; + if (len < sizeof(*sch) + sizeof(*comp) + sizeof(*suh)) { + LOGP(DGPRS, LOGL_ERROR, "SN-UNITDATA PDU too short (%u)\n", len); + return -EIO; + } + + sne = sndcp_entity_by_lle(lle, sch->nsapi); + if (!sne) { + LOGP(DGPRS, LOGL_ERROR, "Message for non-existing SNDCP Entity\n"); + return -EIO; + } + + if (!sch->first || sch->more) { + /* FIXME: implement fragment re-assembly */ + LOGP(DGPRS, LOGL_ERROR, "We don't support reassembly yet\n"); + return -EIO; + } + + comp = (hdr + sizeof(struct sndcp_common_hdr)); + if (comp) { + LOGP(DGPRS, LOGL_ERROR, "We don't support compression yet\n"); + return -EIO; + } + suh = (struct sndcp_udata_hdr *) (comp + sizeof(*comp)); + npdu_num = (suh->npdu_high << 8) | suh->npdu_low; + + npdu = (uint8_t *)suh + sizeof(*suh); + npdu_len = (msg->data + msg->len) - npdu; + if (npdu_len) { + LOGP(DGPRS, LOGL_ERROR, "Short SNDCP N-PDU: %d\n", npdu_len); + return -EIO; + } + /* actually send the N-PDU to the SGSN core code, which then + * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ + return sgsn_rx_sndcp_ud_ind(lle->llme->tlli, sne->nsapi, msg, npdu_len, npdu); } /* Section 5.1.2.1 LL-RESET.ind */ -static int sndcp_ll_reset_ind(struct sndcp_entity *se,) +static int sndcp_ll_reset_ind(struct sndcp_entity *se) { /* treat all outstanding SNDCP-LLC request type primitives as not sent */ /* reset all SNDCP XID parameters to default values */ } -/* Section 5.1.2.17 LL-UNITDATA.ind */ -static int sndcp_ll_unitdata_ind() -{ -} - static int sndcp_ll_status_ind() { /* inform the SM sub-layer by means of SNSM-STATUS.req */ } +#if 0 static struct sndcp_state_list {{ uint32_t states; unsigned int type; @@ -127,3 +258,4 @@ static int sndcp_rx_llc_prim() case LL_UNITDATA_IND: case LL_STATUS_IND: } +#endif diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c index 7df932594..03de8ccd2 100644 --- a/openbsc/src/gprs/sgsn_libgtp.c +++ b/openbsc/src/gprs/sgsn_libgtp.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include @@ -255,6 +257,9 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) goto reject; } + /* Activate the SNDCP layer */ + sndcp_sm_activate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi); + /* Send PDP CTX ACT to MS */ return gsm48_tx_gsm_act_pdp_acc(pctx); @@ -356,6 +361,36 @@ static int cb_data_ind(struct pdp_t *pdp, void *packet, unsigned int len) return 0; } +/* Called by SNDCP when it has received/re-assembled a N-PDU */ +int sgsn_rx_sndcp_ud_ind(uint32_t tlli, uint8_t nsapi, struct msgb *msg, + uint32_t npdu_len, uint8_t *npdu) +{ + struct sgsn_mm_ctx *mmctx; + struct sgsn_pdp_ctx *pdp; + struct gprs_ra_id ra_id; + + /* look-up the MM context for this message */ + bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); + mmctx = sgsn_mm_ctx_by_tlli(tlli, &ra_id); + if (!mmctx) { + LOGP(DGPRS, LOGL_ERROR, + "Cannot find MM CTX for TLLI %08x\n", tlli); + return -EIO; + } + /* look-up the PDP context for this message */ + pdp = sgsn_pdp_ctx_by_nsapi(mmctx, nsapi); + if (!pdp) { + LOGP(DGPRS, LOGL_ERROR, "Cannot find PDP CTX for " + "TLLI=%08x, NSAPI=%u\n", tlli, nsapi); + return -EIO; + } + if (!pdp->lib) { + LOGP(DGPRS, LOGL_ERROR, "PDP CTX without libgtp\n"); + return -EIO; + } + return gtp_data_req(pdp->ggsn->gsn, pdp->lib, npdu, npdu_len); +} + /* libgtp select loop integration */ static int sgsn_gtp_fd_cb(struct bsc_fd *fd, unsigned int what) {