diff options
Diffstat (limited to 'openbsc/src/gsm_04_11.c')
-rw-r--r-- | openbsc/src/gsm_04_11.c | 226 |
1 files changed, 158 insertions, 68 deletions
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c index 59020746f..8e3c64974 100644 --- a/openbsc/src/gsm_04_11.c +++ b/openbsc/src/gsm_04_11.c @@ -56,11 +56,6 @@ void *tall_gsms_ctx; static u_int32_t new_callref = 0x40000001; -struct value_string { - u_int32_t value; - const char *str; -}; - static const struct value_string cp_cause_strs[] = { { GSM411_CP_CAUSE_NET_FAIL, "Network Failure" }, { GSM411_CP_CAUSE_CONGESTION, "Congestion" }, @@ -104,19 +99,6 @@ static const struct value_string rp_cause_strs[] = { { 0, NULL } }; -const char *get_value_string(const struct value_string *vs, u_int32_t val) -{ - int i; - - for (i = 0;; i++) { - if (vs[i].value == 0 && vs[i].str == NULL) - break; - if (vs[i].value == val) - return vs[i].str; - } - return "unknown"; -} - struct gsm_sms *sms_alloc(void) { return talloc_zero(tall_gsms_ctx, struct gsm_sms); @@ -216,40 +198,172 @@ static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans, return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA); } -static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp) +/* Turn int into semi-octet representation: 98 => 0x89 */ +static u_int8_t bcdify(u_int8_t value) +{ + u_int8_t ret; + + ret = value / 10; + ret |= (value % 10) << 4; + + return ret; +} + +/* Turn semi-octet representation into int: 0x89 => 98 */ +static u_int8_t unbcdify(u_int8_t value) { + u_int8_t ret; + + if ((value & 0x0F) > 9 || (value >> 4) > 9) + DEBUGP(DSMS, "unbcdify got too big nibble: 0x%02X\n", value); + + ret = (value&0x0F)*10; + if (ret > 90) + ret += value>>4; + + return ret; +} + +/* Generate 03.40 TP-SCTS */ +static void gsm340_gen_scts(u_int8_t *scts, time_t time) +{ + struct tm *tm = localtime(&time); + + *scts++ = bcdify(tm->tm_year % 100); + *scts++ = bcdify(tm->tm_mon + 1); + *scts++ = bcdify(tm->tm_mday); + *scts++ = bcdify(tm->tm_hour); + *scts++ = bcdify(tm->tm_min); + *scts++ = bcdify(tm->tm_sec); + *scts++ = bcdify(tm->tm_gmtoff/(60*15)); +} + +/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */ +static time_t gsm340_scts(u_int8_t *scts) +{ + struct tm tm; + + u_int8_t yr = unbcdify(*scts++); + + if (yr <= 80) + tm.tm_year = 100 + yr; + else + tm.tm_year = yr; + tm.tm_mon = unbcdify(*scts++) - 1; + tm.tm_mday = unbcdify(*scts++); + tm.tm_hour = unbcdify(*scts++); + tm.tm_min = unbcdify(*scts++); + tm.tm_sec = unbcdify(*scts++); + /* according to gsm 03.40 time zone is + "expressed in quarters of an hour" */ + tm.tm_gmtoff = unbcdify(*scts++) * 15*60; + + return mktime(&tm); +} + +/* Return the default validity period in minutes */ +static unsigned long gsm340_vp_default(void) +{ + unsigned long minutes; + /* Default validity: two days */ + minutes = 24 * 60 * 2; + return minutes; +} + +/* Decode validity period format 'relative' */ +static unsigned long gsm340_vp_relative(u_int8_t *sms_vp) +{ + /* Chapter 9.2.3.12.1 */ u_int8_t vp; unsigned long minutes; + vp = *(sms_vp); + if (vp <= 143) + minutes = vp + 1 * 5; + else if (vp <= 167) + minutes = 12*60 + (vp-143) * 30; + else if (vp <= 196) + minutes = vp-166 * 60 * 24; + else + minutes = vp-192 * 60 * 24 * 7; + return minutes; +} + +/* Decode validity period format 'absolute' */ +static unsigned long gsm340_vp_absolute(u_int8_t *sms_vp) +{ + /* Chapter 9.2.3.12.2 */ + time_t expires, now; + unsigned long minutes; + + expires = gsm340_scts(sms_vp); + now = mktime(gmtime(NULL)); + if (expires <= now) + minutes = 0; + else + minutes = (expires-now)/60; + return minutes; +} + +/* Decode validity period format 'relative in integer representation' */ +static unsigned long gsm340_vp_relative_integer(u_int8_t *sms_vp) +{ + u_int8_t vp; + unsigned long minutes; + vp = *(sms_vp); + if (vp == 0) { + DEBUGP(DSMS, "reserved relative_integer validity period\n"); + return gsm340_vp_default(); + } + minutes = vp/60; + return minutes; +} + +/* Decode validity period format 'relative in semi-octet representation' */ +static unsigned long gsm340_vp_relative_semioctet(u_int8_t *sms_vp) +{ + unsigned long minutes; + minutes = unbcdify(*sms_vp++)*60; /* hours */ + minutes += unbcdify(*sms_vp++); /* minutes */ + minutes += unbcdify(*sms_vp++)/60; /* seconds */ + return minutes; +} + +/* decode validity period. return minutes */ +static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp) +{ + u_int8_t fi; /* functionality indicator */ + switch (sms_vpf) { case GSM340_TP_VPF_RELATIVE: - /* Chapter 9.2.3.12.1 */ - vp = *(sms_vp); - if (vp <= 143) - minutes = vp + 1 * 5; - else if (vp <= 167) - minutes = 12*60 + (vp-143) * 30; - else if (vp <= 196) - minutes = vp-166 * 60 * 24; - else - minutes = vp-192 * 60 * 24 * 7; - break; + return gsm340_vp_relative(sms_vp); case GSM340_TP_VPF_ABSOLUTE: - /* Chapter 9.2.3.12.2 */ - /* FIXME: like service center time stamp */ - DEBUGP(DSMS, "VPI absolute not implemented yet\n"); - break; + return gsm340_vp_absolute(sms_vp); case GSM340_TP_VPF_ENHANCED: /* Chapter 9.2.3.12.3 */ - /* FIXME: implementation */ - DEBUGP(DSMS, "VPI enhanced not implemented yet\n"); - break; + fi = *sms_vp++; + /* ignore additional fi */ + if (fi & (1<<7)) sms_vp++; + /* read validity period format */ + switch (fi & 0b111) { + case 0b000: + return gsm340_vp_default(); /* no vpf specified */ + case 0b001: + return gsm340_vp_relative(sms_vp); + case 0b010: + return gsm340_vp_relative_integer(sms_vp); + case 0b011: + return gsm340_vp_relative_semioctet(sms_vp); + default: + /* The GSM spec says that the SC should reject any + unsupported and/or undefined values. FIXME */ + DEBUGP(DSMS, "Reserved enhanced validity period format\n"); + return gsm340_vp_default(); + } case GSM340_TP_VPF_NONE: - /* Default validity: two days */ - minutes = 24 * 60 * 2; - break; + default: + return gsm340_vp_default(); } - return minutes; } /* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */ @@ -317,30 +431,6 @@ static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len, return len_in_bytes; } -static u_int8_t bcdify(u_int8_t value) -{ - u_int8_t ret; - - ret = value / 10; - ret |= (value % 10) << 4; - - return ret; -} - -/* Generate 03.40 TP-SCTS */ -static void gsm340_gen_scts(u_int8_t *scts, time_t time) -{ - struct tm *tm = localtime(&time); - - *scts++ = bcdify(tm->tm_year % 100); - *scts++ = bcdify(tm->tm_mon + 1); - *scts++ = bcdify(tm->tm_mday); - *scts++ = bcdify(tm->tm_hour); - *scts++ = bcdify(tm->tm_min); - *scts++ = bcdify(tm->tm_sec); - *scts++ = 0; /* FIXME: timezone */ -} - /* generate a msgb containing a TPDU derived from struct gsm_sms, * returns total size of TPDU */ static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms) @@ -470,6 +560,8 @@ static int gsm340_rx_tpdu(struct msgb *msg) case GSM340_TP_VPF_ABSOLUTE: case GSM340_TP_VPF_ENHANCED: sms_vp = smsp; + /* the additional functionality indicator... */ + if (*smsp & (1<<7)) smsp++; smsp += 7; break; case GSM340_TP_VPF_NONE: @@ -674,7 +766,7 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, * successfully receive the SMS. We need to investigate * the cause and take action depending on it */ - DEBUGP(DSMS, "RX SMS RP-ERROR, cause %d (%s)\n", cause, + DEBUGP(DSMS, "RX SMS RP-ERROR, cause %d:%d (%s)\n", cause_len, cause, get_value_string(rp_cause_strs, cause)); if (!trans->sms.is_mt) { @@ -1068,8 +1160,6 @@ static int subscr_sig_cb(unsigned int subsys, unsigned int signal, struct gsm_lchan *lchan; struct gsm_sms *sms; - u_int32_t token; - switch (signal) { case S_SUBSCR_ATTACHED: /* A subscriber has attached. Check if there are |