aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gprs_bssgp_pcu.cpp37
-rw-r--r--src/gprs_rlcmac.h1
-rw-r--r--src/gprs_rlcmac_data.cpp47
-rw-r--r--src/pcu_vty.c50
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;