diff options
-rw-r--r-- | src/gprs_bssgp_pcu.cpp | 37 | ||||
-rw-r--r-- | src/gprs_rlcmac.h | 1 | ||||
-rw-r--r-- | src/gprs_rlcmac_data.cpp | 47 | ||||
-rw-r--r-- | src/pcu_vty.c | 50 |
4 files changed, 131 insertions, 4 deletions
diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp index 945addce..217acdb9 100644 --- a/src/gprs_bssgp_pcu.cpp +++ b/src/gprs_bssgp_pcu.cpp @@ -111,6 +111,21 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp) bitvec_read_field(block, rp, 4); // SMS Value } } + /* get lifetime */ + uint16_t delay_csec = 0xffff; + if (TLVP_PRESENT(tp, BSSGP_IE_PDU_LIFETIME)) + { + uint8_t lt_len = TLVP_LEN(tp, BSSGP_IE_PDU_LIFETIME); + uint16_t *lt = (uint16_t *) TLVP_VAL(tp, BSSGP_IE_PDU_LIFETIME); + if (lt_len == 2) + delay_csec = ntohs(*lt); + else + LOGP(DBSSGP, LOGL_NOTICE, "BSSGP invalid length of " + "PDU_LIFETIME IE\n"); + } else + LOGP(DBSSGP, LOGL_NOTICE, "BSSGP missing mandatory " + "PDU_LIFETIME IE\n"); + LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len); /* check for existing TBF */ @@ -128,10 +143,28 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp) tbf_update(tbf); gprs_rlcmac_trigger_downlink_assignment(tbf, 1, NULL); } else { - /* the TBF exists, so we must write it in the queue */ - struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue"); + /* the TBF exists, so we must write it in the queue + * we prepend lifetime in front of PDU */ + struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + struct timeval *tv; + struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv), + "llc_pdu_queue"); if (!llc_msg) return -ENOMEM; + tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv)); + if (bts->force_llc_lifetime) + delay_csec = bts->force_llc_lifetime; + /* keep timestap at 0 for infinite delay */ + if (delay_csec != 0xffff) { + /* calculate timestamp of timeout */ + gettimeofday(tv, NULL); + tv->tv_usec += (delay_csec % 100) * 10000; + tv->tv_sec += delay_csec / 100; + if (tv->tv_usec > 999999) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } + } memcpy(msgb_put(llc_msg, len), data, len); msgb_enqueue(&tbf->llc_queue, llc_msg); /* set ms class for updating TBF */ diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h index 42439e2e..71ff6086 100644 --- a/src/gprs_rlcmac.h +++ b/src/gprs_rlcmac.h @@ -66,6 +66,7 @@ struct gprs_rlcmac_bts { uint8_t cs4; uint8_t initial_cs; uint8_t force_cs; /* 0=use from BTS 1=use from VTY */ + uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */ uint8_t t3142; uint8_t t3169; uint8_t t3191; diff --git a/src/gprs_rlcmac_data.cpp b/src/gprs_rlcmac_data.cpp index 10bc0177..2f67bd0a 100644 --- a/src/gprs_rlcmac_data.cpp +++ b/src/gprs_rlcmac_data.cpp @@ -22,6 +22,11 @@ #include <gprs_rlcmac.h> #include <pcu_l1_if.h> +extern "C" { +int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli, + uint8_t num_frames, uint32_t num_octets); +} + /* After receiving these frames, we send ack/nack. */ #define SEND_ACK_AFTER_FRAMES 20 @@ -868,6 +873,44 @@ int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta) * DL data block flow */ +static struct msgb *llc_dequeue(struct gprs_rlcmac_tbf *tbf) +{ + struct msgb *msg; + struct timeval *tv, tv_now; + uint32_t octets = 0, frames = 0; + + gettimeofday(&tv_now, NULL); + + while ((msg = msgb_dequeue(&tbf->llc_queue))) { + tv = (struct timeval *)msg->data; + msgb_pull(msg, sizeof(*tv)); + if (tv->tv_sec /* not infinite */ + && (tv_now.tv_sec > tv->tv_sec /* and secs expired */ + || (tv_now.tv_sec == tv->tv_sec /* .. or if secs equal .. */ + && tv_now.tv_usec > tv->tv_usec))) { /* .. usecs expired */ + LOGP(DRLCMACDL, LOGL_NOTICE, "Discarding LLC PDU of " + "DL TBF=%d, because lifetime limit reached\n", + tbf->tfi); + frames++; + octets += msg->len; + msgb_free(msg); + continue; + } + break; + } + + if (frames) { + if (frames > 0xff) + frames = 0xff; + if (octets > 0xffffff) + octets = 0xffffff; + bssgp_tx_llc_discarded(bctx, tbf->tlli, frames, octets); + } + + return msg; +} + + /* send DL data block * * The messages are fragmented and forwarded as data blocks. @@ -1083,7 +1126,7 @@ do_resend: /* reset LLC frame */ tbf->llc_index = tbf->llc_length = 0; /* dequeue next LLC frame, if any */ - msg = msgb_dequeue(&tbf->llc_queue); + msg = llc_dequeue(tbf); if (msg) { LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for " "TBF=%d (len=%d)\n", tbf->tfi, msg->len); @@ -1281,7 +1324,7 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final, LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n"); /* check for LLC PDU in the LLC Queue */ - msg = msgb_dequeue(&tbf->llc_queue); + msg = llc_dequeue(tbf); if (!msg) { struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; diff --git a/src/pcu_vty.c b/src/pcu_vty.c index 179080bb..2413f21d 100644 --- a/src/pcu_vty.c +++ b/src/pcu_vty.c @@ -81,6 +81,11 @@ static int config_write_pcu(struct vty *vty) vty_out(vty, "pcu%s", VTY_NEWLINE); if (bts->force_cs) vty_out(vty, " cs %d%s", bts->initial_cs, VTY_NEWLINE); + if (bts->force_llc_lifetime == 0xffff) + vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE); + else if (bts->force_llc_lifetime) + vty_out(vty, " queue lifetime %d%s", bts->force_llc_lifetime, + VTY_NEWLINE); } /* per-BTS configuration */ @@ -120,6 +125,48 @@ DEFUN(cfg_pcu_no_cs, return CMD_SUCCESS; } +#define QUEUE_STR "Packet queue options\n" +#define LIFETIME_STR "Set lifetime limit of LLC frame in centi-seconds " \ + "(overrides the value given by SGSN)\n" + +DEFUN(cfg_pcu_queue_lifetime, + cfg_pcu_queue_lifetime_cmd, + "queue lifetime <1-65534>", + QUEUE_STR LIFETIME_STR "Lifetime in centi-seconds") +{ + struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + uint8_t csec = atoi(argv[0]); + + bts->force_llc_lifetime = csec; + + return CMD_SUCCESS; +} + +DEFUN(cfg_pcu_queue_lifetime_inf, + cfg_pcu_queue_lifetime_inf_cmd, + "queue lifetime infinite", + QUEUE_STR LIFETIME_STR "Infinite lifetime") +{ + struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + + bts->force_llc_lifetime = 0xffff; + + return CMD_SUCCESS; +} + +DEFUN(cfg_pcu_no_queue_lifetime, + cfg_pcu_no_queue_lifetime_cmd, + "no queue lifetime", + NO_STR QUEUE_STR "Disable lifetime limit of LLC frame (use value given " + "by SGSN)\n") +{ + struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + + bts->force_llc_lifetime = 0; + + return CMD_SUCCESS; +} + static const char pcu_copyright[] = "Copyright (C) 2012 by ...\r\n" "License GNU GPL version 2 or later\r\n" @@ -145,6 +192,9 @@ int pcu_vty_init(const struct log_info *cat) install_default(PCU_NODE); install_element(PCU_NODE, &cfg_pcu_cs_cmd); install_element(PCU_NODE, &cfg_pcu_no_cs_cmd); + install_element(PCU_NODE, &cfg_pcu_queue_lifetime_cmd); + install_element(PCU_NODE, &cfg_pcu_queue_lifetime_inf_cmd); + install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd); install_element(PCU_NODE, &ournode_end_cmd); return 0; |