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".
This commit is contained in:
Andreas Eversberg 2012-07-21 11:09:58 +02:00
parent 8b761a3419
commit 24131bf55b
4 changed files with 131 additions and 4 deletions

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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;