From 24131bf55bc010e8238b7b1bb2b761dae4137dee Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 21 Jul 2012 11:09:58 +0200 Subject: [PATCH] Add check of lifetime of LLC frame If lifetime expires of queued LLC frames, they are discarded. The number of discarded frames and the sum of their octets are reported to SGSN via LLC-DISCARDED message. The lifetime can be overridden via VTY. The value can be centi-seconds or "infinite". --- src/gprs_bssgp_pcu.cpp | 37 +++++++++++++++++++++++++++-- src/gprs_rlcmac.h | 1 + src/gprs_rlcmac_data.cpp | 47 +++++++++++++++++++++++++++++++++++-- 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 #include +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;