diff options
-rw-r--r-- | openbsc/include/openbsc/gsm_04_08.h | 12 | ||||
-rw-r--r-- | openbsc/src/gsm_04_08.c | 497 | ||||
-rw-r--r-- | openbsc/src/gsm_04_11.c | 2 |
3 files changed, 480 insertions, 31 deletions
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h index fe18f4ee5..03922acc7 100644 --- a/openbsc/include/openbsc/gsm_04_08.h +++ b/openbsc/include/openbsc/gsm_04_08.h @@ -394,10 +394,11 @@ struct gsm48_imsi_detach_ind { #define GSM48_IE_FACILITY 0x1c /* 10.5.4.15 */ #define GSM48_IE_PROGR_IND 0x1e /* 10.5.4.21 */ #define GSM48_IE_AUX_STATUS 0x24 /* 10.5.4.4 */ +#define GSM48_IE_NOTIFY 0x27 /* 10.5.4.20 */ #define GSM48_IE_KPD_FACILITY 0x2c /* 10.5.4.17 */ #define GSM48_IE_SIGNAL 0x34 /* 10.5.4.23 */ -#define GSM48_IE_CONN_NUM 0x4c /* 10.5.4.13 */ -#define GSM48_IE_CONN_SUBADDR 0x4d /* 10.5.4.14 */ +#define GSM48_IE_CONN_BCD 0x4c /* 10.5.4.13 */ +#define GSM48_IE_CONN_SUB 0x4d /* 10.5.4.14 */ #define GSM48_IE_CALLING_BCD 0x5c /* 10.5.4.9 */ #define GSM48_IE_CALLING_SUB 0x5d /* 10.5.4.10 */ #define GSM48_IE_CALLED_BCD 0x5e /* 10.5.4.7 */ @@ -412,6 +413,8 @@ struct gsm48_imsi_detach_ind { #define GSM48_IE_CLIR_SUPP 0xa1 /* 10.5.4.11a */ #define GSM48_IE_CLIR_INVOC 0xa2 /* 10.5.4.11b */ #define GSM48_IE_REV_C_SETUP 0xa3 /* 10.5.4.22a */ +#define GSM48_IE_REPEAT_CIR 0xd1 /* 10.5.4.22 */ +#define GSM48_IE_REPEAT_SEQ 0xd3 /* 10.5.4.22 */ /* Section 10.5.4.11 / Table 10.5.122 */ #define GSM48_CAUSE_CS_GSM 0x60 @@ -562,7 +565,8 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan); /* convert a ASCII phone number to call-control BCD */ int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len, - u_int8_t type, const char *input); -u_int8_t decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv); + int h_len, const char *input); +int decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv, + int h_len); #endif diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index 0630764a2..052991c3b 100644 --- a/openbsc/src/gsm_04_08.c +++ b/openbsc/src/gsm_04_08.c @@ -48,6 +48,10 @@ #define GSM48_ALLOC_SIZE 1024 #define GSM48_ALLOC_HEADROOM 128 +#define GSM_MAX_FACILITY 128 +#define GSM_MAX_SSVERSION 128 +#define GSM_MAX_USERUSER 128 + static const struct tlv_definition rsl_att_tlvdef = { .def = { [GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV }, @@ -64,10 +68,11 @@ static const struct tlv_definition rsl_att_tlvdef = { [GSM48_IE_FACILITY] = { TLV_TYPE_TLV }, [GSM48_IE_PROGR_IND] = { TLV_TYPE_TLV }, [GSM48_IE_AUX_STATUS] = { TLV_TYPE_TLV }, + [GSM48_IE_NOTIFY] = { TLV_TYPE_TV }, [GSM48_IE_KPD_FACILITY] = { TLV_TYPE_TV }, [GSM48_IE_SIGNAL] = { TLV_TYPE_TV }, - [GSM48_IE_CONN_NUM] = { TLV_TYPE_TLV }, - [GSM48_IE_CONN_SUBADDR] = { TLV_TYPE_TLV }, + [GSM48_IE_CONN_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_CONN_SUB] = { TLV_TYPE_TLV }, [GSM48_IE_CALLING_BCD] = { TLV_TYPE_TLV }, [GSM48_IE_CALLING_SUB] = { TLV_TYPE_TLV }, [GSM48_IE_CALLED_BCD] = { TLV_TYPE_TLV }, @@ -82,6 +87,8 @@ static const struct tlv_definition rsl_att_tlvdef = { [GSM48_IE_CLIR_SUPP] = { TLV_TYPE_T }, [GSM48_IE_CLIR_INVOC] = { TLV_TYPE_T }, [GSM48_IE_REV_C_SETUP] = { TLV_TYPE_T }, + [GSM48_IE_REPEAT_CIR] = { TLV_TYPE_T }, + [GSM48_IE_REPEAT_SEQ] = { TLV_TYPE_T }, /* FIXME: more elements */ }, }; @@ -332,16 +339,14 @@ static const char bcd_num_digits[] = { '8', '9', '*', '#', 'a', 'b', 'c', '\0' }; -/* decode a 'called party BCD number' as in 10.5.4.7 */ -u_int8_t decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv) +/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */ +int decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv, + int h_len) { u_int8_t in_len = bcd_lv[0]; int i; - if (in_len < 1) - return 0; - - for (i = 2; i <= in_len; i++) { + for (i = 1 + h_len; i <= in_len; i++) { /* lower nibble */ output_len--; if (output_len <= 1) @@ -357,8 +362,7 @@ u_int8_t decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv) if (output_len >= 1) *output++ = '\0'; - /* return number type / calling plan */ - return bcd_lv[1] & 0x3f; + return 0; } /* convert a single ASCII character to call-control BCD */ @@ -373,26 +377,21 @@ static int asc_to_bcd(const char asc) return -EINVAL; } -/* convert a ASCII phone number to 'called party BCD number' */ +/* convert a ASCII phone number to 'called/calling/connect party BCD number' */ int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len, - u_int8_t type, const char *input) + int h_len, const char *input) { int in_len = strlen(input); int i; - u_int8_t *bcd_cur = bcd_lv + 2; - - if (in_len/2 + 1 > max_len) - return -EIO; + u_int8_t *bcd_cur = bcd_lv + 1 + h_len; /* two digits per byte, plus type byte */ - bcd_lv[0] = in_len/2 + 1; + bcd_lv[0] = in_len/2 + h_len; if (in_len % 2) bcd_lv[0]++; - /* if the caller wants to create a valid 'calling party BCD - * number', then the extension bit MUST NOT be set. For the - * 'called party BCD number' it MUST be set. *sigh */ - bcd_lv[1] = type; + if (bcd_lv[0] > max_len) + return -EIO; for (i = 0; i < in_len; i++) { int rc = asc_to_bcd(input[i]); @@ -411,6 +410,451 @@ int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len, return (bcd_cur - bcd_lv); } +/* decode 'bearer capability' */ +static int decode_bearer_cap(int *transfer, int *mode, int *coding, + int *radio, int *speech_ctm, int *speech_ver, + const u_int8_t *lv) +{ + u_int8_t in_len = lv[0]; + int i, s; + + if (in_len < 1) + return -EINVAL; + + speech_ver[0] = -1; /* end of list, of maximum 7 values */ + + /* octet 3 */ + *transfer = lv[1] & 0x07; + *mode = (lv[1] & 0x08) >> 3; + *coding = (lv[1] & 0x10) >> 4; + *radio = (lv[1] & 0x60) >> 5; + + i = 1; + s = 0; + while(!(lv[i] & 0x80)) { + i++; /* octet 3a etc */ + if (in_len < i) + return 0; + speech_ver[s++] = lv[i] & 0x0f; + speech_ver[s] = -1; /* end of list */ + if (i == 2) /* octet 3a */ + *speech_ctm = (lv[i] & 0x20) >> 5; + if (s == 7) /* maximum speech versions + end of list */ + return 0; + } + + return 0; +} + +/* encode 'bearer capability' */ +static int encode_bearer_cap(int lv_only, struct msgb *msg, int transfer, + int mode, int coding, int radio, int speech_ctm, + int *speech_ver) +{ + u_int8_t lv[32 + 1]; + int i, s; + + lv[1] = transfer; + lv[1] |= mode << 3; + lv[1] |= coding << 4; + lv[1] |= radio << 5; + + i = 1; + for (s = 0; speech_ver[s] >= 0; s++) { + i++; /* octet 3a etc */ + lv[i] = speech_ver[s]; + if (i == 2) /* octet 3a */ + lv[i] |= speech_ctm << 5; + } + lv[i] |= 0x80; /* last IE of octet 3 etc */ + + lv[0] = i; + if (lv_only) + msgb_lv_put(msg, lv[0], lv+1); + else + msgb_tlv_put(msg, GSM48_IE_BEARER_CAP, lv[0], lv+1); + + return 0; +} + +/* decode 'call control cap' */ +static int decode_cccap(int *dtmf, int *pcp, const u_int8_t *lv) +{ + u_int8_t in_len = lv[0]; + + if (in_len < 1) + return -EINVAL; + + /* octet 3 */ + *dtmf = lv[1] & 0x01; + *pcp = (lv[1] & 0x02) >> 1; + + return 0; +} + +/* decode 'called party BCD number' */ +static int decode_called(int *type, int *plan, char *number, + int number_len, const u_int8_t *lv) +{ + u_int8_t in_len = lv[0]; + + if (in_len < 1) + return -EINVAL; + + /* octet 3 */ + *plan = lv[1] & 0x0f; + *type = (lv[1] & 0x70) >> 4; + + /* octet 4..N */ + decode_bcd_number(number, number_len, lv, 1); + + return 0; +} + +/* encode 'called party BCD number' */ +static int encode_called(struct msgb *msg, int type, int plan, char *number) +{ + u_int8_t lv[18]; + int ret; + + /* octet 3 */ + lv[1] = plan; + lv[1] |= type << 4; + + /* octet 4..N, octet 2 */ + ret = encode_bcd_number(lv, sizeof(lv), 1, number); + if (ret < 0) + return ret; + + msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, lv[0], lv+1); + + return 0; +} + +/* encode callerid of various IEs */ +static int encode_callerid(struct msgb *msg, int ie, int type, int plan, + int present, int screen, char *number) +{ + u_int8_t lv[13]; + int h_len = 1; + int ret; + + /* octet 3 */ + lv[1] = plan; + lv[1] |= type << 4; + + if (present || screen) { + /* octet 3a */ + lv[2] = screen; + lv[2] |= present << 5; + lv[2] |= 0x80; + h_len++; + } else + lv[1] |= 0x80; + + /* octet 4..N, octet 2 */ + ret = encode_bcd_number(lv, sizeof(lv), h_len, number); + if (ret < 0) + return ret; + + msgb_tlv_put(msg, ie, lv[0], lv+1); + + return 0; +} + +/* decode 'cause' */ +static int decode_cause(int *location, int *coding, int *rec, int *rec_val, + int *value, u_char *diag, u_int *diag_len, + const u_int8_t *lv) +{ + u_int8_t in_len = lv[0]; + int i; + + if (in_len < 2) + return -EINVAL; + + *diag_len = 0; + + /* octet 3 */ + *location = lv[1] & 0x0f; + *coding = (lv[1] & 0x60) >> 5; + + i = 1; + if (!(lv[i] & 0x80)) { + i++; /* octet 3a */ + if (in_len < i+1) + return 0; + *rec = 1; + *rec_val = lv[i] & 0x7f; + + } + i++; + + /* octet 4 */ + *value = lv[i] & 0x7f; + i++; + + if (in_len < i) /* no diag */ + return 0; + + if (in_len - (i-1) > 32) /* maximum 32 octets */ + return 0; + + /* octet 5-N */ + memcpy(diag, lv + i, in_len - (i-1)); + *diag_len = in_len - (i-1); + + return 0; +} + +/* encode 'cause' */ +static int encode_cause(int lv_only, struct msgb *msg, int location, + int coding, int rec, int rec_val, int value, + u_char *diag, u_int diag_len) +{ + u_int8_t lv[32+4]; + int i; + + if (diag_len > 32) + return -EINVAL; + + /* octet 3 */ + lv[1] = location; + lv[1] |= coding << 5; + + i = 1; + if (rec) { + i++; /* octet 3a */ + lv[i] = rec_val; + } + lv[i] |= 0x80; /* end of octet 3 */ + + /* octet 4 */ + i++; + lv[i] = 0x80 | value; + + /* octet 5-N */ + if (diag_len) { + memcpy(lv + i, diag, diag_len); + i += diag_len; + } + + lv[0] = i; + if (lv_only) + msgb_lv_put(msg, lv[0], lv+1); + else + msgb_tlv_put(msg, GSM48_IE_CAUSE, lv[0], lv+1); + + return 0; +} + +/* encode 'calling number' */ +static int encode_calling(struct msgb *msg, int type, int plan, int present, + int screen, char *number) +{ + return encode_callerid(msg, GSM48_IE_CALLING_BCD, type, plan, + present, screen, number); +} + +/* encode 'connected number' */ +static int encode_connected(struct msgb *msg, int type, int plan, int present, + int screen, char *number) +{ + return encode_callerid(msg, GSM48_IE_CONN_BCD, type, plan, + present, screen, number); +} + +/* encode 'redirecting number' */ +static int encode_redirecting(struct msgb *msg, int type, int plan, int present, + int screen, char *number) +{ + return encode_callerid(msg, GSM48_IE_REDIR_BCD, type, plan, + present, screen, number); +} + +/* decode 'facility' */ +static int decode_facility(char *info, int info_len, int *len, + const u_int8_t *lv) +{ + u_int8_t in_len = lv[0]; + + if (in_len < 1) + return -EINVAL; + + if (in_len > info_len) + return -EINVAL; + + memcpy(info, lv+1, in_len); + *len = in_len; + + return 0; +} + +/* encode 'facility' */ +static int encode_facility(int lv_only, struct msgb *msg, char *info, int len) +{ + u_int8_t lv[GSM_MAX_FACILITY + 1]; + + if (len < 1 || len > GSM_MAX_FACILITY) + return -EINVAL; + + memcpy(lv+1, info, len); + lv[0] = len; + if (lv_only) + msgb_lv_put(msg, lv[0], lv+1); + else + msgb_tlv_put(msg, GSM48_IE_FACILITY, lv[0], lv+1); + + return 0; +} + +/* decode 'notify' */ +static int decode_notify(int *notify, const u_int8_t *v) +{ + *notify = v[0] & 0x7f; + + return 0; +} + +/* encode 'notify' */ +static int encode_notify(struct msgb *msg, int notify) +{ + msgb_v_put(msg, notify | 0x80); + + return 0; +} + +/* encode 'signal' */ +static int encode_signal(struct msgb *msg, int signal) +{ + msgb_tv_put(msg, GSM48_IE_SIGNAL, signal); + + return 0; +} + +/* decode 'keypad' */ +static int decode_keypad(int *keypad, const u_int8_t *lv) +{ + u_int8_t in_len = lv[0]; + + if (in_len < 1) + return -EINVAL; + + *keypad = lv[1] & 0x7f; + + return 0; +} + +/* encode 'keypad' */ +static int encode_keypad(struct msgb *msg, int keypad) +{ + msgb_tv_put(msg, GSM48_IE_KPD_FACILITY, keypad); + + return 0; +} + +/* decode 'progress' */ +static int decode_progress(int *coding, int *location, int *descr, + const u_int8_t *lv) +{ + u_int8_t in_len = lv[0]; + + if (in_len < 2) + return -EINVAL; + + *coding = (lv[1] & 0x60) >> 5; + *location = lv[1] & 0x0f; + *descr = lv[2] & 0x7f; + + return 0; +} + +/* encode 'progress' */ +static int encode_progress(int lv_only, struct msgb *msg, int coding, + int location, int descr) +{ + u_int8_t lv[3]; + + lv[0] = 2; + lv[1] = 0x80 | ((coding & 0x3) << 5) | (location & 0xf); + lv[2] = 0x80 | (descr & 0x7f); + if (lv_only) + msgb_lv_put(msg, lv[0], lv+1); + else + msgb_tlv_put(msg, GSM48_IE_PROGR_IND, lv[0], lv+1); + + return 0; +} + +/* decode 'user-user' */ +static int decode_useruser(int *proto, char *info, int info_len, + const u_int8_t *lv) +{ + u_int8_t in_len = lv[0]; + int i; + + if (in_len < 1) + return -EINVAL; + + *proto = lv[1]; + + for (i = 2; i <= in_len; i++) { + info_len--; + if (info_len <= 1) + break; + *info++ = lv[i]; + } + if (info_len >= 1) + *info++ = '\0'; + + return 0; +} + +/* encode 'useruser' */ +static int encode_useruser(int lv_only, struct msgb *msg, int proto, char *info) +{ + u_int8_t lv[GSM_MAX_USERUSER + 2]; + + if (strlen(info) > GSM_MAX_USERUSER) + return -EINVAL; + + lv[0] = 1 + strlen(info); + lv[1] = proto; + memcpy(lv + 2, info, strlen(info)); + if (lv_only) + msgb_lv_put(msg, lv[0], lv+1); + else + msgb_tlv_put(msg, GSM48_IE_USER_USER, lv[0], lv+1); + + return 0; +} + +/* decode 'ss version' */ +static int decode_ssversion(char *info, int info_len, int *len, + const u_int8_t *lv) +{ + u_int8_t in_len = lv[0]; + + if (in_len < 1 || in_len < info_len) + return -EINVAL; + + memcpy(info, lv + 1, in_len); + *len = in_len; + + return 0; +} + +/* encode 'more data' */ +static int encode_more(struct msgb *msg) +{ + u_int8_t *ie; + + ie = msgb_put(msg, 1); + ie[0] = GSM48_IE_MORE_DATA; + + return 0; +} + struct msgb *gsm48_msgb_alloc(void) { return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM); @@ -1313,7 +1757,6 @@ static int gsm48_cc_rx_setup(struct msgb *msg) struct gsm_bts *bts; char called_number[(43-2)*2 + 1] = "\0"; struct tlv_parsed tp; - u_int8_t num_type; int ret; if (call->state == GSM_CSTATE_NULL || @@ -1330,8 +1773,8 @@ static int gsm48_cc_rx_setup(struct msgb *msg) goto err; /* Parse the number that was dialed and lookup subscriber */ - num_type = decode_bcd_number(called_number, sizeof(called_number), - TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); + decode_bcd_number(called_number, sizeof(called_number), + TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1, 1); DEBUGP(DCC, "A -> SETUP(tid=0x%02x,number='%s')\n", call->transaction_id, called_number); @@ -1512,13 +1955,15 @@ int gsm48_cc_tx_setup(struct gsm_lchan *lchan, msgb_tv_put(msg, GSM48_IE_SIGNAL, GSM48_SIGNAL_DIALTONE); if (calling_subscr) { - encode_bcd_number(bcd_lv, sizeof(bcd_lv), 0xb9, + bcd_lv[1] = 0xb9; + encode_bcd_number(bcd_lv, sizeof(bcd_lv), 1, calling_subscr->extension); msgb_tlv_put(msg, GSM48_IE_CALLING_BCD, bcd_lv[0], bcd_lv+1); } if (lchan->subscr) { - encode_bcd_number(bcd_lv, sizeof(bcd_lv), 0xb9, + bcd_lv[1] = 0xb9; + encode_bcd_number(bcd_lv, sizeof(bcd_lv), 1, lchan->subscr->extension); msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, bcd_lv[0], bcd_lv+1); diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c index c29953806..a6f3e7aac 100644 --- a/openbsc/src/gsm_04_11.c +++ b/openbsc/src/gsm_04_11.c @@ -190,7 +190,7 @@ static int gsm340_rx_tpdu(struct msgb *msg) /* mangle first byte to reflect length in bytes, not digits */ address_lv[0] = da_len_bytes; /* convert to real number */ - decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv); + decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv, 1); smsp += da_len_bytes; |