diff options
Diffstat (limited to 'openbsc/src')
70 files changed, 6259 insertions, 6370 deletions
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am index 615884a38..2c695993d 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -1,23 +1,29 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \ - isdnsync bsc_mgcp bsc_msc_ip bsc_nat -noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a + isdnsync bsc_mgcp ipaccess-proxy \ + bsc_msc_ip bsc_nat +noinst_LIBRARIES = libbsc.a libmsc.a libvty.a noinst_HEADERS = vty/cardshell.h +bscdir = $(libdir) +bsc_LIBRARIES = libsccp.a + libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \ - msgb.c select.c chan_alloc.c timer.c debug.c \ + chan_alloc.c debug.c \ gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.c \ - trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \ - input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \ - talloc_ctx.c system_information.c bitvec.c rest_octets.c \ - rtp_proxy.c telnet_interface.c + trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c \ + input/misdn.c input/ipaccess.c \ + talloc_ctx.c system_information.c rest_octets.c \ + rtp_proxy.c bts_siemens_bs11.c bts_ipaccess_nanobts.c \ + bts_unknown.c telnet_interface.c meas_rep.c libmsc_a_SOURCES = gsm_subscriber.c db.c \ mncc.c gsm_04_08.c gsm_04_11.c transaction.c \ token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c \ - handover_logic.c handover_decision.c meas_rep.c + handover_logic.c handover_decision.c libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c @@ -26,22 +32,25 @@ libsccp_a_SOURCES = sccp/sccp.c bsc_hack_SOURCES = bsc_hack.c bsc_init.c vty_interface.c vty_interface_layer3.c bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libvty.a -ldl -ldbi $(LIBCRYPT) +bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c debug.c \ + rs232.c bts_siemens_bs11.c bsc_msc_ip_SOURCES = bssap.c bsc_msc_ip.c bsc_init.c vty_interface.c vty_interface_bsc.c \ bsc_msc.c bsc_msc_ip_LDADD = libbsc.a libvty.a libsccp.a -bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c msgb.c debug.c \ - select.c timer.c rs232.c tlv_parser.c signal.c talloc.c -ipaccess_find_SOURCES = ipaccess-find.c select.c timer.c +ipaccess_find_SOURCES = ipaccess/ipaccess-find.c -ipaccess_config_SOURCES = ipaccess-config.c +ipaccess_config_SOURCES = ipaccess/ipaccess-config.c ipaccess/ipaccess-firmware.c ipaccess_config_LDADD = libbsc.a libmsc.a libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT) isdnsync_SOURCES = isdnsync.c -bsc_mgcp_SOURCES = bsc_mgcp.c msgb.c talloc.c debug.c select.c timer.c telnet_interface.c +bsc_mgcp_SOURCES = mgcp/mgcp_main.c mgcp/mgcp_protocol.c mgcp/mgcp_network.c mgcp/mgcp_vty.c \ + debug.c telnet_interface.c bsc_mgcp_LDADD = libvty.a +ipaccess_proxy_SOURCES = ipaccess/ipaccess-proxy.c debug.c + bsc_nat_SOURCES = nat/bsc_nat.c nat/bsc_filter.c nat/bsc_nat_vty.c bsc_msc.c bssap.c bsc_nat_LDADD = libvty.a libbsc.a libsccp.a diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c index bb7248be7..99d8dd621 100644 --- a/openbsc/src/abis_nm.c +++ b/openbsc/src/abis_nm.c @@ -38,15 +38,16 @@ #include <openbsc/gsm_data.h> #include <openbsc/debug.h> -#include <openbsc/msgb.h> -#include <openbsc/tlv.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/talloc.h> #include <openbsc/abis_nm.h> #include <openbsc/misdn.h> #include <openbsc/signal.h> -#include <openbsc/talloc.h> #define OM_ALLOC_SIZE 1024 #define OM_HEADROOM_SIZE 128 +#define IPACC_SEGMENT_SIZE 245 /* unidirectional messages from BTS to BSC */ static const enum abis_nm_msgtype reports[] = { @@ -263,7 +264,7 @@ static const enum abis_nm_attr nm_att_settable[] = { NM_ATT_MEAS_TYPE, }; -static const struct tlv_definition nm_att_tlvdef = { +const struct tlv_definition nm_att_tlvdef = { .def = { [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 }, [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V }, @@ -329,77 +330,6 @@ static const struct tlv_definition nm_att_tlvdef = { [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V }, [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV }, [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V }, - /* BS11 specifics */ - [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV }, - [0xd5] = { TLV_TYPE_TLV }, - [0xa8] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV }, - [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV }, - [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV }, - [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV }, - /* ip.access specifics */ - [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, - [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, - [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_TV, }, - [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, - [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, - [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, - [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, - [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, - //[0x95] = { TLV_TYPE_FIXED, 2 }, - [0x85] = { TLV_TYPE_TV }, - }, }; @@ -422,9 +352,11 @@ int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan) return -EINVAL; } -int abis_nm_tlv_parse(struct tlv_parsed *tp, const u_int8_t *buf, int len) +int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const u_int8_t *buf, int len) { - return tlv_parse(tp, &nm_att_tlvdef, buf, len, 0, 0); + if (!bts->model) + return -EIO; + return tlv_parse(tp, &bts->model->nm_att_tlvdef, buf, len, 0, 0); } static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size) @@ -540,11 +472,11 @@ static const char *obj_class_name(u_int8_t oc) const char *nm_opstate_name(u_int8_t os) { switch (os) { - case 1: + case NM_OPSTATE_DISABLED: return "Disabled"; - case 2: + case NM_OPSTATE_ENABLED: return "Enabled"; - case 0xff: + case NM_OPSTATE_NULL: return "NULL"; default: return "RFU"; @@ -598,6 +530,13 @@ const char *nm_adm_name(u_int8_t adm) } } +int nm_is_running(struct gsm_nm_state *s) { + return (s->operational == NM_OPSTATE_ENABLED) && ( + (s->availability == NM_AVSTATE_OK) || + (s->availability == 0xff) + ); +} + static void debugp_foh(struct abis_om_fom_hdr *foh) { DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ", @@ -793,7 +732,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb) new_state = *nm_state; - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); + abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) { new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE); DEBUGPC(DNM, "OP_STATE=%s ", nm_opstate_name(new_state.operational)); @@ -805,19 +744,25 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb) new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS); DEBUGPC(DNM, "AVAIL=%s(%02x) ", nm_avail_name(new_state.availability), new_state.availability); - } + } else + new_state.availability = 0xff; if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); DEBUGPC(DNM, "ADM=%2s ", nm_adm_name(new_state.administrative)); } DEBUGPC(DNM, "\n"); - if (memcmp(&new_state, nm_state, sizeof(new_state))) { + if ((new_state.administrative != 0 && nm_state->administrative == 0) || + new_state.operational != nm_state->operational || + new_state.availability != nm_state->availability) { /* Update the operational state of a given object in our in-memory data * structures and send an event to the higher layer */ void *obj = objclass2obj(bts, foh->obj_class, &foh->obj_inst); rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state); - *nm_state = new_state; + nm_state->operational = new_state.operational; + nm_state->availability = new_state.availability; + if (nm_state->administrative == 0) + nm_state->administrative = new_state.administrative; } #if 0 if (op_state == 1) { @@ -839,7 +784,7 @@ static int rx_fail_evt_rep(struct msgb *mb) DEBUGPC(DNM, "Failure Event Report "); - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE)) DEBUGPC(DNM, "Type=%s ", event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE))); @@ -933,7 +878,7 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb) if (nack) return ret; - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG); sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG); if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) { @@ -967,7 +912,7 @@ static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb) struct tlv_parsed tp; u_int8_t adm_state; - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) return -EINVAL; @@ -983,7 +928,7 @@ static int abis_nm_rx_lmt_event(struct msgb *mb) struct tlv_parsed tp; DEBUGP(DNM, "LMT Event "); - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) && TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) { u_int8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION); @@ -1029,7 +974,7 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb) else DEBUGPC(DNM, "NACK 0x%02x ", mt); - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) DEBUGPC(DNM, "CAUSE=%s\n", nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); @@ -1043,13 +988,11 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb) /* check if last message is to be acked */ if (is_ack_nack(nmh->last_msgtype)) { if (mt == MT_ACK(nmh->last_msgtype)) { - fprintf(stderr, "received ACK (0x%x)\n", - foh->msg_type); + DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type); /* we got our ACK, continue sending the next msg */ } else if (mt == MT_NACK(nmh->last_msgtype)) { /* we got a NACK, signal this to the caller */ - fprintf(stderr, "received NACK (0x%x)\n", - foh->msg_type); + DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type); /* FIXME: somehow signal this to the caller */ } else { /* really strange things happen */ @@ -1071,6 +1014,12 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb) case NM_MT_CONN_MDROP_LINK_ACK: DEBUGP(DNM, "CONN MDROP LINK ACK\n"); break; + case NM_MT_IPACC_RESTART_ACK: + dispatch_signal(SS_NM, S_NM_IPACC_RESTART_ACK, NULL); + break; + case NM_MT_IPACC_RESTART_NACK: + dispatch_signal(SS_NM, S_NM_IPACC_RESTART_NACK, NULL); + break; } return 0; @@ -1216,6 +1165,22 @@ struct abis_nm_sw { static struct abis_nm_sw g_sw; +static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg) +{ + if (sw->bts->type == GSM_BTS_TYPE_NANOBTS) { + msgb_v_put(msg, NM_ATT_SW_DESCR); + msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); + msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, + sw->file_version); + } else if (sw->bts->type == GSM_BTS_TYPE_BS11) { + msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); + msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, + sw->file_version); + } else { + LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n"); + } +} + /* 6.2.1 / 8.3.1: Load Data Initiate */ static int sw_load_init(struct abis_nm_sw *sw) { @@ -1227,11 +1192,8 @@ static int sw_load_init(struct abis_nm_sw *sw) fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class, sw->obj_instance[0], sw->obj_instance[1], sw->obj_instance[2]); - - /* FIXME: this is BS11 specific format */ - msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); + + sw_add_file_id_and_ver(sw, msg); msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size); return abis_nm_sendmsg(sw->bts, msg); @@ -1289,7 +1251,24 @@ static int sw_load_segment(struct abis_nm_sw *sw) /* we only now know the exact length for the OM hdr */ len = strlen(line_buf)+2; break; + case GSM_BTS_TYPE_NANOBTS: { + static_assert(sizeof(seg_buf) >= IPACC_SEGMENT_SIZE, buffer_big_enough); + len = read(sw->fd, &seg_buf, IPACC_SEGMENT_SIZE); + if (len < 0) { + perror("read failed"); + return -EINVAL; + } + + if (len != IPACC_SEGMENT_SIZE) + sw->last_seg = 1; + + ++sw->seg_in_window; + msgb_tl16v_put(msg, NM_ATT_IPACC_FILE_DATA, len, (const u_int8_t *) seg_buf); + len += 3; + break; + } default: + LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n"); /* FIXME: Other BTS types */ return -1; } @@ -1313,11 +1292,7 @@ static int sw_load_end(struct abis_nm_sw *sw) sw->obj_instance[0], sw->obj_instance[1], sw->obj_instance[2]); - /* FIXME: this is BS11 specific format */ - msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - + sw_add_file_id_and_ver(sw, msg); return abis_nm_sendmsg(sw->bts, msg); } @@ -1341,6 +1316,59 @@ static int sw_activate(struct abis_nm_sw *sw) return abis_nm_sendmsg(sw->bts, msg); } +struct sdp_firmware { + char magic[4]; + char more_magic[4]; + unsigned int header_length; + unsigned int file_length; +} __attribute__ ((packed)); + +static int parse_sdp_header(struct abis_nm_sw *sw) +{ + struct sdp_firmware firmware_header; + int rc; + struct stat stat; + + rc = read(sw->fd, &firmware_header, sizeof(firmware_header)); + if (rc != sizeof(firmware_header)) { + LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n"); + return -1; + } + + if (strncmp(firmware_header.magic, " SDP", 4) != 0) { + LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n"); + return -1; + } + + if (firmware_header.more_magic[0] != 0x10 || + firmware_header.more_magic[1] != 0x02 || + firmware_header.more_magic[2] != 0x00 || + firmware_header.more_magic[3] != 0x00) { + LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n"); + return -1; + } + + + if (fstat(sw->fd, &stat) == -1) { + LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n"); + return -1; + } + + if (ntohl(firmware_header.file_length) != stat.st_size) { + LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n"); + return -1; + } + + /* go back to the start as we checked the whole filesize.. */ + lseek(sw->fd, 0l, SEEK_SET); + LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood.\n" + "There might be checksums in the file that are not\n" + "verified and incomplete firmware might be flashed.\n" + "There is absolutely no WARRANTY that flashing will\n" + "work.\n"); + return 0; +} + static int sw_open_file(struct abis_nm_sw *sw, const char *fname) { char file_id[12+1]; @@ -1372,6 +1400,19 @@ static int sw_open_file(struct abis_nm_sw *sw, const char *fname) /* rewind to start of file */ rewind(sw->stream); break; + case GSM_BTS_TYPE_NANOBTS: + /* TODO: extract that from the filename or content */ + rc = parse_sdp_header(sw); + if (rc < 0) { + fprintf(stderr, "Could not parse the ipaccess SDP header\n"); + return -1; + } + + strcpy((char *)sw->file_id, "id"); + sw->file_id_len = 3; + strcpy((char *)sw->file_version, "version"); + sw->file_version_len = 8; + break; default: /* We don't know how to treat them yet */ close(sw->fd); @@ -1470,6 +1511,12 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb) rc = sw_load_end(sw); } break; + case NM_MT_LOAD_ABORT: + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_ABORT, mb, + sw->cb_data, NULL); + break; } break; case SW_STATE_WAIT_ENDACK: @@ -1483,6 +1530,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_END_ACK, mb, sw->cb_data, NULL); + rc = 0; break; case NM_MT_LOAD_END_NACK: if (sw->forced) { @@ -1559,10 +1607,26 @@ int abis_nm_software_load(struct gsm_bts *bts, const char *fname, return -EBUSY; sw->bts = bts; - sw->obj_class = NM_OC_SITE_MANAGER; - sw->obj_instance[0] = 0xff; - sw->obj_instance[1] = 0xff; - sw->obj_instance[2] = 0xff; + + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + sw->obj_class = NM_OC_SITE_MANAGER; + sw->obj_instance[0] = 0xff; + sw->obj_instance[1] = 0xff; + sw->obj_instance[2] = 0xff; + break; + case GSM_BTS_TYPE_NANOBTS: + sw->obj_class = NM_OC_BASEB_TRANSC; + sw->obj_instance[0] = 0x00; + sw->obj_instance[1] = 0x00; + sw->obj_instance[2] = 0xff; + break; + case GSM_BTS_TYPE_UNKNOWN: + default: + LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n"); + return -1; + break; + } sw->window_size = win_size; sw->state = SW_STATE_WAIT_INITACK; sw->cbfn = cbfn; @@ -1590,7 +1654,10 @@ int abis_nm_software_load_status(struct gsm_bts *bts) return rc; } - percent = (ftell(sw->stream) * 100) / st.st_size; + if (sw->stream) + percent = (ftell(sw->stream) * 100) / st.st_size; + else + percent = (lseek(sw->fd, 0, SEEK_CUR) * 100) / st.st_size; return percent; } @@ -2281,11 +2348,19 @@ int abis_nm_bs11_get_cclk(struct gsm_bts *bts) } //static const u_int8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 }; -static const u_int8_t bs11_logon_c8[] = { 0x02 }; -static const u_int8_t bs11_logon_c9[] = "FACTORY"; int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on) { + return abis_nm_bs11_logon(bts, 0x02, "FACTORY", on); +} + +int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on) +{ + return abis_nm_bs11_logon(bts, 0x03, "FIELD ", on); +} + +int abis_nm_bs11_logon(struct gsm_bts *bts, u_int8_t level, const char *name, int on) +{ struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); struct bs11_date_time bdt; @@ -2295,15 +2370,15 @@ int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on) oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); if (on) { u_int8_t len = 3*2 + sizeof(bdt) - + sizeof(bs11_logon_c8) + sizeof(bs11_logon_c9); + + 1 + strlen(name); fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON, NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME, sizeof(bdt), (u_int8_t *) &bdt); msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV, - sizeof(bs11_logon_c8), bs11_logon_c8); + 1, &level); msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME, - sizeof(bs11_logon_c9), bs11_logon_c9); + strlen(name), (u_int8_t *)name); } else { fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF, NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); @@ -2351,6 +2426,27 @@ int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked) return abis_nm_sendmsg(bts, msg); } +/* Set the calibration value of the PLL (work value/set value) + * It depends on the login which one is changed */ +int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value) +{ + struct abis_om_hdr *oh; + struct msgb *msg; + u_int8_t tlv_value[2]; + + msg = nm_msgb_alloc(); + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, + BS11_OBJ_TRX1, 0x00, 0x00); + + tlv_value[0] = value>>8; + tlv_value[1] = value&0xff; + + msgb_tlv_put(msg, NM_ATT_BS11_PLL, 2, tlv_value); + + return abis_nm_sendmsg(bts, msg); +} + int abis_nm_bs11_get_state(struct gsm_bts *bts) { return __simple_cmd(bts, NM_MT_BS11_GET_STATE); @@ -2597,14 +2693,15 @@ static int abis_nm_rx_ipacc(struct msgb *msg) struct abis_om_fom_hdr *foh; u_int8_t idstrlen = oh->data[0]; struct tlv_parsed tp; + struct ipacc_ack_signal_data signal; if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { - DEBUGP(DNM, "id string is not com.ipaccess !?!\n"); + LOGP(DNM, LOGL_ERROR, "id string is not com.ipaccess !?!\n"); return -EINVAL; } foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen); - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); + abis_nm_tlv_parse(&tp, msg->trx->bts, foh->data, oh->length-sizeof(*foh)); debugp_foh(foh); @@ -2627,7 +2724,7 @@ static int abis_nm_rx_ipacc(struct msgb *msg) DEBUGPC(DNM, "\n"); break; case NM_MT_IPACC_RSL_CONNECT_NACK: - DEBUGPC(DNM, "RSL CONNECT NACK "); + LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) DEBUGPC(DNM, " CAUSE=%s\n", nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); @@ -2639,35 +2736,35 @@ static int abis_nm_rx_ipacc(struct msgb *msg) /* FIXME: decode and show the actual attributes */ break; case NM_MT_IPACC_SET_NVATTR_NACK: - DEBUGPC(DNM, "SET NVATTR NACK "); + LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - DEBUGPC(DNM, " CAUSE=%s\n", + LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else - DEBUGPC(DNM, "\n"); + LOGPC(DNM, LOGL_ERROR, "\n"); break; case NM_MT_IPACC_GET_NVATTR_ACK: DEBUGPC(DNM, "GET NVATTR ACK\n"); /* FIXME: decode and show the actual attributes */ break; case NM_MT_IPACC_GET_NVATTR_NACK: - DEBUGPC(DNM, "GET NVATTR NACK "); + LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - DEBUGPC(DNM, " CAUSE=%s\n", + LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else - DEBUGPC(DNM, "\n"); + LOGPC(DNM, LOGL_ERROR, "\n"); break; case NM_MT_IPACC_SET_ATTR_ACK: DEBUGPC(DNM, "SET ATTR ACK\n"); break; case NM_MT_IPACC_SET_ATTR_NACK: - DEBUGPC(DNM, "SET ATTR NACK "); + LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - DEBUGPC(DNM, " CAUSE=%s\n", + LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else - DEBUGPC(DNM, "\n"); + LOGPC(DNM, LOGL_ERROR, "\n"); break; default: DEBUGPC(DNM, "unknown\n"); @@ -2679,7 +2776,14 @@ static int abis_nm_rx_ipacc(struct msgb *msg) case NM_MT_IPACC_RSL_CONNECT_NACK: case NM_MT_IPACC_SET_NVATTR_NACK: case NM_MT_IPACC_GET_NVATTR_NACK: - dispatch_signal(SS_NM, S_NM_IPACC_NACK, &foh->msg_type); + signal.bts = msg->trx->bts; + signal.msg_type = foh->msg_type; + dispatch_signal(SS_NM, S_NM_IPACC_NACK, &signal); + break; + case NM_MT_IPACC_SET_NVATTR_ACK: + signal.bts = msg->trx->bts; + signal.msg_type = foh->msg_type; + dispatch_signal(SS_NM, S_NM_IPACC_ACK, &signal); break; default: break; @@ -2726,11 +2830,11 @@ int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type, } /* set some attributes in NVRAM */ -int abis_nm_ipaccess_set_nvattr(struct gsm_bts *bts, u_int8_t *attr, +int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, u_int8_t *attr, int attr_len) { - return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_NVATTR, - NM_OC_BASEB_TRANSC, 0, 0, 0xff, attr, + return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR, + NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr, attr_len); } @@ -2781,7 +2885,7 @@ void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked) { int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED; - trx->rf_locked = locked; + trx->nm_state.administrative = new_state; if (!trx->bts || !trx->bts->oml_link) return; diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c index e2fadf00c..3079d0d54 100644 --- a/openbsc/src/abis_rsl.c +++ b/openbsc/src/abis_rsl.c @@ -1,7 +1,7 @@ /* GSM Radio Signalling Link messages on the A-bis interface * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> * * All Rights Reserved * @@ -30,104 +30,23 @@ #include <openbsc/gsm_data.h> #include <openbsc/gsm_04_08.h> -#include <openbsc/gsm_utils.h> +#include <osmocore/gsm_utils.h> #include <openbsc/abis_rsl.h> #include <openbsc/chan_alloc.h> #include <openbsc/bsc_rll.h> #include <openbsc/debug.h> -#include <openbsc/tlv.h> +#include <osmocore/tlv.h> #include <openbsc/paging.h> #include <openbsc/signal.h> #include <openbsc/meas_rep.h> #include <openbsc/rtp_proxy.h> +#include <osmocore/rsl.h> #define RSL_ALLOC_SIZE 1024 #define RSL_ALLOC_HEADROOM 128 #define MAX(a, b) (a) >= (b) ? (a) : (b) -static const struct tlv_definition rsl_att_tlvdef = { - .def = { - [RSL_IE_CHAN_NR] = { TLV_TYPE_TV }, - [RSL_IE_LINK_IDENT] = { TLV_TYPE_TV }, - [RSL_IE_ACT_TYPE] = { TLV_TYPE_TV }, - [RSL_IE_BS_POWER] = { TLV_TYPE_TV }, - [RSL_IE_CHAN_IDENT] = { TLV_TYPE_TLV }, - [RSL_IE_CHAN_MODE] = { TLV_TYPE_TLV }, - [RSL_IE_ENCR_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_FRAME_NUMBER] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_HANDO_REF] = { TLV_TYPE_TV }, - [RSL_IE_L1_INFO] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_L3_INFO] = { TLV_TYPE_TL16V }, - [RSL_IE_MS_IDENTITY] = { TLV_TYPE_TLV }, - [RSL_IE_MS_POWER] = { TLV_TYPE_TV }, - [RSL_IE_PAGING_GROUP] = { TLV_TYPE_TV }, - [RSL_IE_PAGING_LOAD] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_PYHS_CONTEXT] = { TLV_TYPE_TLV }, - [RSL_IE_ACCESS_DELAY] = { TLV_TYPE_TV }, - [RSL_IE_RACH_LOAD] = { TLV_TYPE_TLV }, - [RSL_IE_REQ_REFERENCE] = { TLV_TYPE_FIXED, 3 }, - [RSL_IE_RELEASE_MODE] = { TLV_TYPE_TV }, - [RSL_IE_RESOURCE_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_RLM_CAUSE] = { TLV_TYPE_TLV }, - [RSL_IE_STARTNG_TIME] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_TIMING_ADVANCE] = { TLV_TYPE_TV }, - [RSL_IE_UPLINK_MEAS] = { TLV_TYPE_TLV }, - [RSL_IE_CAUSE] = { TLV_TYPE_TLV }, - [RSL_IE_MEAS_RES_NR] = { TLV_TYPE_TV }, - [RSL_IE_MSG_ID] = { TLV_TYPE_TV }, - [RSL_IE_SYSINFO_TYPE] = { TLV_TYPE_TV }, - [RSL_IE_MS_POWER_PARAM] = { TLV_TYPE_TLV }, - [RSL_IE_BS_POWER_PARAM] = { TLV_TYPE_TLV }, - [RSL_IE_PREPROC_PARAM] = { TLV_TYPE_TLV }, - [RSL_IE_PREPROC_MEAS] = { TLV_TYPE_TLV }, - [RSL_IE_IMM_ASS_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_SMSCB_INFO] = { TLV_TYPE_FIXED, 23 }, - [RSL_IE_MS_TIMING_OFFSET] = { TLV_TYPE_TV }, - [RSL_IE_ERR_MSG] = { TLV_TYPE_TLV }, - [RSL_IE_FULL_BCCH_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_CHAN_NEEDED] = { TLV_TYPE_TV }, - [RSL_IE_CB_CMD_TYPE] = { TLV_TYPE_TV }, - [RSL_IE_SMSCB_MSG] = { TLV_TYPE_TLV }, - [RSL_IE_FULL_IMM_ASS_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_SACCH_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_CBCH_LOAD_INFO] = { TLV_TYPE_TV }, - [RSL_IE_SMSCB_CHAN_INDICATOR] = { TLV_TYPE_TV }, - [RSL_IE_GROUP_CALL_REF] = { TLV_TYPE_TLV }, - [RSL_IE_CHAN_DESC] = { TLV_TYPE_TLV }, - [RSL_IE_NCH_DRX_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_CMD_INDICATOR] = { TLV_TYPE_TLV }, - [RSL_IE_EMLPP_PRIO] = { TLV_TYPE_TV }, - [RSL_IE_UIC] = { TLV_TYPE_TLV }, - [RSL_IE_MAIN_CHAN_REF] = { TLV_TYPE_TV }, - [RSL_IE_MR_CONFIG] = { TLV_TYPE_TLV }, - [RSL_IE_MR_CONTROL] = { TLV_TYPE_TV }, - [RSL_IE_SUP_CODEC_TYPES] = { TLV_TYPE_TLV }, - [RSL_IE_CODEC_CONFIG] = { TLV_TYPE_TLV }, - [RSL_IE_RTD] = { TLV_TYPE_TV }, - [RSL_IE_TFO_STATUS] = { TLV_TYPE_TV }, - [RSL_IE_LLP_APDU] = { TLV_TYPE_TLV }, - [RSL_IE_SIEMENS_MRPCI] = { TLV_TYPE_TV }, - [RSL_IE_IPAC_PROXY_UDP] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_IPAC_BSCMPL_TOUT] = { TLV_TYPE_TV }, - [RSL_IE_IPAC_REMOTE_IP] = { TLV_TYPE_FIXED, 4 }, - [RSL_IE_IPAC_REMOTE_PORT] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_IPAC_RTP_PAYLOAD] = { TLV_TYPE_TV }, - [RSL_IE_IPAC_LOCAL_PORT] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_IPAC_SPEECH_MODE] = { TLV_TYPE_TV }, - [RSL_IE_IPAC_LOCAL_IP] = { TLV_TYPE_FIXED, 4 }, - [RSL_IE_IPAC_CONN_ID] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_IPAC_RTP_CSD_FMT] = { TLV_TYPE_TV }, - [RSL_IE_IPAC_RTP_JIT_BUF] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_IPAC_RTP_COMPR] = { TLV_TYPE_TV }, - [RSL_IE_IPAC_RTP_PAYLOAD2] = { TLV_TYPE_TV }, - [RSL_IE_IPAC_RTP_MPLEX] = { TLV_TYPE_FIXED, 8 }, - [RSL_IE_IPAC_RTP_MPLEX_ID] = { TLV_TYPE_TV }, - }, -}; -#define rsl_tlv_parse(dec, buf, len) \ - tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0) - static u_int8_t mdisc_by_msgtype(u_int8_t msg_type) { /* mask off the transparent bit ? */ @@ -155,44 +74,6 @@ static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh, dh->ie_chan = RSL_IE_CHAN_NR; } -static inline void init_llm_hdr(struct abis_rsl_rll_hdr *dh, - u_int8_t msg_type) -{ - /* dh->c.msg_discr = mdisc_by_msgtype(msg_type); */ - dh->c.msg_discr = ABIS_RSL_MDISC_RLL; - dh->c.msg_type = msg_type; - dh->ie_chan = RSL_IE_CHAN_NR; - dh->ie_link_id = RSL_IE_LINK_IDENT; -} - - -/* encode channel number as per Section 9.3.1 */ -u_int8_t rsl_enc_chan_nr(u_int8_t type, u_int8_t subch, u_int8_t timeslot) -{ - u_int8_t ret; - - ret = (timeslot & 0x07) | type; - - switch (type) { - case RSL_CHAN_Lm_ACCHs: - subch &= 0x01; - break; - case RSL_CHAN_SDCCH4_ACCH: - subch &= 0x07; - break; - case RSL_CHAN_SDCCH8_ACCH: - subch &= 0x07; - break; - default: - /* no subchannels allowed */ - subch = 0x00; - break; - } - ret |= (subch << 3); - - return ret; -} - /* determine logical channel based on TRX and channel number IE */ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr) { @@ -237,6 +118,8 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr) } lchan = &ts->lchan[lch_idx]; + debug_set_context(BSC_CTX_LCHAN, lchan); + debug_set_context(BSC_CTX_SUBSCR, lchan->subscr); return lchan; } @@ -333,57 +216,14 @@ static int build_encr_info(u_int8_t *out, struct gsm_lchan *lchan) return lchan->encr.key_len + 1; } - -static const char *rsl_err_vals[0xff] = { - [RSL_ERR_RADIO_IF_FAIL] = "Radio Interface Failure", - [RSL_ERR_RADIO_LINK_FAIL] = "Radio Link Failure", - [RSL_ERR_HANDOVER_ACC_FAIL] = "Handover Access Failure", - [RSL_ERR_TALKER_ACC_FAIL] = "Talker Access Failure", - [RSL_ERR_OM_INTERVENTION] = "O&M Intervention", - [RSL_ERR_NORMAL_UNSPEC] = "Normal event, unspecified", - [RSL_ERR_T_MSRFPCI_EXP] = "Siemens: T_MSRFPCI Expired", - [RSL_ERR_EQUIPMENT_FAIL] = "Equipment Failure", - [RSL_ERR_RR_UNAVAIL] = "Radio Resource not available", - [RSL_ERR_TERR_CH_FAIL] = "Terrestrial Channel Failure", - [RSL_ERR_CCCH_OVERLOAD] = "CCCH Overload", - [RSL_ERR_ACCH_OVERLOAD] = "ACCH Overload", - [RSL_ERR_PROCESSOR_OVERLOAD] = "Processor Overload", - [RSL_ERR_RES_UNAVAIL] = "Resource not available, unspecified", - [RSL_ERR_TRANSC_UNAVAIL] = "Transcoding not available", - [RSL_ERR_SERV_OPT_UNAVAIL] = "Service or Option not available", - [RSL_ERR_ENCR_UNIMPL] = "Encryption algorithm not implemented", - [RSL_ERR_SERV_OPT_UNIMPL] = "Service or Option not implemented", - [RSL_ERR_RCH_ALR_ACTV_ALLOC] = "Radio channel already activated", - [RSL_ERR_INVALID_MESSAGE] = "Invalid Message, unspecified", - [RSL_ERR_MSG_DISCR] = "Message Discriminator Error", - [RSL_ERR_MSG_TYPE] = "Message Type Error", - [RSL_ERR_MSG_SEQ] = "Message Sequence Error", - [RSL_ERR_IE_ERROR] = "General IE error", - [RSL_ERR_MAND_IE_ERROR] = "Mandatory IE error", - [RSL_ERR_OPT_IE_ERROR] = "Optional IE error", - [RSL_ERR_IE_NONEXIST] = "IE non-existent", - [RSL_ERR_IE_LENGTH] = "IE length error", - [RSL_ERR_IE_CONTENT] = "IE content error", - [RSL_ERR_PROTO] = "Protocol error, unspecified", - [RSL_ERR_INTERWORKING] = "Interworking error, unspecified", -}; - -static const char *rsl_err_name(u_int8_t err) -{ - if (rsl_err_vals[err]) - return rsl_err_vals[err]; - else - return "unknown"; -} - -static void print_rsl_cause(const u_int8_t *cause_v, u_int8_t cause_len) +static void print_rsl_cause(int lvl, const u_int8_t *cause_v, u_int8_t cause_len) { int i; - DEBUGPC(DRSL, "CAUSE=0x%02x(%s) ", + LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ", cause_v[0], rsl_err_name(cause_v[0])); for (i = 1; i < cause_len-1; i++) - DEBUGPC(DRSL, "%02x ", cause_v[i]); + LOGPC(DRSL, lvl, "%02x ", cause_v[i]); } /* Send a BCCH_INFO message as per Chapter 8.5.1 */ @@ -723,8 +563,7 @@ int rsl_deact_sacch(struct gsm_lchan *lchan) msg->lchan = lchan; msg->trx = lchan->ts->trx; - DEBUGP(DRSL, "DEACTivate SACCH CMD channel=%s chan_nr=0x%02x\n", - gsm_ts_name(lchan->ts), dh->chan_nr); + DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan)); return abis_rsl_sendmsg(msg); } @@ -742,8 +581,7 @@ int rsl_rf_chan_release(struct gsm_lchan *lchan) msg->lchan = lchan; msg->trx = lchan->ts->trx; - DEBUGP(DRSL, "RF Channel Release CMD channel=%s chan_nr=0x%02x\n", - gsm_ts_name(lchan->ts), dh->chan_nr); + DEBUGP(DRSL, "%s RF Channel Release CMD\n", gsm_lchan_name(lchan)); /* BTS will respond by RF CHAN REL ACK */ return abis_rsl_sendmsg(msg); @@ -836,8 +674,8 @@ int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci) dh->chan_nr = lchan2chan_nr(lchan); msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(u_int8_t *)mrpci); - DEBUGP(DRSL, "channel=%s chan_nr=0x%02x TX Siemens MRPCI 0x%02x\n", - gsm_ts_name(lchan->ts), dh->chan_nr, *(u_int8_t *)mrpci); + DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n", + gsm_lchan_name(lchan), *(u_int8_t *)mrpci); msg->trx = lchan->ts->trx; @@ -849,27 +687,13 @@ int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci) /* Chapter 8.3.1 */ int rsl_data_request(struct msgb *msg, u_int8_t link_id) { - u_int8_t l3_len = msg->tail - (u_int8_t *)msgb_l3(msg); - struct abis_rsl_rll_hdr *rh; - if (msg->lchan == NULL) { LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n"); return -EINVAL; } - if (msg->lchan->use_count <= 0) { - DEBUGP(DRSL, "BUG: Trying to send data on unused lchan\n"); - } - - /* First push the L3 IE tag and length */ - msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); - - /* Then push the RSL header */ - rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh)); - init_llm_hdr(rh, RSL_MT_DATA_REQ); - rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP; - rh->chan_nr = lchan2chan_nr(msg->lchan); - rh->link_id = link_id; + rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, lchan2chan_nr(msg->lchan), + link_id, 1); msg->trx = msg->lchan->ts->trx; @@ -880,15 +704,10 @@ int rsl_data_request(struct msgb *msg, u_int8_t link_id) /* Chapter 8.3.1 */ int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id) { - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_rll_hdr *rh; - - rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh)); - init_llm_hdr(rh, RSL_MT_EST_REQ); - //rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP; - rh->chan_nr = lchan2chan_nr(lchan); - rh->link_id = link_id; + struct msgb *msg; + msg = rsl_rll_simple(RSL_MT_EST_REQ, lchan2chan_nr(lchan), + link_id, 0); msg->trx = lchan->ts->trx; return abis_rsl_sendmsg(msg); @@ -901,16 +720,16 @@ int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id) lchan_free() */ int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id) { - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_rll_hdr *rh; - rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh)); - init_llm_hdr(rh, RSL_MT_REL_REQ); - //rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP; - rh->chan_nr = lchan2chan_nr(lchan); - rh->link_id = link_id; + struct msgb *msg; + + msg = rsl_rll_simple(RSL_MT_REL_REQ, lchan2chan_nr(lchan), + link_id, 0); msgb_tv_put(msg, RSL_IE_RELEASE_MODE, 0); /* normal release */ + lchan->state = LCHAN_S_REL_REQ; + /* FIXME: start some timer in case we don't receive a REL ACK ? */ + msg->trx = lchan->ts->trx; return abis_rsl_sendmsg(msg); @@ -926,6 +745,10 @@ static int rsl_rx_chan_act_ack(struct msgb *msg) if (rslh->ie_chan != RSL_IE_CHAN_NR) return -EINVAL; + if (msg->lchan->state != LCHAN_S_ACT_REQ) + LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n", + gsm_lchan_name(msg->lchan), + gsm_lchans_name(msg->lchan->state)); msg->lchan->state = LCHAN_S_ACTIVE; dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_ACK, msg->lchan); @@ -939,16 +762,24 @@ static int rsl_rx_chan_act_nack(struct msgb *msg) struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tp; + LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK", + gsm_lchan_name(msg->lchan)); + /* BTS has rejected channel activation ?!? */ if (dh->ie_chan != RSL_IE_CHAN_NR) return -EINVAL; rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) - print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE), + if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) { + const u_int8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE); + print_rsl_cause(LOGL_ERROR, cause, TLVP_LEN(&tp, RSL_IE_CAUSE)); - - msg->lchan->state = LCHAN_S_NONE; + if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) + msg->lchan->state = LCHAN_S_NONE; + } else + msg->lchan->state = LCHAN_S_NONE; + + LOGPC(DRSL, LOGL_ERROR, "\n"); dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_NACK, msg->lchan); @@ -963,14 +794,16 @@ static int rsl_rx_conn_fail(struct msgb *msg) struct tlv_parsed tp; /* FIXME: print which channel */ - LOGP(DRSL, LOGL_NOTICE, "CONNECTION FAIL: RELEASING\n"); + LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING ", + gsm_lchan_name(msg->lchan)); rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) - print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE), + print_rsl_cause(LOGL_NOTICE, TLVP_VAL(&tp, RSL_IE_CAUSE), TLVP_LEN(&tp, RSL_IE_CAUSE)); + LOGPC(DRSL, LOGL_NOTICE, "\n"); /* FIXME: only free it after channel release ACK */ return rsl_rf_chan_release(msg->lchan); } @@ -1020,8 +853,8 @@ static void print_meas_rep(struct gsm_meas_rep *mr) return; for (i = 0; i < mr->num_cell; i++) { struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - DEBUGP(DMEAS, "ARFCN=%u BSIC=%u => %d dBm\n", mrc->arfcn, mrc->bsic, - rxlev2dbm(mrc->rxlev)); + DEBUGP(DMEAS, "IDX=%u ARFCN=%u BSIC=%u => %d dBm\n", + mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev)); } } @@ -1036,8 +869,11 @@ static int rsl_rx_meas_res(struct msgb *msg) /* check if this channel is actually active */ /* FIXME: maybe this check should be way more generic/centralized */ - if (msg->lchan->state != LCHAN_S_ACTIVE) + if (msg->lchan->state != LCHAN_S_ACTIVE) { + LOGP(DRSL, LOGL_NOTICE, "%s: MEAS RES for inactive channel\n", + gsm_lchan_name(msg->lchan)); return 0; + } memset(mr, 0, sizeof(*mr)); mr->lchan = msg->lchan; @@ -1098,7 +934,7 @@ static int rsl_rx_hando_det(struct msgb *msg) struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tp; - DEBUGP(DRSL, "HANDOVER DETECT "); + DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan)); rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); @@ -1120,18 +956,14 @@ static int abis_rsl_rx_dchan(struct msgb *msg) char *ts_name; msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr); - ts_name = gsm_ts_name(msg->lchan->ts); - - if (rslh->c.msg_type != RSL_MT_MEAS_RES) - DEBUGP(DRSL, "channel=%s chan_nr=0x%02x ", ts_name, rslh->chan_nr); + ts_name = gsm_lchan_name(msg->lchan); switch (rslh->c.msg_type) { case RSL_MT_CHAN_ACTIV_ACK: - DEBUGPC(DRSL, "CHANNEL ACTIVATE ACK\n"); + DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name); rc = rsl_rx_chan_act_ack(msg); break; case RSL_MT_CHAN_ACTIV_NACK: - DEBUGPC(DRSL, "CHANNEL ACTIVATE NACK\n"); rc = rsl_rx_chan_act_nack(msg); break; case RSL_MT_CONN_FAIL: @@ -1144,27 +976,31 @@ static int abis_rsl_rx_dchan(struct msgb *msg) rc = rsl_rx_hando_det(msg); break; case RSL_MT_RF_CHAN_REL_ACK: - DEBUGPC(DRSL, "RF CHANNEL RELEASE ACK\n"); + DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", ts_name); + if (msg->lchan->state != LCHAN_S_REL_REQ) + LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n", + gsm_lchan_name(msg->lchan), + gsm_lchans_name(msg->lchan->state)); msg->lchan->state = LCHAN_S_NONE; lchan_free(msg->lchan); break; case RSL_MT_MODE_MODIFY_ACK: - DEBUGPC(DRSL, "CHANNEL MODE MODIFY ACK\n"); + DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name); break; case RSL_MT_MODE_MODIFY_NACK: - DEBUGPC(DRSL, "CHANNEL MODE MODIFY NACK\n"); + LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name); break; case RSL_MT_IPAC_PDCH_ACT_ACK: - DEBUGPC(DRSL, "IPAC PDCH ACT ACK\n"); + DEBUGPC(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name); break; case RSL_MT_IPAC_PDCH_ACT_NACK: - DEBUGPC(DRSL, "IPAC PDCH ACT NACK\n"); + LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name); break; case RSL_MT_IPAC_PDCH_DEACT_ACK: - DEBUGPC(DRSL, "IPAC PDCH DEACT ACK\n"); + DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name); break; case RSL_MT_IPAC_PDCH_DEACT_NACK: - DEBUGPC(DRSL, "IPAC PDCH DEACT NACK\n"); + LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name); break; case RSL_MT_PHY_CONTEXT_CONF: case RSL_MT_PREPROC_MEAS_RES: @@ -1174,12 +1010,12 @@ static int abis_rsl_rx_dchan(struct msgb *msg) case RSL_MT_MR_CODEC_MOD_ACK: case RSL_MT_MR_CODEC_MOD_NACK: case RSL_MT_MR_CODEC_MOD_PER: - DEBUGPC(DRSL, "Unimplemented Abis RSL DChan msg 0x%02x\n", - rslh->c.msg_type); + LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan " + "msg 0x%02x\n", ts_name, rslh->c.msg_type); break; default: - DEBUGPC(DRSL, "unknown Abis RSL DChan msg 0x%02x\n", - rslh->c.msg_type); + LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n", + ts_name, rslh->c.msg_type); return -EINVAL; } @@ -1191,12 +1027,12 @@ static int rsl_rx_error_rep(struct msgb *msg) struct abis_rsl_common_hdr *rslh = msgb_l2(msg); struct tlv_parsed tp; - LOGP(DRSL, LOGL_ERROR, "ERROR REPORT "); + LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(msg->trx)); rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh)); if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) - print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE), + print_rsl_cause(LOGL_ERROR, TLVP_VAL(&tp, RSL_IE_CAUSE), TLVP_LEN(&tp, RSL_IE_CAUSE)); LOGPC(DRSL, LOGL_ERROR, "\n"); @@ -1215,15 +1051,16 @@ static int abis_rsl_rx_trx(struct msgb *msg) break; case RSL_MT_RF_RES_IND: /* interference on idle channels of TRX */ - //DEBUGP(DRSL, "TRX: RF Interference Indication\n"); + //DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(msg->trx)); break; case RSL_MT_OVERLOAD: /* indicate CCCH / ACCH / processor overload */ - LOGP(DRSL, LOGL_ERROR, "TRX: CCCH/ACCH/CPU Overload\n"); + LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n", + gsm_trx_name(msg->trx)); break; default: - DEBUGP(DRSL, "Unknown Abis RSL TRX message type 0x%02x\n", - rslh->msg_type); + LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message " + "type 0x%02x\n", gsm_trx_name(msg->trx), rslh->msg_type); return -EINVAL; } return rc; @@ -1269,18 +1106,24 @@ static int rsl_rx_chan_rqd(struct msgb *msg) lctype = get_ctype_by_chreq(bts, rqd_ref->ra, bts->network->neci); chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra, bts->network->neci); - bts->network->stats.chreq.total++; + counter_inc(bts->network->stats.chreq.total); /* check availability / allocate channel */ lchan = lchan_alloc(bts, lctype); if (!lchan) { - DEBUGP(DRSL, "CHAN RQD: no resources for %s 0x%x\n", - gsm_lchan_name(lctype), rqd_ref->ra); - bts->network->stats.chreq.no_channel++; + LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n", + msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra); + counter_inc(bts->network->stats.chreq.no_channel); /* FIXME: send some kind of reject ?!? */ return -ENOMEM; } + if (lchan->state != LCHAN_S_NONE) + LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel " + "in state %s\n", gsm_lchan_name(lchan), + gsm_lchans_name(lchan->state)); + lchan->state = LCHAN_S_ACT_REQ; + ts_number = lchan->ts->nr; arfcn = lchan->ts->trx->arfcn; subch = lchan->nr; @@ -1308,10 +1151,9 @@ static int rsl_rx_chan_rqd(struct msgb *msg) ia.timing_advance = rqd_ta; ia.mob_alloc_len = 0; - DEBUGP(DRSL, "Activating ARFCN(%u) TS(%u) SS(%u) lctype %s " - "chan_nr=0x%02x r=%s ra=0x%02x\n", - arfcn, ts_number, subch, gsm_lchan_name(lchan->type), - ia.chan_desc.chan_nr, gsm_chreq_name(chreq_reason), + DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s " + "r=%s ra=0x%02x\n", gsm_lchan_name(lchan), arfcn, subch, + gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason), rqd_ref->ra); /* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */ @@ -1390,10 +1232,12 @@ static int rsl_rx_rll_err_ind(struct msgb *msg) struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); u_int8_t *rlm_cause = rllh->data; - LOGP(DRLL, LOGL_ERROR, "ERROR INDICATION cause=0x%02x\n", rlm_cause[1]); + LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s\n", + gsm_lchan_name(msg->lchan), + get_value_string(rsl_rlm_cause_strs, rlm_cause[1])); rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); - + if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED) return rsl_rf_chan_release(msg->lchan); @@ -1414,9 +1258,8 @@ static int abis_rsl_rx_rll(struct msgb *msg) u_int8_t sapi = rllh->link_id & 7; msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr); - ts_name = gsm_ts_name(msg->lchan->ts); - DEBUGP(DRLL, "channel=%s chan_nr=0x%02x sapi=%u ", ts_name, - rllh->chan_nr, sapi); + ts_name = gsm_lchan_name(msg->lchan); + DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi); switch (rllh->c.msg_type) { case RSL_MT_DATA_IND: @@ -1566,9 +1409,8 @@ int rsl_ipacc_crcx(struct gsm_lchan *lchan) lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan); msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); - DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_BIND " - "speech_mode=0x%02x\n", gsm_ts_name(lchan->ts), - dh->chan_nr, lchan->abis_ip.speech_mode); + DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x\n", + gsm_lchan_name(lchan), lchan->abis_ip.speech_mode); msg->trx = lchan->ts->trx; @@ -1597,9 +1439,8 @@ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port, lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan); ia.s_addr = htonl(ip); - DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_MDCX " - "IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d speech_mode=0x%02x\n", - gsm_ts_name(lchan->ts), dh->chan_nr, inet_ntoa(ia), port, + DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d " + "speech_mode=0x%02x\n", gsm_lchan_name(lchan), inet_ntoa(ia), port, rtp_payload2, lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode); msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); @@ -1640,8 +1481,7 @@ int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan) dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; dh->chan_nr = lchan2chan_nr(lchan); - DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_PDCH_ACT\n", - gsm_ts_name(lchan->ts), dh->chan_nr); + DEBUGP(DRSL, "%s IPAC_PDCH_ACT\n", gsm_lchan_name(lchan)); msg->trx = lchan->ts->trx; @@ -1722,7 +1562,7 @@ static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg) rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (TLVP_PRESENT(&tv, RSL_IE_CAUSE)) - print_rsl_cause(TLVP_VAL(&tv, RSL_IE_CAUSE), + print_rsl_cause(LOGL_DEBUG, TLVP_VAL(&tv, RSL_IE_CAUSE), TLVP_LEN(&tv, RSL_IE_CAUSE)); /* the BTS tells us a RTP stream has been disconnected */ @@ -1739,38 +1579,38 @@ static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg) static int abis_rsl_rx_ipacc(struct msgb *msg) { struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + char *ts_name; int rc = 0; msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr); - DEBUGP(DRSL, "channel=%s chan_nr=0x%02x ", - gsm_ts_name(msg->lchan->ts), rllh->chan_nr); + ts_name = gsm_lchan_name(msg->lchan); switch (rllh->c.msg_type) { case RSL_MT_IPAC_CRCX_ACK: - DEBUGPC(DRSL, "IPAC_CRCX_ACK "); + DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name); rc = abis_rsl_rx_ipacc_crcx_ack(msg); break; case RSL_MT_IPAC_CRCX_NACK: /* somehow the BTS was unable to bind the lchan to its local * port?!? */ - DEBUGPC(DRSL, "IPAC_CRCX_NACK "); + LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name); break; case RSL_MT_IPAC_MDCX_ACK: /* the BTS tells us that a connect operation was successful */ - DEBUGPC(DRSL, "IPAC_MDCX_ACK "); + DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name); rc = abis_rsl_rx_ipacc_mdcx_ack(msg); break; case RSL_MT_IPAC_MDCX_NACK: /* somehow the BTS was unable to connect the lchan to a remote * port */ - DEBUGPC(DRSL, "IPAC_MDCX_NACK "); + LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name); break; case RSL_MT_IPAC_DLCX_IND: - DEBUGPC(DRSL, "IPAC_DLCX_IND "); + DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name); rc = abis_rsl_rx_ipacc_dlcx_ind(msg); break; default: - LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x", + LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n", rllh->c.msg_type); break; } @@ -1827,45 +1667,6 @@ int abis_rsl_rcvmsg(struct msgb *msg) return rc; } - -/* Section 3.3.2.3 TS 05.02. I think this looks like a table */ -int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf) -{ - switch (ccch_conf) { - case RSL_BCCH_CCCH_CONF_1_NC: - return 1; - case RSL_BCCH_CCCH_CONF_1_C: - return 1; - case RSL_BCCH_CCCH_CONF_2_NC: - return 2; - case RSL_BCCH_CCCH_CONF_3_NC: - return 3; - case RSL_BCCH_CCCH_CONF_4_NC: - return 4; - default: - return -1; - } -} - -/* Section 3.3.2.3 TS 05.02 */ -int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf) -{ - switch (ccch_conf) { - case RSL_BCCH_CCCH_CONF_1_NC: - return 0; - case RSL_BCCH_CCCH_CONF_1_C: - return 1; - case RSL_BCCH_CCCH_CONF_2_NC: - return 0; - case RSL_BCCH_CCCH_CONF_3_NC: - return 0; - case RSL_BCCH_CCCH_CONF_4_NC: - return 0; - default: - return -1; - } -} - /* From Table 10.5.33 of GSM 04.08 */ int rsl_number_of_paging_subchannels(struct gsm_bts *bts) { diff --git a/openbsc/src/bitvec.c b/openbsc/src/bitvec.c deleted file mode 100644 index d6f5679cf..000000000 --- a/openbsc/src/bitvec.c +++ /dev/null @@ -1,170 +0,0 @@ -/* bit vector utility routines */ - -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - - -#include <errno.h> -#include <sys/types.h> - -#include <openbsc/bitvec.h> - -#define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit) - -static inline unsigned int bytenum_from_bitnum(unsigned int bitnum) -{ - unsigned int bytenum = bitnum / 8; - - return bytenum; -} - -/* convert ZERO/ONE/L/H to a bitmask at given pos in a byte */ -static u_int8_t bitval2mask(enum bit_value bit, u_int8_t bitnum) -{ - int bitval; - - switch (bit) { - case ZERO: - bitval = (0 << bitnum); - break; - case ONE: - bitval = (1 << bitnum); - break; - case L: - bitval = ((0x2b ^ (0 << bitnum)) & (1 << bitnum)); - break; - case H: - bitval = ((0x2b ^ (1 << bitnum)) & (1 << bitnum)); - break; - default: - return 0; - } - return bitval; -} - -/* check if the bit is 0 or 1 for a given position inside a bitvec */ -enum bit_value bitvec_get_bit_pos(struct bitvec *bv, unsigned int bitnr) -{ - unsigned int bytenum = bytenum_from_bitnum(bitnr); - unsigned int bitnum = 7 - (bitnr % 8); - u_int8_t bitval; - - if (bytenum >= bv->data_len) - return -EINVAL; - - bitval = bitval2mask(ONE, bitnum); - - if (bv->data[bytenum] & bitval) - return ONE; - - return ZERO; -} - -/* get the Nth set bit inside the bit vector */ -unsigned int bitvec_get_nth_set_bit(struct bitvec *bv, unsigned int n) -{ - unsigned int i, k = 0; - - for (i = 0; i < bv->data_len*8; i++) { - if (bitvec_get_bit_pos(bv, i) == ONE) { - k++; - if (k == n) - return i; - } - } - - return 0; -} - -/* set the bit at a given position inside a bitvec */ -int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr, - enum bit_value bit) -{ - unsigned int bytenum = bytenum_from_bitnum(bitnr); - unsigned int bitnum = 7 - (bitnr % 8); - u_int8_t bitval; - - if (bytenum >= bv->data_len) - return -EINVAL; - - /* first clear the bit */ - bitval = bitval2mask(ONE, bitnum); - bv->data[bytenum] &= ~bitval; - - /* then set it to desired value */ - bitval = bitval2mask(bit, bitnum); - bv->data[bytenum] |= bitval; - - return 0; -} - -/* set the next bit inside a bitvec */ -int bitvec_set_bit(struct bitvec *bv, enum bit_value bit) -{ - int rc; - - rc = bitvec_set_bit_pos(bv, bv->cur_bit, bit); - if (!rc) - bv->cur_bit++; - - return rc; -} - -/* set multiple bits (based on array of bitvals) at current pos */ -int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count) -{ - int i, rc; - - for (i = 0; i < count; i++) { - rc = bitvec_set_bit(bv, bits[i]); - if (rc) - return rc; - } - - return 0; -} - -/* set multiple bits (based on numeric value) at current pos */ -int bitvec_set_uint(struct bitvec *bv, unsigned int ui, int num_bits) -{ - int i, rc; - - for (i = 0; i < num_bits; i++) { - int bit = 0; - if (ui & (1 << (num_bits - i - 1))) - bit = 1; - rc = bitvec_set_bit(bv, bit); - if (rc) - return rc; - } - - return 0; -} - -/* pad all remaining bits up to num_bits */ -int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit) -{ - unsigned int i; - - for (i = bv->cur_bit; i <= up_to_bit; i++) - bitvec_set_bit(bv, L); - - return 0; -} diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c index 3e8bf88a3..80f9ba956 100644 --- a/openbsc/src/bs11_config.c +++ b/openbsc/src/bs11_config.c @@ -36,10 +36,10 @@ #include <openbsc/gsm_data.h> #include <openbsc/abis_nm.h> -#include <openbsc/msgb.h> -#include <openbsc/tlv.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> #include <openbsc/debug.h> -#include <openbsc/select.h> +#include <osmocore/select.h> #include <openbsc/rs232.h> /* state of our bs11_config application */ @@ -51,7 +51,7 @@ enum bs11cfg_state { STATE_QUERY, }; static enum bs11cfg_state bs11cfg_state = STATE_NONE; -static char *command; +static char *command, *value; struct timer_list status_timer; static const u_int8_t obj_li_attr[] = { @@ -71,6 +71,13 @@ static const char *trx1_password = "1111111111"; static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 }; +static struct debug_target *stderr_target; + +/* dummy function to keep gsm_data.c happy */ +struct counter *counter_alloc(const char *name) +{ + return NULL; +} int handle_serial_msg(struct msgb *rx_msg); @@ -533,6 +540,21 @@ static int handle_state_resp(enum abis_bs11_phase state) sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; + } else if (!strcmp(command, "pll-setvalue")) { + abis_nm_bs11_set_pll(g_bts, atoi(value)); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-workvalue")) { + /* To set the work value we need to login as FIELD */ + abis_nm_bs11_factory_logon(g_bts, 0); + sleep(1); + abis_nm_bs11_infield_logon(g_bts, 1); + sleep(1); + abis_nm_bs11_set_pll(g_bts, atoi(value)); + sleep(1); + abis_nm_bs11_infield_logon(g_bts, 0); + command = NULL; } else if (!strcmp(command, "oml-tei")) { abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML); command = NULL; @@ -627,7 +649,7 @@ int handle_serial_msg(struct msgb *rx_msg) exit(0); break; case NM_MT_BS11_GET_STATE_ACK: - rc = abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); + rc = abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); print_state(&tp); if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) && TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1) @@ -635,7 +657,7 @@ int handle_serial_msg(struct msgb *rx_msg) break; case NM_MT_GET_ATTR_RESP: printf("\n%sATTRIBUTES:\n", obj_name(foh)); - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); + abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); rc = print_attr(&tp); //hexdump(foh->data, oh->length-sizeof(*foh)); break; @@ -699,25 +721,27 @@ static void print_help(void) printf("\t-p --port </dev/ttyXXX>\t\tSpecify serial port\n"); printf("\t-s --software <file>\t\tSpecify Software file\n"); printf("\t-S --safety <file>\t\tSpecify Safety Load file\n"); - printf("\t-d --delay <ms>\t\tSpecify delay in milliseconds\n"); + printf("\t-d --delay <ms>\t\t\tSpecify delay in milliseconds\n"); printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n"); printf("\t-w --win-size <num>\t\tSpecify Window Size\n"); printf("\t-f --forced\t\t\tForce Software Load\n"); printf("\nSupported commands:\n"); - printf("\tquery\t\tQuery the BS-11 about serial number and configuration\n"); - printf("\tdisconnect\tDisconnect A-bis link (go into administrative state)\n"); - printf("\tresconnect\tReconnect A-bis link (go into normal state)\n"); - printf("\trestart\t\tRestart the BTS\n"); - printf("\tsoftware\tDownload Software (only in administrative state)\n"); - printf("\tcreate-trx1\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n"); - printf("\tdelete-trx1\tDelete objects for TRX1\n"); - printf("\tpll-e1-locked\tSet the PLL to be locked to E1 clock\n"); - printf("\tpll-standalone\tSet the PLL to be in standalone mode\n"); - printf("\toml-tei\tSet OML E1 TS and TEI\n"); - printf("\tbport0-star\tSet BPORT0 line config to star\n"); + printf("\tquery\t\t\tQuery the BS-11 about serial number and configuration\n"); + printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n"); + printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n"); + printf("\trestart\t\t\tRestart the BTS\n"); + printf("\tsoftware\t\tDownload Software (only in administrative state)\n"); + printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n"); + printf("\tdelete-trx1\t\tDelete objects for TRX1\n"); + printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n"); + printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n"); + printf("\tpll-setvalue <value>\tSet the PLL set value\n"); + printf("\tpll-workvalue <value>\tSet the PLL work value\n"); + printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n"); + printf("\tbport0-star\t\tSet BPORT0 line config to star\n"); printf("\tbport0-multiport\tSet BPORT0 line config to multiport\n"); - printf("\tcreate-bport1\tCreate BPORT1 object\n"); - printf("\tdelete-bport1\tDelete BPORT1 object\n"); + printf("\tcreate-bport1\t\tCreate BPORT1 object\n"); + printf("\tdelete-bport1\t\tDelete BPORT1 object\n"); } static void handle_options(int argc, char **argv) @@ -754,7 +778,7 @@ static void handle_options(int argc, char **argv) serial_port = optarg; break; case 'b': - debug_parse_category_mask(optarg); + debug_parse_category_mask(stderr_target, optarg); break; case 's': fname_software = optarg; @@ -784,6 +808,9 @@ static void handle_options(int argc, char **argv) } if (optind < argc) command = argv[optind]; + if (optind+1 < argc) + value = argv[optind+1]; + } static int num_sigint; @@ -807,7 +834,12 @@ int main(int argc, char **argv) struct gsm_network *gsmnet; int rc; + debug_init(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + debug_set_all_filter(stderr_target, 1); handle_options(argc, argv); + bts_model_bs11_init(); gsmnet = gsm_network_init(1, 1, NULL); if (!gsmnet) { diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c index a9a5d372f..49c9d36ef 100644 --- a/openbsc/src/bsc_hack.c +++ b/openbsc/src/bsc_hack.c @@ -1,6 +1,6 @@ /* A hackish minimal BSC (+MSC +HLR) implementation */ -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> * All Rights Reserved * @@ -31,17 +31,23 @@ #include <getopt.h> #include <openbsc/db.h> -#include <openbsc/select.h> +#include <osmocore/select.h> #include <openbsc/debug.h> #include <openbsc/e1_input.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/signal.h> /* MCC and MNC for the Location Area Identifier */ +static struct debug_target *stderr_target; struct gsm_network *bsc_gsmnet = 0; static const char *database_name = "hlr.sqlite3"; static const char *config_file = "openbsc.cfg"; + +/* timer to store statistics */ +#define DB_SYNC_INTERVAL 60, 0 +static struct timer_list db_sync_timer; + extern int bsc_bootstrap_network(int (*mmc_rev)(struct gsm_network *, int, void *), const char *cfg_file); extern int bsc_shutdown_net(struct gsm_network *net); @@ -105,10 +111,10 @@ static void handle_options(int argc, char** argv) print_help(); exit(0); case 's': - debug_use_color(0); + debug_set_use_color(stderr_target, 0); break; case 'd': - debug_parse_category_mask(optarg); + debug_parse_category_mask(stderr_target, optarg); break; case 'l': database_name = strdup(optarg); @@ -120,7 +126,7 @@ static void handle_options(int argc, char** argv) create_pcap_file(optarg); break; case 'T': - debug_timestamp(1); + debug_set_print_timestamp(stderr_target, 1); break; case 'P': ipacc_rtp_direct = 0; @@ -132,6 +138,7 @@ static void handle_options(int argc, char** argv) } } +extern void *tall_vty_ctx; static void signal_handler(int signal) { fprintf(stdout, "signal %u received\n", signal); @@ -147,22 +154,53 @@ static void signal_handler(int signal) /* in case of abort, we want to obtain a talloc report * and then return to the caller, who will abort the process */ case SIGUSR1: + talloc_report(tall_vty_ctx, stderr); talloc_report_full(tall_bsc_ctx, stderr); break; + case SIGUSR2: + talloc_report_full(tall_vty_ctx, stderr); + break; default: break; } } +/* timer handling */ +static int _db_store_counter(struct counter *counter, void *data) +{ + return db_store_counter(counter); +} + +static void db_sync_timer_cb(void *data) +{ + /* store counters to database and re-schedule */ + counters_for_each(_db_store_counter, NULL); + bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL); +} + +extern int bts_model_unknown_init(void); +extern int bts_model_bs11_init(void); +extern int bts_model_nanobts_init(void); + int main(int argc, char **argv) { int rc; + debug_init(); tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); talloc_ctx_init(); on_dso_load_token(); on_dso_load_rrlp(); on_dso_load_ho_dec(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + + bts_model_unknown_init(); + bts_model_bs11_init(); + bts_model_nanobts_init(); + + /* enable filters */ + debug_set_all_filter(stderr_target, 1); /* parse options */ handle_options(argc, argv); @@ -182,6 +220,11 @@ int main(int argc, char **argv) } printf("DB: Database prepared.\n"); + /* setup the timer */ + db_sync_timer.cb = db_sync_timer_cb; + db_sync_timer.data = NULL; + bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL); + rc = bsc_bootstrap_network(mncc_recv, config_file); if (rc < 0) exit(1); @@ -189,10 +232,12 @@ int main(int argc, char **argv) signal(SIGINT, &signal_handler); signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); signal(SIGPIPE, SIG_IGN); while (1) { bsc_upqueue(bsc_gsmnet); + debug_reset_context(); bsc_select_main(0); } } diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c index c87750d77..ecf0f03cd 100644 --- a/openbsc/src/bsc_init.c +++ b/openbsc/src/bsc_init.c @@ -21,7 +21,7 @@ */ #include <openbsc/gsm_data.h> -#include <openbsc/gsm_utils.h> +#include <osmocore/gsm_utils.h> #include <openbsc/gsm_04_08.h> #include <openbsc/abis_rsl.h> #include <openbsc/abis_nm.h> @@ -31,7 +31,7 @@ #include <openbsc/system_information.h> #include <openbsc/paging.h> #include <openbsc/signal.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> /* global pointer to the gsm network data structure */ extern struct gsm_network *bsc_gsmnet; @@ -355,11 +355,11 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, switch (obj_class) { case NM_OC_SITE_MANAGER: bts = container_of(obj, struct gsm_bts, site_mgr); - if (new_state->operational == 2 && - new_state->availability == NM_AVSTATE_OK) { - printf("STARTING SITE MANAGER\n"); + if ((new_state->operational == 2 && + new_state->availability == NM_AVSTATE_OK) || + (new_state->operational == 1 && + new_state->availability == NM_AVSTATE_OFF_LINE)) abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff); - } break; case NM_OC_BTS: bts = obj; @@ -412,6 +412,8 @@ static int sw_activ_rep(struct msgb *mb) struct gsm_bts *bts = mb->trx->bts; struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); + if (!trx) + return -EINVAL; switch (foh->obj_class) { case NM_OC_BASEB_TRANSC: @@ -433,8 +435,7 @@ static int sw_activ_rep(struct msgb *mb) * This code is here to make sure that on start * a TRX remains locked. */ - int rc_state = trx->rf_locked ? - NM_STATE_LOCKED : NM_STATE_UNLOCKED; + int rc_state = trx->nm_state.administrative; /* Patch ARFCN into radio attribute */ nanobts_attr_radio[5] &= 0xf0; nanobts_attr_radio[5] |= trx->arfcn >> 8; @@ -691,7 +692,7 @@ static int set_system_infos(struct gsm_bts_trx *trx) rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp)); } #ifdef GPRS - i = 13 + i = 13; rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_13); if (rc < 0) goto err_out; @@ -756,9 +757,10 @@ static void patch_nm_tables(struct gsm_bts *bts) static void bootstrap_rsl(struct gsm_bts_trx *trx) { LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) " - "using MCC=%u MNC=%u BSIC=%u TSC=%u\n", - trx->bts->nr, trx->nr, bsc_gsmnet->country_code, - bsc_gsmnet->network_code, trx->bts->bsic, trx->bts->tsc); + "on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u TSC=%u\n", + trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code, + bsc_gsmnet->network_code, trx->bts->location_area_code, + trx->bts->cell_identity, trx->bts->bsic, trx->bts->tsc); set_system_infos(trx); } @@ -829,11 +831,6 @@ static int bootstrap_bts(struct gsm_bts *bts) /* T3212 is set from vty/config */ /* some defaults for our system information */ - bts->si_common.rach_control.re = 1; /* no re-establishment */ - bts->si_common.rach_control.tx_integer = 5; /* 8 slots spread */ - bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ - bts->si_common.rach_control.t2 = 4; /* no emergency calls */ - bts->si_common.cell_options.radio_link_timeout = 2; /* 12 */ bts->si_common.cell_options.dtx = 2; /* MS shall not use upplink DTX */ bts->si_common.cell_options.pwrc = 0; /* PWRC not set */ diff --git a/openbsc/src/bsc_mgcp.c b/openbsc/src/bsc_mgcp.c deleted file mode 100644 index d25edc884..000000000 --- a/openbsc/src/bsc_mgcp.c +++ /dev/null @@ -1,1237 +0,0 @@ -/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ - -/* - * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2009 by on-waves.com - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <ctype.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <limits.h> -#include <unistd.h> - -#include <sys/socket.h> -#include <arpa/inet.h> - -#include <openbsc/debug.h> -#include <openbsc/msgb.h> -#include <openbsc/talloc.h> -#include <openbsc/gsm_data.h> -#include <openbsc/select.h> -#include <openbsc/mgcp.h> -#include <openbsc/telnet_interface.h> - -#include <vty/command.h> -#include <vty/vty.h> - -/* this is here for the vty... it will never be called */ -void subscr_put() { abort(); } - -#define _GNU_SOURCE -#include <getopt.h> - -#warning "Make use of the rtp proxy code" - -static int source_port = 2427; -static const char *local_ip = NULL; -static const char *source_addr = "0.0.0.0"; -static struct bsc_fd bfd; -static unsigned int number_endpoints = 0; -static const char *bts_ip = NULL; -static struct in_addr bts_in; -static int first_request = 1; -static const char *audio_name = "GSM-EFR/8000"; -static int audio_payload = 97; -static int audio_loop = 0; -static int early_bind = 0; -static int rtp_base_port = 4000; - -static char *forward_ip = NULL; -static int forward_port = 0; -static char *config_file = "mgcp.cfg"; - -/* used by msgb and mgcp */ -void *tall_bsc_ctx = NULL; - -enum mgcp_connection_mode { - MGCP_CONN_NONE = 0, - MGCP_CONN_RECV_ONLY = 1, - MGCP_CONN_SEND_ONLY = 2, - MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY, -}; - -enum { - DEST_NETWORK = 0, - DEST_BTS = 1, -}; - -enum { - PROTO_RTP, - PROTO_RTCP, -}; - -#define CI_UNUSED 0 -static unsigned int last_call_id = 0; - -struct mgcp_endpoint { - int ci; - char *callid; - char *local_options; - int conn_mode; - - /* the local rtp port we are binding to */ - int rtp_port; - - /* - * RTP mangling: - * - we get RTP and RTCP to us and need to forward to the BTS - * - we get RTP and RTCP from the BTS and forward to the network - */ - struct bsc_fd local_rtp; - struct bsc_fd local_rtcp; - - struct in_addr remote; - struct in_addr bts; - - /* in network byte order */ - int net_rtp, net_rtcp; - int bts_rtp, bts_rtcp; -}; - -static struct mgcp_endpoint *endpoints = NULL; -#define ENDPOINT_NUMBER(endp) abs(endp - endpoints) - -/** - * Macro for tokenizing MGCP messages and SDP in one go. - * - */ -#define MSG_TOKENIZE_START \ - line_start = 0; \ - for (i = 0; i < msgb_l3len(msg); ++i) { \ - /* we have a line end */ \ - if (msg->l3h[i] == '\n') { \ - /* skip the first line */ \ - if (line_start == 0) { \ - line_start = i + 1; \ - continue; \ - } \ - \ - /* check if we have a proper param */ \ - if (i - line_start == 1 && msg->l3h[line_start] == '\r') { \ - } else if (i - line_start > 2 \ - && islower(msg->l3h[line_start]) \ - && msg->l3h[line_start + 1] == '=') { \ - } else if (i - line_start < 3 \ - || msg->l3h[line_start + 1] != ':' \ - || msg->l3h[line_start + 2] != ' ') \ - goto error; \ - \ - msg->l3h[i] = '\0'; \ - if (msg->l3h[i-1] == '\r') \ - msg->l3h[i-1] = '\0'; - -#define MSG_TOKENIZE_END \ - line_start = i + 1; \ - } \ - } - - -struct mgcp_msg_ptr { - unsigned int start; - unsigned int length; -}; - -struct mgcp_request { - char *name; - void (*handle_request) (struct msgb *msg, struct sockaddr_in *source); - char *debug_name; -}; - -#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \ - { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME }, - -static void handle_audit_endpoint(struct msgb *msg, struct sockaddr_in *source); -static void handle_create_con(struct msgb *msg, struct sockaddr_in *source); -static void handle_delete_con(struct msgb *msg, struct sockaddr_in *source); -static void handle_modify_con(struct msgb *msg, struct sockaddr_in *source); - -static int generate_call_id() -{ - int i; - - /* use the call id */ - ++last_call_id; - - /* handle wrap around */ - if (last_call_id == CI_UNUSED) - ++last_call_id; - - /* callstack can only be of size number_of_endpoints */ - /* verify that the call id is free, e.g. in case of overrun */ - for (i = 1; i < number_endpoints; ++i) - if (endpoints[i].ci == last_call_id) - return generate_call_id(); - - return last_call_id; -} - -/* FIXIME/TODO: need to have a list of pending transactions and check that */ -static unsigned int generate_transaction_id() -{ - return abs(rand()); -} - -static int _send(int fd, struct in_addr *addr, int port, char *buf, int len) -{ - struct sockaddr_in out; - out.sin_family = AF_INET; - out.sin_port = port; - memcpy(&out.sin_addr, addr, sizeof(*addr)); - - return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out)); -} - -/* - * There is data coming. We will have to figure out if it - * came from the BTS or the MediaGateway of the MSC. On top - * of that we need to figure out if it was RTP or RTCP. - * - * Currently we do not communicate with the BSC so we have - * no idea where the BTS is listening for RTP and need to - * do the classic routing trick. Wait for the first packet - * from the BTS and then go ahead. - */ -static int rtp_data_cb(struct bsc_fd *fd, unsigned int what) -{ - char buf[4096]; - struct sockaddr_in addr; - socklen_t slen = sizeof(addr); - struct mgcp_endpoint *endp; - int rc, dest, proto; - - endp = (struct mgcp_endpoint *) fd->data; - - rc = recvfrom(fd->fd, &buf, sizeof(buf), 0, - (struct sockaddr *) &addr, &slen); - if (rc < 0) { - DEBUGP(DMGCP, "Failed to receive message on: 0x%x\n", - ENDPOINT_NUMBER(endp)); - return -1; - } - - /* do not forward aynthing... maybe there is a packet from the bts */ - if (endp->ci == CI_UNUSED) { - DEBUGP(DMGCP, "Unknown message on endpoint: 0x%x\n", ENDPOINT_NUMBER(endp)); - return -1; - } - - /* - * Figure out where to forward it to. This code assumes that we - * have received the Connection Modify and know who is a legitimate - * partner. According to the spec we could attempt to forward even - * after the Create Connection but we will not as we are not really - * able to tell if this is legitimate. - */ - #warning "Slight spec violation. With connection mode recvonly we should attempt to forward." - dest = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0 && - (endp->net_rtp == addr.sin_port || endp->net_rtcp == addr.sin_port) - ? DEST_BTS : DEST_NETWORK; - proto = fd == &endp->local_rtp ? PROTO_RTP : PROTO_RTCP; - - /* We have no idea who called us, maybe it is the BTS. */ - if (dest == DEST_NETWORK && (endp->bts_rtp == 0 || forward_ip)) { - /* it was the BTS... */ - if (!bts_ip || memcmp(&addr.sin_addr, &bts_in, sizeof(bts_in)) == 0) { - if (fd == &endp->local_rtp) { - endp->bts_rtp = addr.sin_port; - } else { - endp->bts_rtcp = addr.sin_port; - } - - endp->bts = addr.sin_addr; - DEBUGP(DMGCP, "Found BTS for endpoint: 0x%x on port: %d/%d\n", - ENDPOINT_NUMBER(endp), ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp)); - } - } - - /* dispatch */ - if (audio_loop) - dest = !dest; - - if (dest == DEST_NETWORK) { - return _send(fd->fd, &endp->remote, - proto == PROTO_RTP ? endp->net_rtp : endp->net_rtcp, - buf, rc); - } else { - return _send(fd->fd, &endp->bts, - proto == PROTO_RTP ? endp->bts_rtp : endp->bts_rtcp, - buf, rc); - } -} - -static int create_bind(struct bsc_fd *fd, int port) -{ - struct sockaddr_in addr; - int on = 1; - - fd->fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd->fd < 0) - return -1; - - setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - inet_aton(source_addr, &addr.sin_addr); - - if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) - return -1; - - return 0; -} - -static int bind_rtp(struct mgcp_endpoint *endp) -{ - if (create_bind(&endp->local_rtp, endp->rtp_port) != 0) { - DEBUGP(DMGCP, "Failed to create RTP port: %d on 0x%x\n", - endp->rtp_port, ENDPOINT_NUMBER(endp)); - goto cleanup0; - } - - if (create_bind(&endp->local_rtcp, endp->rtp_port + 1) != 0) { - DEBUGP(DMGCP, "Failed to create RTCP port: %d on 0x%x\n", - endp->rtp_port + 1, ENDPOINT_NUMBER(endp)); - goto cleanup1; - } - - endp->local_rtp.cb = rtp_data_cb; - endp->local_rtp.data = endp; - endp->local_rtp.when = BSC_FD_READ; - if (bsc_register_fd(&endp->local_rtp) != 0) { - DEBUGP(DMGCP, "Failed to register RTP port %d on 0x%x\n", - endp->rtp_port, ENDPOINT_NUMBER(endp)); - goto cleanup2; - } - - endp->local_rtcp.cb = rtp_data_cb; - endp->local_rtcp.data = endp; - endp->local_rtcp.when = BSC_FD_READ; - if (bsc_register_fd(&endp->local_rtcp) != 0) { - DEBUGP(DMGCP, "Failed to register RTCP port %d on 0x%x\n", - endp->rtp_port + 1, ENDPOINT_NUMBER(endp)); - goto cleanup3; - } - - return 0; - -cleanup3: - bsc_unregister_fd(&endp->local_rtp); -cleanup2: - close(endp->local_rtcp.fd); - endp->local_rtcp.fd = -1; -cleanup1: - close(endp->local_rtp.fd); - endp->local_rtp.fd = -1; -cleanup0: - return -1; -} - -/* - * array of function pointers for handling various - * messages. In the future this might be binary sorted - * for performance reasons. - */ -static const struct mgcp_request mgcp_requests [] = { - MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint") - MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection") - MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection") - MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection") -}; - -static void send_response_with_data(int code, const char *msg, const char *trans, - const char *data, struct sockaddr_in *source) -{ - char buf[4096]; - int len; - - if (data) { - len = snprintf(buf, sizeof(buf), "%d %s\n%s", code, trans, data); - } else { - len = snprintf(buf, sizeof(buf), "%d %s\n", code, trans); - } - DEBUGP(DMGCP, "Sending response: code: %d for '%s'\n", code, msg); - - sendto(bfd.fd, buf, len, 0, (struct sockaddr *)source, sizeof(*source)); -} - -static void send_response(int code, const char *msg, const char *trans, struct sockaddr_in *source) -{ - send_response_with_data(code, msg, trans, NULL, source); -} - -static void send_with_sdp(struct mgcp_endpoint *endp, const char *msg, const char *trans_id, struct sockaddr_in *source) -{ - const char *addr = local_ip; - char sdp_record[4096]; - - if (!addr) - addr = source_addr; - - snprintf(sdp_record, sizeof(sdp_record) - 1, - "I: %d\n\n" - "v=0\r\n" - "c=IN IP4 %s\r\n" - "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d %s\r\n", - endp->ci, addr, endp->rtp_port, - audio_payload, audio_payload, audio_name); - return send_response_with_data(200, msg, trans_id, sdp_record, source); -} - -/* send a static record */ -static void send_rsip(struct sockaddr_in *source) -{ - char reset[4096]; - int len, rc; - - len = snprintf(reset, sizeof(reset) - 1, - "RSIP %u *@mgw MGCP 1.0\n" - "RM: restart\n", generate_transaction_id()); - rc = sendto(bfd.fd, reset, len, 0, (struct sockaddr *) source, sizeof(*source)); - if (rc < 0) { - DEBUGP(DMGCP, "Failed to send RSIP: %d\n", rc); - } -} - -/* - * handle incoming messages: - * - this can be a command (four letters, space, transaction id) - * - or a response (three numbers, space, transaction id) - */ -static void handle_message(struct msgb *msg, struct sockaddr_in *source) -{ - int code; - - if (msg->len < 4) { - DEBUGP(DMGCP, "mgs too short: %d\n", msg->len); - return; - } - - /* attempt to treat it as a response */ - if (sscanf((const char *)&msg->data[0], "%3d %*s", &code) == 1) { - DEBUGP(DMGCP, "Response: Code: %d\n", code); - } else { - int i, handled = 0; - msg->l3h = &msg->l2h[4]; - for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) - if (strncmp(mgcp_requests[i].name, (const char *) &msg->data[0], 4) == 0) { - handled = 1; - mgcp_requests[i].handle_request(msg, source); - } - if (!handled) { - DEBUGP(DMGCP, "MSG with type: '%.4s' not handled\n", &msg->data[0]); - } - } -} - -/* string tokenizer for the poor */ -static int find_msg_pointers(struct msgb *msg, struct mgcp_msg_ptr *ptrs, int ptrs_length) -{ - int i, found = 0; - - int whitespace = 1; - for (i = 0; i < msgb_l3len(msg) && ptrs_length > 0; ++i) { - /* if we have a space we found an end */ - if (msg->l3h[i] == ' ' || msg->l3h[i] == '\r' || msg->l3h[i] == '\n') { - if (!whitespace) { - ++found; - whitespace = 1; - ptrs->length = i - ptrs->start - 1; - ++ptrs; - --ptrs_length; - } else { - /* skip any number of whitespace */ - } - - /* line end... stop */ - if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') - break; - } else if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') { - /* line end, be done */ - break; - } else if (whitespace) { - whitespace = 0; - ptrs->start = i; - } - } - - if (ptrs_length == 0) - return -1; - return found; -} - -static struct mgcp_endpoint *find_endpoint(const char *mgcp) -{ - char *endptr = NULL; - unsigned int gw = INT_MAX; - - gw = strtoul(mgcp, &endptr, 16); - if (gw == 0 || gw >= number_endpoints || strcmp(endptr, "@mgw") != 0) { - DEBUGP(DMGCP, "Not able to find endpoint: '%s'\n", mgcp); - return NULL; - } - - return &endpoints[gw]; -} - -static int analyze_header(struct msgb *msg, struct mgcp_msg_ptr *ptr, int size, - const char **transaction_id, struct mgcp_endpoint **endp) -{ - int found; - - if (size < 3) { - DEBUGP(DMGCP, "Not enough space in ptr\n"); - return -1; - } - - found = find_msg_pointers(msg, ptr, size); - - if (found < 3) { - DEBUGP(DMGCP, "Gateway: Not enough params. Found: %d\n", found); - return -1; - } - - /* - * replace the space with \0. the main method gurantess that - * we still have + 1 for null termination - */ - msg->l3h[ptr[3].start + ptr[3].length + 1] = '\0'; - msg->l3h[ptr[2].start + ptr[2].length + 1] = '\0'; - msg->l3h[ptr[1].start + ptr[1].length + 1] = '\0'; - msg->l3h[ptr[0].start + ptr[0].length + 1] = '\0'; - - if (strncmp("1.0", (const char *)&msg->l3h[ptr[3].start], 3) != 0 - || strncmp("MGCP", (const char *)&msg->l3h[ptr[2].start], 4) != 0) { - DEBUGP(DMGCP, "Wrong MGCP version. Not handling: '%s' '%s'\n", - (const char *)&msg->l3h[ptr[3].start], - (const char *)&msg->l3h[ptr[2].start]); - return -1; - } - - *transaction_id = (const char *)&msg->l3h[ptr[0].start]; - *endp = find_endpoint((const char *)&msg->l3h[ptr[1].start]); - return *endp == NULL; -} - -static int verify_call_id(const struct mgcp_endpoint *endp, - const char *callid) -{ - if (strcmp(endp->callid, callid) != 0) { - DEBUGP(DMGCP, "CallIDs does not match on 0x%x. '%s' != '%s'\n", - ENDPOINT_NUMBER(endp), endp->callid, callid); - return -1; - } - - return 0; -} - -static int verify_ci(const struct mgcp_endpoint *endp, - const char *ci) -{ - if (atoi(ci) != endp->ci) { - DEBUGP(DMGCP, "ConnectionIdentifiers do not match on 0x%x. %d != %s\n", - ENDPOINT_NUMBER(endp), endp->ci, ci); - return -1; - } - - return 0; -} - -static void handle_audit_endpoint(struct msgb *msg, struct sockaddr_in *source) -{ - struct mgcp_msg_ptr data_ptrs[6]; - int found, response; - const char *trans_id; - struct mgcp_endpoint *endp; - - found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); - if (found != 0) - response = 500; - else - response = 200; - - return send_response(response, "AUEP", trans_id, source); -} - -static int parse_conn_mode(const char* msg, int *conn_mode) -{ - int ret = 0; - if (strcmp(msg, "recvonly") == 0) - *conn_mode = MGCP_CONN_RECV_ONLY; - else if (strcmp(msg, "sendrecv") == 0) - *conn_mode = MGCP_CONN_RECV_SEND; - else { - DEBUGP(DMGCP, "Unknown connection mode: '%s'\n", msg); - ret = -1; - } - - return ret; -} - -static void handle_create_con(struct msgb *msg, struct sockaddr_in *source) -{ - struct mgcp_msg_ptr data_ptrs[6]; - int found, i, line_start; - const char *trans_id; - struct mgcp_endpoint *endp; - int error_code = 500; - - found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); - if (found != 0) - return send_response(500, "CRCX", trans_id, source); - - if (endp->ci != CI_UNUSED) { - DEBUGP(DMGCP, "Endpoint is already used. 0x%x\n", ENDPOINT_NUMBER(endp)); - return send_response(500, "CRCX", trans_id, source); - } - - /* parse CallID C: and LocalParameters L: */ - MSG_TOKENIZE_START - switch (msg->l3h[line_start]) { - case 'L': - endp->local_options = talloc_strdup(endpoints, - (const char *)&msg->l3h[line_start + 3]); - break; - case 'C': - endp->callid = talloc_strdup(endpoints, - (const char *)&msg->l3h[line_start + 3]); - break; - case 'M': - if (parse_conn_mode((const char *)&msg->l3h[line_start + 3], - &endp->conn_mode) != 0) { - error_code = 517; - goto error2; - } - break; - default: - DEBUGP(DMGCP, "Unhandled option: '%c'/%d on 0x%x\n", - msg->l3h[line_start], msg->l3h[line_start], - ENDPOINT_NUMBER(endp)); - break; - } - MSG_TOKENIZE_END - - /* initialize */ - endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0; - - /* set to zero until we get the info */ - memset(&endp->remote, 0, sizeof(endp->remote)); - - /* bind to the port now */ - endp->rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), rtp_base_port); - if (!early_bind && bind_rtp(endp) != 0) - goto error2; - - /* assign a local call identifier or fail */ - endp->ci = generate_call_id(); - if (endp->ci == CI_UNUSED) - goto error2; - - DEBUGP(DMGCP, "Creating endpoint on: 0x%x CI: %u port: %u\n", - ENDPOINT_NUMBER(endp), endp->ci, endp->rtp_port); - return send_with_sdp(endp, "CRCX", trans_id, source); -error: - DEBUGP(DMGCP, "Malformed line: %s on 0x%x with: line_start: %d %d\n", - hexdump(msg->l3h, msgb_l3len(msg)), - ENDPOINT_NUMBER(endp), line_start, i); - return send_response(error_code, "CRCX", trans_id, source); - -error2: - DEBUGP(DMGCP, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp)); - return send_response(error_code, "CRCX", trans_id, source); -} - -static void handle_modify_con(struct msgb *msg, struct sockaddr_in *source) -{ - struct mgcp_msg_ptr data_ptrs[6]; - int found, i, line_start; - const char *trans_id; - struct mgcp_endpoint *endp; - int error_code = 500; - - found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); - if (found != 0) - return send_response(error_code, "MDCX", trans_id, source); - - if (endp->ci == CI_UNUSED) { - DEBUGP(DMGCP, "Endpoint is not holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp)); - return send_response(error_code, "MDCX", trans_id, source); - } - - MSG_TOKENIZE_START - switch (msg->l3h[line_start]) { - case 'C': { - if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0) - goto error3; - break; - } - case 'I': { - if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0) - goto error3; - break; - } - case 'L': - /* skip */ - break; - case 'M': - if (parse_conn_mode((const char *)&msg->l3h[line_start + 3], - &endp->conn_mode) != 0) { - error_code = 517; - goto error3; - } - break; - case '\0': - /* SDP file begins */ - break; - case 'a': - case 'o': - case 's': - case 't': - case 'v': - /* skip these SDP attributes */ - break; - case 'm': { - int port; - const char *param = (const char *)&msg->l3h[line_start]; - - if (sscanf(param, "m=audio %d RTP/AVP %*d", &port) == 1) { - endp->net_rtp = htons(port); - endp->net_rtcp = htons(port + 1); - } - break; - } - case 'c': { - char ipv4[16]; - const char *param = (const char *)&msg->l3h[line_start]; - - if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) { - inet_aton(ipv4, &endp->remote); - } - break; - } - default: - DEBUGP(DMGCP, "Unhandled option: '%c'/%d on 0x%x\n", - msg->l3h[line_start], msg->l3h[line_start], - ENDPOINT_NUMBER(endp)); - break; - } - MSG_TOKENIZE_END - - /* modify */ - DEBUGP(DMGCP, "Modified endpoint on: 0x%x Server: %s:%u\n", - ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), endp->net_rtp); - return send_with_sdp(endp, "MDCX", trans_id, source); - -error: - DEBUGP(DMGCP, "Malformed line: %s on 0x%x with: line_start: %d %d %d\n", - hexdump(msg->l3h, msgb_l3len(msg)), - ENDPOINT_NUMBER(endp), line_start, i, msg->l3h[line_start]); - return send_response(error_code, "MDCX", trans_id, source); - -error3: - return send_response(error_code, "MDCX", trans_id, source); -} - -static void handle_delete_con(struct msgb *msg, struct sockaddr_in *source) -{ - struct mgcp_msg_ptr data_ptrs[6]; - int found, i, line_start; - const char *trans_id; - struct mgcp_endpoint *endp; - int error_code = 500; - - found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); - if (found != 0) - return send_response(error_code, "DLCX", trans_id, source); - - if (endp->ci == CI_UNUSED) { - DEBUGP(DMGCP, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp)); - return send_response(error_code, "DLCX", trans_id, source); - } - - MSG_TOKENIZE_START - switch (msg->l3h[line_start]) { - case 'C': { - if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0) - goto error3; - break; - } - case 'I': { - if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0) - goto error3; - break; - } - default: - DEBUGP(DMGCP, "Unhandled option: '%c'/%d on 0x%x\n", - msg->l3h[line_start], msg->l3h[line_start], - ENDPOINT_NUMBER(endp)); - break; - } - MSG_TOKENIZE_END - - - /* free the connection */ - DEBUGP(DMGCP, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp)); - endp->ci= CI_UNUSED; - talloc_free(endp->callid); - talloc_free(endp->local_options); - - if (!early_bind) { - bsc_unregister_fd(&endp->local_rtp); - bsc_unregister_fd(&endp->local_rtcp); - } - - endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0; - - return send_response(250, "DLCX", trans_id, source); - -error: - DEBUGP(DMGCP, "Malformed line: %s on 0x%x with: line_start: %d %d\n", - hexdump(msg->l3h, msgb_l3len(msg)), - ENDPOINT_NUMBER(endp), line_start, i); - return send_response(error_code, "DLCX", trans_id, source); - -error3: - return send_response(error_code, "DLCX", trans_id, source); -} - -static void print_help() -{ - printf("Some useful help...\n"); - printf(" -h --help is printing this text.\n"); - printf(" -c --config-file filename The config file to use.\n"); -} - -static void handle_options(int argc, char** argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"config-file", 1, 0, 'c'}, - {0, 0, 0, 0}, - }; - - c = getopt_long(argc, argv, "hc:", long_options, &option_index); - - if (c == -1) - break; - - switch(c) { - case 'h': - print_help(); - exit(0); - break; - case 'c': - config_file = talloc_strdup(tall_bsc_ctx, optarg); - break; - default: - /* ignore */ - break; - }; - } -} - -static int read_call_agent(struct bsc_fd *fd, unsigned int what) -{ - struct sockaddr_in addr; - socklen_t slen = sizeof(addr); - struct msgb *msg; - - msg = (struct msgb *) fd->data; - - /* read one less so we can use it as a \0 */ - int rc = recvfrom(bfd.fd, msg->data, msg->data_len - 1, 0, - (struct sockaddr *) &addr, &slen); - if (rc < 0) { - perror("Gateway failed to read"); - return -1; - } else if (slen > sizeof(addr)) { - fprintf(stderr, "Gateway received message from outerspace: %d %d\n", - slen, sizeof(addr)); - return -1; - } - - if (first_request) { - first_request = 0; - send_rsip(&addr); - return 0; - } - - /* handle message now */ - msg->l2h = msgb_put(msg, rc); - handle_message(msg, &addr); - msgb_reset(msg); - return 0; -} - -/* - * vty code for mgcp below - */ -struct cmd_node mgcp_node = { - MGCP_NODE, - "%s(mgcp)#", - 1, -}; - -static int config_write_mgcp(struct vty *vty) -{ - vty_out(vty, "mgcp%s", VTY_NEWLINE); - if (local_ip) - vty_out(vty, " local ip %s%s", local_ip, VTY_NEWLINE); - if (bts_ip) - vty_out(vty, " bts ip %s%s", bts_ip, VTY_NEWLINE); - vty_out(vty, " bind ip %s%s", source_addr, VTY_NEWLINE); - vty_out(vty, " bind port %u%s", source_port, VTY_NEWLINE); - vty_out(vty, " bind early %u%s", !!early_bind, VTY_NEWLINE); - vty_out(vty, " rtp base %u%s", rtp_base_port, VTY_NEWLINE); - vty_out(vty, " sdp audio payload number %u%s", audio_payload, VTY_NEWLINE); - vty_out(vty, " sdp audio payload name %s%s", audio_name, VTY_NEWLINE); - vty_out(vty, " loop %u%s", !!audio_loop, VTY_NEWLINE); - vty_out(vty, " endpoints %u%s", number_endpoints, VTY_NEWLINE); - if (forward_ip) - vty_out(vty, " forward audio ip %s%s", forward_ip, VTY_NEWLINE); - if (forward_port != 0) - vty_out(vty, " forward audio port %d%s", forward_port, VTY_NEWLINE); - - return CMD_SUCCESS; -} - -DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp", - SHOW_STR "Display information about the MGCP Media Gateway") -{ - int i; - - vty_out(vty, "MGCP is up and running with %u endpoints:%s", number_endpoints - 1, VTY_NEWLINE); - for (i = 1; i < number_endpoints; ++i) { - struct mgcp_endpoint *endp = &endpoints[i]; - vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u%s", - i, endp->ci, - ntohs(endp->net_rtp), ntohs(endp->net_rtcp), - ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp), VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp, - cfg_mgcp_cmd, - "mgcp", - "Configure the MGCP") -{ - vty->node = MGCP_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_local_ip, - cfg_mgcp_local_ip_cmd, - "local ip IP", - "Set the IP to be used in SDP records") -{ - local_ip = talloc_strdup(tall_bsc_ctx, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bts_ip, - cfg_mgcp_bts_ip_cmd, - "bts ip IP", - "Set the IP of the BTS for RTP forwarding") -{ - bts_ip = talloc_strdup(tall_bsc_ctx, argv[0]); - inet_aton(bts_ip, &bts_in); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bind_ip, - cfg_mgcp_bind_ip_cmd, - "bind ip IP", - "Bind the MGCP to this local addr") -{ - source_addr = talloc_strdup(tall_bsc_ctx, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bind_port, - cfg_mgcp_bind_port_cmd, - "bind port <0-65534>", - "Bind the MGCP to this port") -{ - unsigned int port = atoi(argv[0]); - if (port > 65534) { - vty_out(vty, "%% wrong bind port '%s'%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - source_port = port; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bind_early, - cfg_mgcp_bind_early_cmd, - "bind early (0|1)", - "Bind all RTP ports early") -{ - unsigned int bind = atoi(argv[0]); - if (bind != 0 && bind != 1) { - vty_out(vty, "%% param must be 0 or 1.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - early_bind = bind == 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_base_port, - cfg_mgcp_rtp_base_port_cmd, - "rtp base <0-65534>", - "Base port to use") -{ - unsigned int port = atoi(argv[0]); - if (port > 65534) { - vty_out(vty, "%% wrong base port '%s'%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - rtp_base_port = port; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_sdp_payload_number, - cfg_mgcp_sdp_payload_number_cmd, - "sdp audio payload number <1-255>", - "Set the audio codec to use") -{ - unsigned int payload = atoi(argv[0]); - if (payload > 255) { - vty_out(vty, "%% wrong payload number '%s'%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - audio_payload = payload; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_sdp_payload_name, - cfg_mgcp_sdp_payload_name_cmd, - "sdp audio payload name NAME", - "Set the audio name to use") -{ - audio_name = talloc_strdup(tall_bsc_ctx, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_loop, - cfg_mgcp_loop_cmd, - "loop (0|1)", - "Loop the audio") -{ - audio_loop = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_number_endp, - cfg_mgcp_number_endp_cmd, - "number endpoints <0-65534>", - "The number of endpoints to allocate. This is not dynamic.") -{ - /* + 1 as we start counting at one */ - number_endpoints = atoi(argv[0]) + 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_forward_ip, - cfg_mgcp_forward_ip_cmd, - "forward audio ip IP", - "Forward packets from and to the IP. This disables most of the MGCP feature.") -{ - if (forward_ip) - talloc_free(forward_ip); - forward_ip = talloc_strdup(tall_bsc_ctx, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_forward_port, - cfg_mgcp_forward_port_cmd, - "forward audio port <1-15000>", - "Forward packets from and to the port. This disables most of the MGCP feature.") -{ - forward_port = atoi(argv[0]); - return CMD_SUCCESS; -} - -int bsc_vty_init(struct gsm_network *dummy) -{ - cmd_init(1); - vty_init(); - - install_element(VIEW_NODE, &show_mgcp_cmd); - - - install_element(CONFIG_NODE, &cfg_mgcp_cmd); - install_node(&mgcp_node, config_write_mgcp); - install_default(MGCP_NODE); - install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd); - install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd); - install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd); - install_element(MGCP_NODE, &cfg_mgcp_loop_cmd); - install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd); - install_element(MGCP_NODE, &cfg_mgcp_forward_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_forward_port_cmd); - return 0; -} - -int main(int argc, char** argv) -{ - struct gsm_network dummy_network; - struct sockaddr_in addr; - int on = 1, i, rc; - - tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent"); - handle_options(argc, argv); - - telnet_init(&dummy_network, 4243); - rc = vty_read_config_file(config_file); - if (rc < 0) { - fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); - return rc; - } - - - if (!bts_ip) - fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n"); - - endpoints = _talloc_zero_array(tall_bsc_ctx, - sizeof(struct mgcp_endpoint), - number_endpoints, "endpoints"); - if (!endpoints) { - fprintf(stderr, "Failed to allocate endpoints: %d. Quitting.\n", number_endpoints); - return -1; - } - - /* Initialize all endpoints */ - for (i = 0; i < number_endpoints; ++i) { - endpoints[i].local_rtp.fd = -1; - endpoints[i].local_rtcp.fd = -1; - endpoints[i].ci = CI_UNUSED; - } - - /* - * This application supports two modes. - * 1.) a true MGCP gateway with support for AUEP, CRCX, MDCX, DLCX - * 2.) plain forwarding of RTP packets on the endpoints. - * both modes are mutual exclusive - */ - if (forward_ip) { - int port = rtp_base_port; - if (forward_port != 0) - port = forward_port; - - if (!early_bind) { - DEBUGP(DMGCP, "Forwarding requires early bind.\n"); - return -1; - } - - /* - * Store the forward IP and assign a ci. For early bind - * the sockets will be created after this. - */ - for (i = 1; i < number_endpoints; ++i) { - struct mgcp_endpoint *endp = &endpoints[i]; - inet_aton(forward_ip, &endp->remote); - endp->ci = CI_UNUSED + 23; - endp->net_rtp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port)); - endp->net_rtcp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port) + 1); - } - - DEBUGP(DMGCP, "Configured for Audio Forwarding.\n"); - } else { - bfd.when = BSC_FD_READ; - bfd.cb = read_call_agent; - bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); - if (bfd.fd < 0) { - perror("Gateway failed to listen"); - return -1; - } - - setsockopt(bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(source_port); - inet_aton(source_addr, &addr.sin_addr); - - if (bind(bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("Gateway failed to bind"); - return -1; - } - - bfd.data = msgb_alloc(4096, "mgcp-msg"); - if (!bfd.data) { - fprintf(stderr, "Gateway memory error.\n"); - return -1; - } - - - if (bsc_register_fd(&bfd) != 0) { - DEBUGP(DMGCP, "Failed to register the fd\n"); - return -1; - } - - DEBUGP(DMGCP, "Configured for MGCP.\n"); - } - - /* initialisation */ - srand(time(NULL)); - - /* early bind */ - if (early_bind) { - for (i = 1; i < number_endpoints; ++i) { - struct mgcp_endpoint *endp = &endpoints[i]; - endp->rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), rtp_base_port); - if (bind_rtp(endp) != 0) - return -1; - } - } - - /* main loop */ - while (1) { - bsc_select_main(0); - } - - - return 0; -} diff --git a/openbsc/src/bsc_msc_ip.c b/openbsc/src/bsc_msc_ip.c index 5e3831bd4..4d5c42be4 100644 --- a/openbsc/src/bsc_msc_ip.c +++ b/openbsc/src/bsc_msc_ip.c @@ -35,11 +35,8 @@ #define _GNU_SOURCE #include <getopt.h> -#include <openbsc/select.h> #include <openbsc/debug.h> #include <openbsc/e1_input.h> -#include <openbsc/talloc.h> -#include <openbsc/select.h> #include <openbsc/ipaccess.h> #include <openbsc/bssap.h> #include <openbsc/paging.h> @@ -47,12 +44,16 @@ #include <openbsc/chan_alloc.h> #include <openbsc/bsc_msc.h> +#include <osmocore/select.h> +#include <osmocore/talloc.h> + #include <sccp/sccp.h> /* SCCP helper */ #define SCCP_IT_TIMER 60 /* MCC and MNC for the Location Area Identifier */ +static struct debug_target *stderr_target; struct gsm_network *bsc_gsmnet = 0; static const char *config_file = "openbsc.cfg"; static char *msc_address = "127.0.0.1"; @@ -714,16 +715,16 @@ static void handle_options(int argc, char** argv) print_help(); exit(0); case 's': - debug_use_color(0); + debug_set_use_color(stderr_target, 0); break; case 'd': - debug_parse_category_mask(optarg); + debug_parse_category_mask(stderr_target, optarg); break; case 'c': config_file = strdup(optarg); break; case 'T': - debug_timestamp(1); + debug_set_print_timestamp(stderr_target, 1); break; case 'P': ipacc_rtp_direct = 0; @@ -802,11 +803,25 @@ static void test_mode() bssmap_rcvmsg_dt1(&conn, msg, ARRAY_SIZE(assignment_req)); } +extern int bts_model_unknown_init(void); +extern int bts_model_bs11_init(void); +extern int bts_model_nanobts_init(void); + int main(int argc, char **argv) { int rc; + debug_init(); tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + + bts_model_unknown_init(); + bts_model_bs11_init(); + bts_model_nanobts_init(); + + /* enable filters */ + debug_set_all_filter(stderr_target, 1); /* parse options */ handle_options(argc, argv); diff --git a/openbsc/src/bsc_rll.c b/openbsc/src/bsc_rll.c index 780a84e39..e9d6f252a 100644 --- a/openbsc/src/bsc_rll.c +++ b/openbsc/src/bsc_rll.c @@ -24,9 +24,9 @@ #include <errno.h> #include <openbsc/debug.h> -#include <openbsc/talloc.h> -#include <openbsc/timer.h> -#include <openbsc/linuxlist.h> +#include <osmocore/talloc.h> +#include <osmocore/timer.h> +#include <osmocore/linuxlist.h> #include <openbsc/bsc_rll.h> #include <openbsc/gsm_data.h> #include <openbsc/chan_alloc.h> diff --git a/openbsc/src/bssap.c b/openbsc/src/bssap.c index 002e7ce21..7ded8d1f3 100644 --- a/openbsc/src/bssap.c +++ b/openbsc/src/bssap.c @@ -26,10 +26,11 @@ #include <openbsc/debug.h> #include <openbsc/mgcp.h> #include <openbsc/signal.h> -#include <openbsc/tlv.h> #include <openbsc/paging.h> #include <openbsc/chan_alloc.h> +#include <osmocore/tlv.h> + #include <sccp/sccp.h> #include <arpa/inet.h> @@ -684,9 +685,9 @@ int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length) if (gh->msg_type == GSM48_MT_MM_LOC_UPD_ACCEPT) { struct gsm_network *net = gsm48->trx->bts->network; struct gsm48_loc_area_id *lai = (struct gsm48_loc_area_id *) &gh->data[0]; - gsm0408_generate_lai(lai, net->country_code, - net->network_code, - gsm48->trx->bts->location_area_code); + gsm48_generate_lai(lai, net->country_code, + net->network_code, + gsm48->trx->bts->location_area_code); } } } @@ -726,8 +727,8 @@ struct msgb *bssmap_create_layer3(struct msgb *msg_l3) data[2] = CELL_IDENT_WHOLE_GLOBAL; lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); - gsm0408_generate_lai(lai, country_code, - network_code, bts->location_area_code); + gsm48_generate_lai(lai, country_code, + network_code, bts->location_area_code); ci = (u_int16_t *) msgb_put(msg, 2); *ci = htons(bts->cell_identity); diff --git a/openbsc/src/bts_ipaccess_nanobts.c b/openbsc/src/bts_ipaccess_nanobts.c new file mode 100644 index 000000000..cb48ea98a --- /dev/null +++ b/openbsc/src/bts_ipaccess_nanobts.c @@ -0,0 +1,84 @@ +/* ip.access nanoBTS specific code */ + +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <sys/types.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/tlv.h> +#include <openbsc/abis_nm.h> + +static struct gsm_bts_model model_nanobts = { + .type = GSM_BTS_TYPE_NANOBTS, + .nm_att_tlvdef = { + .def = { + /* ip.access specifics */ + [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, + [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, + [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_TV, }, + [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, + [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, + [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, + [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, + [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, + }, + }, +}; + +int bts_model_nanobts_init(void) +{ + return gsm_bts_model_register(&model_nanobts); +} diff --git a/openbsc/src/bts_siemens_bs11.c b/openbsc/src/bts_siemens_bs11.c new file mode 100644 index 000000000..c966825ee --- /dev/null +++ b/openbsc/src/bts_siemens_bs11.c @@ -0,0 +1,66 @@ +/* Siemens BS-11 specific code */ + +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <sys/types.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/tlv.h> +#include <openbsc/abis_nm.h> + +static struct gsm_bts_model model_bs11 = { + .type = GSM_BTS_TYPE_BS11, + .nm_att_tlvdef = { + .def = { + [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TLV }, + /* BS11 specifics */ + [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV }, + [0xd5] = { TLV_TYPE_TLV }, + [0xa8] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV }, + [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV }, + [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV }, + [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV }, + [0x95] = { TLV_TYPE_FIXED, 2 }, + }, + }, +}; + +int bts_model_bs11_init(void) +{ + return gsm_bts_model_register(&model_bs11); +} diff --git a/openbsc/src/bts_unknown.c b/openbsc/src/bts_unknown.c new file mode 100644 index 000000000..aac5d99c8 --- /dev/null +++ b/openbsc/src/bts_unknown.c @@ -0,0 +1,40 @@ +/* Generic BTS - VTY code tries to allocate this BTS before type is known */ + +/* (C) 2010 by Daniel Willmann <daniel@totalueberwachung.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <sys/types.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/tlv.h> +#include <openbsc/abis_nm.h> + +static struct gsm_bts_model model_unknown = { + .type = GSM_BTS_TYPE_UNKNOWN, + .nm_att_tlvdef = { + .def = { + }, + }, +}; + +int bts_model_unknown_init(void) +{ + return gsm_bts_model_register(&model_unknown); +} diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c index 27d582931..b5497bcb8 100644 --- a/openbsc/src/chan_alloc.c +++ b/openbsc/src/chan_alloc.c @@ -33,6 +33,29 @@ #include <openbsc/debug.h> #include <openbsc/signal.h> +static int ts_is_usable(struct gsm_bts_trx_ts *ts) +{ + /* FIXME: How does this behave for BS-11 ? */ + if (is_ipaccess_bts(ts->trx->bts)) { + if (!nm_is_running(&ts->nm_state)) + return 0; + } + + return 1; +} + +int trx_is_usable(struct gsm_bts_trx *trx) +{ + /* FIXME: How does this behave for BS-11 ? */ + if (is_ipaccess_bts(trx->bts)) { + if (!nm_is_running(&trx->nm_state) || + !nm_is_running(&trx->bb_transc.nm_state)) + return 0; + } + + return 1; +} + struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) { @@ -61,6 +84,9 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, llist_for_each_entry(trx, &bts->trx_list, list) { int from, to; + if (!trx_is_usable(trx)) + continue; + /* the following constraints are pure policy, * no requirement to put this restriction in place */ if (trx == bts->c0) { @@ -95,6 +121,10 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, for (j = from; j <= to; j++) { struct gsm_bts_trx_ts *ts = &trx->ts[j]; + + if (!ts_is_usable(ts)) + continue; + if (ts->pchan == GSM_PCHAN_NONE) { ts->pchan = pchan; /* set channel attribute on OML */ @@ -119,6 +149,7 @@ static const u_int8_t subslots_per_pchan[] = { [GSM_PCHAN_TCH_F] = 1, [GSM_PCHAN_TCH_H] = 2, [GSM_PCHAN_SDCCH8_SACCH8C] = 8, + /* FIXME: what about dynamic TCH_F_TCH_H ? */ }; static struct gsm_lchan * @@ -127,14 +158,20 @@ _lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan) struct gsm_bts_trx_ts *ts; int j, ss; + if (!trx_is_usable(trx)) + return NULL; + for (j = 0; j < 8; j++) { ts = &trx->ts[j]; + if (!ts_is_usable(ts)) + continue; if (ts->pchan != pchan) continue; /* check if all sub-slots are allocated yet */ for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) { struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->type == GSM_LCHAN_NONE) + if (lc->type == GSM_LCHAN_NONE && + lc->state == LCHAN_S_NONE) return lc; } } @@ -256,6 +293,8 @@ void lchan_free(struct gsm_lchan *lchan) } for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) lchan->neigh_meas[i].arfcn = 0; + lchan->silent_call = 0; + /* FIXME: ts_free() the timeslot, if we're the last logical * channel using it */ } @@ -280,7 +319,7 @@ int _lchan_release(struct gsm_lchan *lchan) LOGP(DRLL, LOGL_ERROR, "Channel count is negative: %d\n", lchan->use_count); - DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr); + DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan)); rsl_release_request(lchan, 0); return 1; } @@ -317,3 +356,51 @@ struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr) return NULL; } + +void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + int i; + + /* skip administratively deactivated tranxsceivers */ + if (!nm_is_running(&trx->nm_state) || + !nm_is_running(&trx->bb_transc.nm_state)) + continue; + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + struct load_counter *pl = &cl->pchan[ts->pchan]; + int j; + + /* skip administratively deactivated timeslots */ + if (!nm_is_running(&ts->nm_state)) + continue; + + for (j = 0; j < subslots_per_pchan[ts->pchan]; j++) { + struct gsm_lchan *lchan = &ts->lchan[j]; + + pl->total++; + + switch (lchan->state) { + case LCHAN_S_NONE: + break; + default: + pl->used++; + break; + } + } + } + } +} + +void network_chan_load(struct pchan_load *pl, struct gsm_network *net) +{ + struct gsm_bts *bts; + + memset(pl, 0, sizeof(*pl)); + + llist_for_each_entry(bts, &net->bts_list, list) + bts_chan_load(pl, bts); +} diff --git a/openbsc/src/db.c b/openbsc/src/db.c index d85386548..10c1d6d4c 100644 --- a/openbsc/src/db.c +++ b/openbsc/src/db.c @@ -23,8 +23,9 @@ #include <openbsc/gsm_data.h> #include <openbsc/gsm_04_11.h> #include <openbsc/db.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/debug.h> +#include <osmocore/statistics.h> #include <libgen.h> #include <stdio.h> @@ -117,12 +118,35 @@ static char *create_stmts[] = { "subscriber_id INTEGER NOT NULL, " "apdu BLOB " ")", + "CREATE TABLE IF NOT EXISTS Counters (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "timestamp TIMESTAMP NOT NULL, " + "value INTEGER NOT NULL, " + "name TEXT NOT NULL " + ")", + "CREATE TABLE IF NOT EXISTS AuthKeys (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "subscriber_id INTEGER UNIQUE NOT NULL, " + "algorithm_id INTEGER NOT NULL, " + "a3a8_ki BLOB " + ")", + "CREATE TABLE IF NOT EXISTS AuthTuples (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "subscriber_id NUMERIC UNIQUE NOT NULL, " + "issued TIMESTAMP NOT NULL, " + "use_count INTEGER NOT NULL DEFAULT 0, " + "key_seq INTEGER NOT NULL, " + "rand BLOB NOT NULL, " + "sres BLOB NOT NULL, " + "kc BLOB NOT NULL " + ")", }; -void db_error_func(dbi_conn conn, void* data) { - const char* msg; +void db_error_func(dbi_conn conn, void *data) +{ + const char *msg; dbi_conn_error(conn, &msg); - printf("DBI: %s\n", msg); + LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg); } static int check_db_revision(void) @@ -149,11 +173,13 @@ static int check_db_revision(void) return 0; } -int db_init(const char *name) { +int db_init(const char *name) +{ dbi_initialize(NULL); + conn = dbi_conn_new("sqlite3"); - if (conn==NULL) { - printf("DB: Failed to create connection.\n"); + if (conn == NULL) { + LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n"); return 1; } @@ -186,21 +212,23 @@ out_err: } -int db_prepare() { +int db_prepare() +{ dbi_result result; int i; for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { result = dbi_conn_query(conn, create_stmts[i]); - if (result==NULL) { - printf("DB: Failed to create some table.\n"); + if (!result) { + LOGP(DDB, LOGL_ERROR, + "Failed to create some table.\n"); return 1; } dbi_result_free(result); } if (check_db_revision() < 0) { - fprintf(stderr, "Database schema revision invalid, " + LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, " "please update your database schema\n"); return -1; } @@ -208,7 +236,8 @@ int db_prepare() { return 0; } -int db_fini() { +int db_fini() +{ dbi_conn_close(conn); dbi_shutdown(); @@ -219,10 +248,10 @@ int db_fini() { return 0; } -struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi) +struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, char *imsi) { dbi_result result; - struct gsm_subscriber* subscr; + struct gsm_subscriber *subscr; /* Is this subscriber known in the db? */ subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); @@ -230,11 +259,10 @@ struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi) result = dbi_conn_queryf(conn, "UPDATE Subscriber set updated = datetime('now') " "WHERE imsi = %s " , imsi); - if (result==NULL) { - printf("DB: failed to update timestamp\n"); - } else { + if (!result) + LOGP(DDB, LOGL_ERROR, "failed to update timestamp\n"); + else dbi_result_free(result); - } return subscr; } @@ -249,14 +277,13 @@ struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi) "(%s, datetime('now'), datetime('now')) ", imsi ); - if (result==NULL) { - printf("DB: Failed to create Subscriber by IMSI.\n"); - } + if (!result) + LOGP(DDB, LOGL_ERROR, "Failed to create Subscriber by IMSI.\n"); subscr->net = net; subscr->id = dbi_conn_sequence_last(conn, NULL); strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1); dbi_result_free(result); - printf("DB: New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi); + LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi); db_subscriber_alloc_exten(subscr); return subscr; } @@ -288,7 +315,9 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr) if (string) strncpy(equip->imei, string, sizeof(equip->imei)); - cm1 = dbi_result_get_uint(result, "classmark1") & 0xff; + string = dbi_result_get_string(result, "classmark1"); + if (string) + cm1 = atoi(string) & 0xff; equip->classmark1 = *((struct gsm48_classmark1 *) &cm1); equip->classmark2_len = dbi_result_get_field_length(result, "classmark2"); @@ -307,6 +336,214 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr) return 0; } + +int get_authinfo_by_subscr(struct gsm_auth_info *ainfo, + struct gsm_subscriber *subscr) +{ + dbi_result result; + const unsigned char *a3a8_ki; + + result = dbi_conn_queryf(conn, + "SELECT * FROM AuthKeys WHERE subscriber_id=%u", + subscr->id); + if (!result) + return -EIO; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -ENOENT; + } + + ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id"); + ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki"); + a3a8_ki = dbi_result_get_binary(result, "a3a8_ki"); + if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki)) + ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki_len); + memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len); + + dbi_result_free(result); + + return 0; +} + +int set_authinfo_for_subscr(struct gsm_auth_info *ainfo, + struct gsm_subscriber *subscr) +{ + dbi_result result; + struct gsm_auth_info ainfo_old; + int rc, upd; + unsigned char *ki_str; + + /* Deletion ? */ + if (ainfo == NULL) { + result = dbi_conn_queryf(conn, + "DELETE FROM AuthKeys WHERE subscriber_id=%u", + subscr->id); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; + } + + /* Check if already existing */ + rc = get_authinfo_by_subscr(&ainfo_old, subscr); + if (rc && rc != -ENOENT) + return rc; + upd = rc ? 0 : 1; + + /* Update / Insert */ + dbi_conn_quote_binary_copy(conn, + ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str); + + if (!upd) { + result = dbi_conn_queryf(conn, + "INSERT INTO AuthKeys " + "(subscriber_id, algorithm_id, a3a8_ki) " + "VALUES (%u, %u, %s)", + subscr->id, ainfo->auth_algo, ki_str); + } else { + result = dbi_conn_queryf(conn, + "UPDATE AuthKeys " + "SET algorithm_id=%u, a3a8_ki=%s " + "WHERE subscriber_id=%u", + ainfo->auth_algo, ki_str, subscr->id); + } + + free(ki_str); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; +} + +int get_authtuple_by_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr) +{ + dbi_result result; + int len; + const unsigned char *blob; + + result = dbi_conn_queryf(conn, + "SELECT * FROM AuthTuples WHERE subscriber_id=%u", + subscr->id); + if (!result) + return -EIO; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -ENOENT; + } + + memset(atuple, 0, sizeof(atuple)); + + atuple->use_count = dbi_result_get_ulonglong(result, "use_count"); + atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq"); + + len = dbi_result_get_field_length(result, "rand"); + if (len != sizeof(atuple->rand)) + goto err_size; + + blob = dbi_result_get_binary(result, "rand"); + memcpy(atuple->rand, blob, len); + + len = dbi_result_get_field_length(result, "sres"); + if (len != sizeof(atuple->sres)) + goto err_size; + + blob = dbi_result_get_binary(result, "sres"); + memcpy(atuple->sres, blob, len); + + len = dbi_result_get_field_length(result, "kc"); + if (len != sizeof(atuple->kc)) + goto err_size; + + blob = dbi_result_get_binary(result, "kc"); + memcpy(atuple->kc, blob, len); + + dbi_result_free(result); + + return 0; + +err_size: + dbi_result_free(result); + return -EIO; +} + +int set_authtuple_for_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr) +{ + dbi_result result; + int rc, upd; + struct gsm_auth_tuple atuple_old; + unsigned char *rand_str, *sres_str, *kc_str; + + /* Deletion ? */ + if (atuple == NULL) { + result = dbi_conn_queryf(conn, + "DELETE FROM AuthTuples WHERE subscriber_id=%u", + subscr->id); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; + } + + /* Check if already existing */ + rc = get_authtuple_by_subscr(&atuple_old, subscr); + if (rc && rc != -ENOENT) + return rc; + upd = rc ? 0 : 1; + + /* Update / Insert */ + dbi_conn_quote_binary_copy(conn, + atuple->rand, sizeof(atuple->rand), &rand_str); + dbi_conn_quote_binary_copy(conn, + atuple->sres, sizeof(atuple->sres), &sres_str); + dbi_conn_quote_binary_copy(conn, + atuple->kc, sizeof(atuple->kc), &kc_str); + + if (!upd) { + result = dbi_conn_queryf(conn, + "INSERT INTO AuthTuples " + "(subscriber_id, issued, use_count, " + "key_seq, rand, sres, kc) " + "VALUES (%u, datetime('now'), %u, " + "%u, %s, %s, %s ) ", + subscr->id, atuple->use_count, atuple->key_seq, + rand_str, sres_str, kc_str); + } else { + char *issued = atuple->key_seq == atuple_old.key_seq ? + "issued" : "datetime('now')"; + result = dbi_conn_queryf(conn, + "UPDATE AuthKeys " + "SET issued=%s, use_count=%u, " + "key_seq=%u, rand=%s, sres=%s, kc=%s " + "WHERE subscriber_id = %u", + issued, atuple->use_count, atuple->key_seq, + rand_str, sres_str, kc_str, subscr->id); + } + + free(rand_str); + free(sres_str); + free(kc_str); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; +} + #define BASE_QUERY "SELECT * FROM Subscriber " struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, enum gsm_subscriber_field field, @@ -353,15 +590,15 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, free(quoted); break; default: - printf("DB: Unknown query selector for Subscriber.\n"); + LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n"); return NULL; } - if (result==NULL) { - printf("DB: Failed to query Subscriber.\n"); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n"); return NULL; } if (!dbi_result_next_row(result)) { - printf("DB: Failed to find the Subscriber. '%u' '%s'\n", + DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n", field, id); dbi_result_free(result); return NULL; @@ -388,7 +625,7 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, subscr->lac = dbi_result_get_uint(result, "lac"); subscr->authorized = dbi_result_get_uint(result, "authorized"); - printf("DB: Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n", + DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n", subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension, subscr->lac, subscr->authorized); dbi_result_free(result); @@ -398,7 +635,8 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, return subscr; } -int db_sync_subscriber(struct gsm_subscriber* subscriber) { +int db_sync_subscriber(struct gsm_subscriber *subscriber) +{ dbi_result result; char tmsi[14]; char *q_tmsi; @@ -410,6 +648,7 @@ int db_sync_subscriber(struct gsm_subscriber* subscriber) { &q_tmsi); } else q_tmsi = strdup("NULL"); + result = dbi_conn_queryf(conn, "UPDATE Subscriber " "SET updated = datetime('now'), " @@ -424,14 +663,17 @@ int db_sync_subscriber(struct gsm_subscriber* subscriber) { subscriber->authorized, q_tmsi, subscriber->lac, - subscriber->imsi - ); + subscriber->imsi); + free(q_tmsi); - if (result==NULL) { - printf("DB: Failed to update Subscriber (by IMSI).\n"); + + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to update Subscriber (by IMSI).\n"); return 1; } + dbi_result_free(result); + return 0; } @@ -442,15 +684,15 @@ int db_sync_equipment(struct gsm_equipment *equip) u_int8_t classmark1; memcpy(&classmark1, &equip->classmark1, sizeof(classmark1)); - printf("DB: Sync Equipment IMEI=%s, classmark1=%02x", + DEBUGP(DDB, "Sync Equipment IMEI=%s, classmark1=%02x", equip->imei, classmark1); if (equip->classmark2_len) - printf(", classmark2=%s", + DEBUGPC(DDB, ", classmark2=%s", hexdump(equip->classmark2, equip->classmark2_len)); if (equip->classmark3_len) - printf(", classmark3=%s", + DEBUGPC(DDB, ", classmark3=%s", hexdump(equip->classmark3, equip->classmark3_len)); - printf("\n"); + DEBUGPC(DDB, "\n"); dbi_conn_quote_binary_copy(conn, equip->classmark2, equip->classmark2_len, &cm2); @@ -470,7 +712,7 @@ int db_sync_equipment(struct gsm_equipment *equip) free(cm3); if (!result) { - printf("DB: Failed to update Equipment\n"); + LOGP(DDB, LOGL_ERROR, "Failed to update Equipment\n"); return -EIO; } @@ -478,10 +720,12 @@ int db_sync_equipment(struct gsm_equipment *equip) return 0; } -int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) { - dbi_result result=NULL; +int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber) +{ + dbi_result result = NULL; char tmsi[14]; char* tmsi_quoted; + for (;;) { subscriber->tmsi = rand(); if (subscriber->tmsi == GSM_RESERVED_TMSI) @@ -492,20 +736,23 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) { result = dbi_conn_queryf(conn, "SELECT * FROM Subscriber " "WHERE tmsi = %s ", - tmsi_quoted - ); + tmsi_quoted); + free(tmsi_quoted); - if (result==NULL) { - printf("DB: Failed to query Subscriber while allocating new TMSI.\n"); + + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " + "while allocating new TMSI.\n"); return 1; } - if (dbi_result_get_numrows(result)){ + if (dbi_result_get_numrows(result)) { dbi_result_free(result); continue; } if (!dbi_result_next_row(result)) { dbi_result_free(result); - printf("DB: Allocated TMSI %u for IMSI %s.\n", subscriber->tmsi, subscriber->imsi); + DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n", + subscriber->tmsi, subscriber->imsi); return db_sync_subscriber(subscriber); } dbi_result_free(result); @@ -513,9 +760,11 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) { return 0; } -int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) { - dbi_result result=NULL; +int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber) +{ + dbi_result result = NULL; u_int32_t try; + for (;;) { try = (rand()%(GSM_MAX_EXTEN-GSM_MIN_EXTEN+1)+GSM_MIN_EXTEN); result = dbi_conn_queryf(conn, @@ -523,8 +772,9 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) { "WHERE extension = %i", try ); - if (result==NULL) { - printf("DB: Failed to query Subscriber while allocating new extension.\n"); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " + "while allocating new extension.\n"); return 1; } if (dbi_result_get_numrows(result)){ @@ -538,7 +788,7 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) { dbi_result_free(result); } sprintf(subscriber->extension, "%i", try); - printf("DB: Allocated extension %i for IMSI %s.\n", try, subscriber->imsi); + DEBUGP(DDB, "Allocated extension %i for IMSI %s.\n", try, subscriber->imsi); return db_sync_subscriber(subscriber); } /* @@ -547,7 +797,7 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) { * an error. */ -int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* token) +int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, u_int32_t *token) { dbi_result result; u_int32_t try; @@ -561,7 +811,8 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke "WHERE subscriber_id = %llu OR token = \"%08X\" ", subscriber->id, try); if (!result) { - printf("DB: Failed to query AuthToken while allocating new token.\n"); + LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken " + "while allocating new token.\n"); return 1; } if (dbi_result_get_numrows(result)) { @@ -581,17 +832,19 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke "(%llu, datetime('now'), \"%08X\") ", subscriber->id, try); if (!result) { - printf("DB: Failed to create token %08X for IMSI %s.\n", try, subscriber->imsi); + LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for " + "IMSI %s.\n", try, subscriber->imsi); return 1; } dbi_result_free(result); *token = try; - printf("DB: Allocated token %08X for IMSI %s.\n", try, subscriber->imsi); + DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi); return 0; } -int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IMEI_LENGTH]) { +int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IMEI_LENGTH]) +{ unsigned long long equipment_id, watch_id; dbi_result result; @@ -603,32 +856,32 @@ int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IM "(imei, created, updated) " "VALUES " "(%s, datetime('now'), datetime('now')) ", - imei - ); - if (result==NULL) { - printf("DB: Failed to create Equipment by IMEI.\n"); + imei); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n"); return 1; } + equipment_id = 0; if (dbi_result_get_numrows_affected(result)) { equipment_id = dbi_conn_sequence_last(conn, NULL); } dbi_result_free(result); - if (equipment_id) { - printf("DB: New Equipment: ID %llu, IMEI %s\n", equipment_id, imei); - } + + if (equipment_id) + DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei); else { result = dbi_conn_queryf(conn, "SELECT id FROM Equipment " "WHERE imei = %s ", imei ); - if (result==NULL) { - printf("DB: Failed to query Equipment by IMEI.\n"); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n"); return 1; } if (!dbi_result_next_row(result)) { - printf("DB: Failed to find the Equipment.\n"); + LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n"); dbi_result_free(result); return 1; } @@ -641,33 +894,33 @@ int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IM "(subscriber_id, equipment_id, created, updated) " "VALUES " "(%llu, %llu, datetime('now'), datetime('now')) ", - subscriber->id, equipment_id - ); - if (result==NULL) { - printf("DB: Failed to create EquipmentWatch.\n"); + subscriber->id, equipment_id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n"); return 1; } + watch_id = 0; - if (dbi_result_get_numrows_affected(result)) { + if (dbi_result_get_numrows_affected(result)) watch_id = dbi_conn_sequence_last(conn, NULL); - } + dbi_result_free(result); - if (watch_id) { - printf("DB: New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei); - } + if (watch_id) + DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", + equipment_id, subscriber->imsi, imei); else { result = dbi_conn_queryf(conn, "UPDATE EquipmentWatch " "SET updated = datetime('now') " "WHERE subscriber_id = %llu AND equipment_id = %llu ", - subscriber->id, equipment_id - ); - if (result==NULL) { - printf("DB: Failed to update EquipmentWatch.\n"); + subscriber->id, equipment_id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n"); return 1; } dbi_result_free(result); - printf("DB: Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei); + DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", + equipment_id, subscriber->imsi, imei); } return 0; @@ -788,6 +1041,33 @@ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id) return sms; } +struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, int min_subscr_id) +{ + dbi_result result; + struct gsm_sms *sms; + + result = dbi_conn_queryf(conn, + "SELECT * FROM SMS,Subscriber " + "WHERE sms.receiver_id >= %llu AND sms.sent is NULL " + "AND sms.receiver_id = subscriber.id " + "AND subscriber.lac > 0 " + "ORDER BY sms.receiver_id, id LIMIT 1", + min_subscr_id); + if (!result) + return NULL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return NULL; + } + + sms = sms_from_result(net, result); + + dbi_result_free(result); + + return sms; +} + /* retrieve the next unsent SMS for a given subscriber */ struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr) { @@ -826,7 +1106,7 @@ int db_sms_mark_sent(struct gsm_sms *sms) "SET sent = datetime('now') " "WHERE id = %llu", sms->id); if (!result) { - printf("DB: Failed to mark SMS %llu as sent.\n", sms->id); + LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id); return 1; } @@ -844,7 +1124,8 @@ int db_sms_inc_deliver_attempts(struct gsm_sms *sms) "SET deliver_attempts = deliver_attempts + 1 " "WHERE id = %llu", sms->id); if (!result) { - printf("DB: Failed to inc deliver attempts for SMS %llu.\n", sms->id); + LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for " + "SMS %llu.\n", sms->id); return 1; } @@ -875,3 +1156,24 @@ int db_apdu_blob_store(struct gsm_subscriber *subscr, dbi_result_free(result); return 0; } + +int db_store_counter(struct counter *ctr) +{ + dbi_result result; + char *q_name; + + dbi_conn_quote_string_copy(conn, ctr->name, &q_name); + + result = dbi_conn_queryf(conn, + "INSERT INTO Counters " + "(timestamp,name,value) VALUES " + "(datetime('now'),%s,%lu)", q_name, ctr->value); + + free(q_name); + + if (!result) + return -EIO; + + dbi_result_free(result); + return 0; +} diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c index 6e9213025..dabd14f43 100644 --- a/openbsc/src/debug.c +++ b/openbsc/src/debug.c @@ -25,23 +25,59 @@ #include <string.h> #include <strings.h> #include <time.h> +#include <errno.h> #include <openbsc/debug.h> +#include <osmocore/talloc.h> +#include <osmocore/utils.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_subscriber.h> -unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB|DMEAS); +/* default categories */ +static struct debug_category default_categories[Debug_LastEntry] = { + [DRLL] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DCC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DNM] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DRR] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DRSL] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMM] = { .enabled = 1, .loglevel = LOGL_INFO }, + [DMNCC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DSMS] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DPAG] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMEAS] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMI] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMIB] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMUX] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DINP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DSCCP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMSC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMGCP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DHO] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DDB] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DREF] = { .enabled = 0, .loglevel = LOGL_NOTICE }, +}; struct debug_info { const char *name; const char *color; const char *description; int number; + int position; +}; + +struct debug_context { + struct gsm_lchan *lchan; + struct gsm_subscriber *subscr; + struct gsm_bts *bts; }; +static struct debug_context debug_context; +static void *tall_dbg_ctx = NULL; +static LLIST_HEAD(target_list); + #define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \ { .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER }, -#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) - static const struct debug_info debug_info[] = { DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "") DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "") @@ -62,52 +98,82 @@ static const struct debug_info debug_info[] = { DEBUG_CATEGORY(DMGCP, "DMGCP", "", "") DEBUG_CATEGORY(DHO, "DHO", "", "") DEBUG_CATEGORY(DNAT, "DNAT", "", "") + DEBUG_CATEGORY(DDB, "DDB", "", "") + DEBUG_CATEGORY(DDB, "DREF", "", "") }; -static int use_color = 1; +static const struct value_string loglevel_strs[] = { + { 0, "EVERYTHING" }, + { 1, "DEBUG" }, + { 3, "INFO" }, + { 5, "NOTICE" }, + { 7, "ERROR" }, + { 8, "FATAL" }, + { 0, NULL }, +}; -void debug_use_color(int color) +int debug_parse_level(const char *lvl) { - use_color = color; + return get_string_value(loglevel_strs, lvl); } -static int print_timestamp = 0; - -void debug_timestamp(int enable) +int debug_parse_category(const char *category) { - print_timestamp = enable; -} + int i; + + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { + if (!strcasecmp(debug_info[i].name+1, category)) + return debug_info[i].number; + } + return -EINVAL; +} /* * Parse the category mask. - * category1:category2:category3 + * The format can be this: category1:category2:category3 + * or category1,2:category2,3:... */ -void debug_parse_category_mask(const char *_mask) +void debug_parse_category_mask(struct debug_target* target, const char *_mask) { - unsigned int new_mask = 0; int i = 0; char *mask = strdup(_mask); char *category_token = NULL; + /* Disable everything to enable it afterwards */ + for (i = 0; i < ARRAY_SIZE(target->categories); ++i) + target->categories[i].enabled = 0; + category_token = strtok(mask, ":"); do { for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { - if (strcasecmp(debug_info[i].name, category_token) == 0) - new_mask |= debug_info[i].number; + char* colon = strstr(category_token, ","); + int length = strlen(category_token); + + if (colon) + length = colon - category_token; + + if (strncasecmp(debug_info[i].name, category_token, length) == 0) { + int number = debug_info[i].number; + int level = 0; + + if (colon) + level = atoi(colon+1); + + target->categories[number].enabled = 1; + target->categories[number].loglevel = level; + } } } while ((category_token = strtok(NULL, ":"))); - free(mask); - debug_mask = new_mask; } -const char* color(int subsys) +static const char* color(int subsys) { int i = 0; - for (i = 0; use_color && i < ARRAY_SIZE(debug_info); ++i) { + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { if (debug_info[i].number == subsys) return debug_info[i].color; } @@ -115,35 +181,111 @@ const char* color(int subsys) return ""; } -void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) +static void _output(struct debug_target *target, unsigned int subsys, char *file, int line, + int cont, const char *format, va_list ap) { - va_list ap; - FILE *outfd = stderr; - - if (!(debug_mask & subsys)) - return; + char col[30]; + char sub[30]; + char tim[30]; + char buf[4096]; + char final[4096]; - va_start(ap, format); + /* prepare the data */ + col[0] = '\0'; + sub[0] = '\0'; + tim[0] = '\0'; + buf[0] = '\0'; - fprintf(outfd, "%s", color(subsys)); + /* are we using color */ + if (target->use_color) { + snprintf(col, sizeof(col), "%s", color(subsys)); + col[sizeof(col)-1] = '\0'; + } + vsnprintf(buf, sizeof(buf), format, ap); + buf[sizeof(buf)-1] = '\0'; if (!cont) { - if (print_timestamp) { + if (target->print_timestamp) { char *timestr; time_t tm; tm = time(NULL); timestr = ctime(&tm); timestr[strlen(timestr)-1] = '\0'; - fprintf(outfd, "%s ", timestr); + snprintf(tim, sizeof(tim), "%s ", timestr); + tim[sizeof(tim)-1] = '\0'; + } + snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line); + sub[sizeof(sub)-1] = '\0'; + } + + snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf); + final[sizeof(final)-1] = '\0'; + target->output(target, final); +} + + +static void _debugp(unsigned int subsys, int level, char *file, int line, + int cont, const char *format, va_list ap) +{ + struct debug_target *tar; + + llist_for_each_entry(tar, &target_list, entry) { + struct debug_category *category; + int output = 0; + + category = &tar->categories[subsys]; + /* subsystem is not supposed to be debugged */ + if (!category->enabled) + continue; + + /* Check the global log level */ + if (tar->loglevel != 0 && level < tar->loglevel) + continue; + + /* Check the category log level */ + if (category->loglevel != 0 && level < category->loglevel) + continue; + + /* + * Apply filters here... if that becomes messy we will need to put + * filters in a list and each filter will say stop, continue, output + */ + if ((tar->filter_map & DEBUG_FILTER_ALL) != 0) { + output = 1; + } else if ((tar->filter_map & DEBUG_FILTER_IMSI) != 0 + && debug_context.subscr && strcmp(debug_context.subscr->imsi, tar->imsi_filter) == 0) { + output = 1; + } + + if (output) { + /* FIXME: copying the va_list is an ugly workaround against a bug + * hidden somewhere in _output. If we do not copy here, the first + * call to _output() will corrupt the va_list contents, and any + * further _output() calls with the same va_list will segfault */ + va_list bp; + va_copy(bp, ap); + _output(tar, subsys, file, line, cont, format, bp); + va_end(bp); } - fprintf(outfd, "<%4.4x> %s:%d ", subsys, file, line); } - vfprintf(outfd, format, ap); - fprintf(outfd, "\033[0;m"); +} + +void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + _debugp(subsys, LOGL_DEBUG, file, line, cont, format, ap); va_end(ap); +} + +void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; - fflush(outfd); + va_start(ap, format); + _debugp(subsys, level, file, line, cont, format, ap); + va_end(ap); } static char hexd_buff[4096]; @@ -165,3 +307,122 @@ char *hexdump(const unsigned char *buf, int len) return hexd_buff; } + + +void debug_add_target(struct debug_target *target) +{ + llist_add_tail(&target->entry, &target_list); +} + +void debug_del_target(struct debug_target *target) +{ + llist_del(&target->entry); +} + +void debug_reset_context(void) +{ + memset(&debug_context, 0, sizeof(debug_context)); +} + +/* currently we are not reffing these */ +void debug_set_context(int ctx, void *value) +{ + switch (ctx) { + case BSC_CTX_LCHAN: + debug_context.lchan = (struct gsm_lchan *) value; + break; + case BSC_CTX_SUBSCR: + debug_context.subscr = (struct gsm_subscriber *) value; + break; + case BSC_CTX_BTS: + debug_context.bts = (struct gsm_bts *) value; + break; + case BSC_CTX_SCCP: + break; + default: + break; + } +} + +void debug_set_imsi_filter(struct debug_target *target, const char *imsi) +{ + if (imsi) { + target->filter_map |= DEBUG_FILTER_IMSI; + target->imsi_filter = talloc_strdup(target, imsi); + } else if (target->imsi_filter) { + target->filter_map &= ~DEBUG_FILTER_IMSI; + talloc_free(target->imsi_filter); + target->imsi_filter = NULL; + } +} + +void debug_set_all_filter(struct debug_target *target, int all) +{ + if (all) + target->filter_map |= DEBUG_FILTER_ALL; + else + target->filter_map &= ~DEBUG_FILTER_ALL; +} + +void debug_set_use_color(struct debug_target *target, int use_color) +{ + target->use_color = use_color; +} + +void debug_set_print_timestamp(struct debug_target *target, int print_timestamp) +{ + target->print_timestamp = print_timestamp; +} + +void debug_set_log_level(struct debug_target *target, int log_level) +{ + target->loglevel = log_level; +} + +void debug_set_category_filter(struct debug_target *target, int category, int enable, int level) +{ + if (category >= Debug_LastEntry) + return; + target->categories[category].enabled = !!enable; + target->categories[category].loglevel = level; +} + +static void _stderr_output(struct debug_target *target, const char *log) +{ + fprintf(target->tgt_stdout.out, "%s", log); + fflush(target->tgt_stdout.out); +} + +struct debug_target *debug_target_create(void) +{ + struct debug_target *target; + + target = talloc_zero(tall_dbg_ctx, struct debug_target); + if (!target) + return NULL; + + INIT_LLIST_HEAD(&target->entry); + memcpy(target->categories, default_categories, sizeof(default_categories)); + target->use_color = 1; + target->print_timestamp = 0; + target->loglevel = 0; + return target; +} + +struct debug_target *debug_target_create_stderr(void) +{ + struct debug_target *target; + + target = debug_target_create(); + if (!target) + return NULL; + + target->tgt_stdout.out = stderr; + target->output = _stderr_output; + return target; +} + +void debug_init(void) +{ + tall_dbg_ctx = talloc_named_const(NULL, 1, "debug"); +} diff --git a/openbsc/src/e1_config.c b/openbsc/src/e1_config.c index 6a2abd85b..50fbceccd 100644 --- a/openbsc/src/e1_config.c +++ b/openbsc/src/e1_config.c @@ -9,7 +9,7 @@ #include <openbsc/trau_mux.h> #include <openbsc/misdn.h> #include <openbsc/ipaccess.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/debug.h> #define SAPI_L2ML 0 diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c index 083d8f8de..c20359c09 100644 --- a/openbsc/src/e1_input.c +++ b/openbsc/src/e1_input.c @@ -40,18 +40,18 @@ #define PF_ISDN AF_ISDN #endif -#include <openbsc/select.h> -#include <openbsc/msgb.h> +#include <osmocore/select.h> +#include <osmocore/msgb.h> #include <openbsc/debug.h> #include <openbsc/gsm_data.h> #include <openbsc/e1_input.h> #include <openbsc/abis_nm.h> #include <openbsc/abis_rsl.h> -#include <openbsc/linuxlist.h> +#include <osmocore/linuxlist.h> #include <openbsc/subchan_demux.h> #include <openbsc/trau_frame.h> #include <openbsc/trau_mux.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/signal.h> #include <openbsc/misdn.h> @@ -234,10 +234,16 @@ int abis_rsl_sendmsg(struct msgb *msg) msg->l2h = msg->data; - if (!msg->trx || !msg->trx->rsl_link) { - LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx == NULL\n"); + if (!msg->trx) { + LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx == NULL: %s\n", + hexdump(msg->data, msg->len)); talloc_free(msg); return -EINVAL; + } else if (!msg->trx->rsl_link) { + LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx->rsl_link == NULL: %s\n", + hexdump(msg->data, msg->len)); + talloc_free(msg); + return -EIO; } sign_link = msg->trx->rsl_link; @@ -435,6 +441,8 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, "tei %d, sapi %d\n", tei, sapi); return -EINVAL; } + + debug_set_context(BSC_CTX_BTS, link->trx->bts); switch (link->type) { case E1INP_SIGN_OML: msg->trx = link->trx; diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index 0e419742e..e57ef4f59 100644 --- a/openbsc/src/gsm_04_08.c +++ b/openbsc/src/gsm_04_08.c @@ -31,12 +31,12 @@ #include <netinet/in.h> #include <openbsc/db.h> -#include <openbsc/msgb.h> -#include <openbsc/bitvec.h> -#include <openbsc/tlv.h> +#include <osmocore/msgb.h> +#include <osmocore/bitvec.h> +#include <osmocore/tlv.h> #include <openbsc/debug.h> #include <openbsc/gsm_data.h> -#include <openbsc/gsm_utils.h> +#include <osmocore/gsm_utils.h> #include <openbsc/gsm_subscriber.h> #include <openbsc/gsm_04_11.h> #include <openbsc/gsm_04_08.h> @@ -47,124 +47,14 @@ #include <openbsc/trau_frame.h> #include <openbsc/trau_mux.h> #include <openbsc/rtp_proxy.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> +#include <osmocore/gsm48.h> #include <openbsc/transaction.h> #include <openbsc/ussd.h> - -#define GSM_MAX_FACILITY 128 -#define GSM_MAX_SSVERSION 128 -#define GSM_MAX_USERUSER 128 +#include <openbsc/silent_call.h> void *tall_locop_ctx; -static const struct tlv_definition rsl_att_tlvdef = { - .def = { - [GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV }, - [GSM48_IE_NAME_LONG] = { TLV_TYPE_TLV }, - [GSM48_IE_NAME_SHORT] = { TLV_TYPE_TLV }, - [GSM48_IE_UTC] = { TLV_TYPE_TV }, - [GSM48_IE_NET_TIME_TZ] = { TLV_TYPE_FIXED, 7 }, - [GSM48_IE_LSA_IDENT] = { TLV_TYPE_TLV }, - - [GSM48_IE_BEARER_CAP] = { TLV_TYPE_TLV }, - [GSM48_IE_CAUSE] = { TLV_TYPE_TLV }, - [GSM48_IE_CC_CAP] = { TLV_TYPE_TLV }, - [GSM48_IE_ALERT] = { TLV_TYPE_TLV }, - [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_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 }, - [GSM48_IE_CALLED_SUB] = { TLV_TYPE_TLV }, - [GSM48_IE_REDIR_BCD] = { TLV_TYPE_TLV }, - [GSM48_IE_REDIR_SUB] = { TLV_TYPE_TLV }, - [GSM48_IE_LOWL_COMPAT] = { TLV_TYPE_TLV }, - [GSM48_IE_HIGHL_COMPAT] = { TLV_TYPE_TLV }, - [GSM48_IE_USER_USER] = { TLV_TYPE_TLV }, - [GSM48_IE_SS_VERS] = { TLV_TYPE_TLV }, - [GSM48_IE_MORE_DATA] = { TLV_TYPE_T }, - [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 */ - }, -}; - -static const char *rr_cause_names[] = { - [GSM48_RR_CAUSE_NORMAL] = "Normal event", - [GSM48_RR_CAUSE_ABNORMAL_UNSPEC] = "Abnormal release, unspecified", - [GSM48_RR_CAUSE_ABNORMAL_UNACCT] = "Abnormal release, channel unacceptable", - [GSM48_RR_CAUSE_ABNORMAL_TIMER] = "Abnormal release, timer expired", - [GSM48_RR_CAUSE_ABNORMAL_NOACT] = "Abnormal release, no activity on radio path", - [GSM48_RR_CAUSE_PREMPTIVE_REL] = "Preemptive release", - [GSM48_RR_CAUSE_HNDOVER_IMP] = "Handover impossible, timing advance out of range", - [GSM48_RR_CAUSE_CHAN_MODE_UNACCT] = "Channel mode unacceptable", - [GSM48_RR_CAUSE_FREQ_NOT_IMPL] = "Frequency not implemented", - [GSM48_RR_CAUSE_CALL_CLEARED] = "Call already cleared", - [GSM48_RR_CAUSE_SEMANT_INCORR] = "Semantically incorrect message", - [GSM48_RR_CAUSE_INVALID_MAND_INF] = "Invalid mandatory information", - [GSM48_RR_CAUSE_MSG_TYPE_N] = "Message type non-existant or not implemented", - [GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT] = "Message type not compatible with protocol state", - [GSM48_RR_CAUSE_COND_IE_ERROR] = "Conditional IE error", - [GSM48_RR_CAUSE_NO_CELL_ALLOC_A] = "No cell allocation available", - [GSM48_RR_CAUSE_PROT_ERROR_UNSPC] = "Protocol error unspecified", -}; - -static const char *cc_state_names[] = { - "NULL", - "INITIATED", - "illegal state 2", - "MO_CALL_PROC", - "CALL_DELIVERED", - "illegal state 5", - "CALL_PRESENT", - "CALL_RECEIVED", - "CONNECT_REQUEST", - "MO_TERM_CALL_CONF", - "ACTIVE", - "DISCONNECT_REQ", - "DISCONNECT_IND", - "illegal state 13", - "illegal state 14", - "illegal state 15", - "illegal state 16", - "illegal state 17", - "illegal state 18", - "RELEASE_REQ", - "illegal state 20", - "illegal state 21", - "illegal state 22", - "illegal state 23", - "illegal state 24", - "illegal state 25", - "MO_ORIG_MODIFY", - "MO_TERM_MODIFY", - "CONNECT_IND", - "illegal state 29", - "illegal state 30", - "illegal state 31", -}; - -static char strbuf[64]; - -static const char *rr_cause_name(u_int8_t cause) -{ - if (cause < ARRAY_SIZE(rr_cause_names) && - rr_cause_names[cause]) - return rr_cause_names[cause]; - - snprintf(strbuf, sizeof(strbuf), "0x%02x", cause); - return strbuf; -} - int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi); static int gsm48_tx_simple(struct gsm_lchan *lchan, u_int8_t pdisc, u_int8_t msg_type); @@ -280,539 +170,6 @@ static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal, return 0; } -static const char bcd_num_digits[] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '*', '#', 'a', 'b', 'c', '\0' -}; - -/* 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; - - for (i = 1 + h_len; i <= in_len; i++) { - /* lower nibble */ - output_len--; - if (output_len <= 1) - break; - *output++ = bcd_num_digits[bcd_lv[i] & 0xf]; - - /* higher nibble */ - output_len--; - if (output_len <= 1) - break; - *output++ = bcd_num_digits[bcd_lv[i] >> 4]; - } - if (output_len >= 1) - *output++ = '\0'; - - return 0; -} - -/* convert a single ASCII character to call-control BCD */ -static int asc_to_bcd(const char asc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bcd_num_digits); i++) { - if (bcd_num_digits[i] == asc) - return i; - } - return -EINVAL; -} - -/* 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, - int h_len, const char *input) -{ - int in_len = strlen(input); - int i; - u_int8_t *bcd_cur = bcd_lv + 1 + h_len; - - /* two digits per byte, plus type byte */ - bcd_lv[0] = in_len/2 + h_len; - if (in_len % 2) - bcd_lv[0]++; - - if (bcd_lv[0] > max_len) - return -EIO; - - for (i = 0; i < in_len; i++) { - int rc = asc_to_bcd(input[i]); - if (rc < 0) - return rc; - if (i % 2 == 0) - *bcd_cur = rc; - else - *bcd_cur++ |= (rc << 4); - } - /* append padding nibble in case of odd length */ - if (i % 2) - *bcd_cur++ |= 0xf0; - - /* return how many bytes we used */ - return (bcd_cur - bcd_lv); -} - -/* decode 'bearer capability' */ -static int decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap, - const u_int8_t *lv) -{ - u_int8_t in_len = lv[0]; - int i, s; - - if (in_len < 1) - return -EINVAL; - - bcap->speech_ver[0] = -1; /* end of list, of maximum 7 values */ - - /* octet 3 */ - bcap->transfer = lv[1] & 0x07; - bcap->mode = (lv[1] & 0x08) >> 3; - bcap->coding = (lv[1] & 0x10) >> 4; - bcap->radio = (lv[1] & 0x60) >> 5; - - if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) { - i = 1; - s = 0; - while(!(lv[i] & 0x80)) { - i++; /* octet 3a etc */ - if (in_len < i) - return 0; - bcap->speech_ver[s++] = lv[i] & 0x0f; - bcap->speech_ver[s] = -1; /* end of list */ - if (i == 2) /* octet 3a */ - bcap->speech_ctm = (lv[i] & 0x20) >> 5; - if (s == 7) /* maximum speech versions + end of list */ - return 0; - } - } else { - i = 1; - while (!(lv[i] & 0x80)) { - i++; /* octet 3a etc */ - if (in_len < i) - return 0; - /* ignore them */ - } - /* FIXME: implement OCTET 4+ parsing */ - } - - return 0; -} - -/* encode 'bearer capability' */ -static int encode_bearer_cap(struct msgb *msg, int lv_only, - const struct gsm_mncc_bearer_cap *bcap) -{ - u_int8_t lv[32 + 1]; - int i = 1, s; - - lv[1] = bcap->transfer; - lv[1] |= bcap->mode << 3; - lv[1] |= bcap->coding << 4; - lv[1] |= bcap->radio << 5; - - if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) { - for (s = 0; bcap->speech_ver[s] >= 0; s++) { - i++; /* octet 3a etc */ - lv[i] = bcap->speech_ver[s]; - if (i == 2) /* octet 3a */ - lv[i] |= bcap->speech_ctm << 5; - } - lv[i] |= 0x80; /* last IE of octet 3 etc */ - } else { - /* FIXME: implement OCTET 4+ encoding */ - } - - 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(struct gsm_mncc_cccap *ccap, const u_int8_t *lv) -{ - u_int8_t in_len = lv[0]; - - if (in_len < 1) - return -EINVAL; - - /* octet 3 */ - ccap->dtmf = lv[1] & 0x01; - ccap->pcp = (lv[1] & 0x02) >> 1; - - return 0; -} - -/* decode 'called party BCD number' */ -static int decode_called(struct gsm_mncc_number *called, - const u_int8_t *lv) -{ - u_int8_t in_len = lv[0]; - - if (in_len < 1) - return -EINVAL; - - /* octet 3 */ - called->plan = lv[1] & 0x0f; - called->type = (lv[1] & 0x70) >> 4; - - /* octet 4..N */ - decode_bcd_number(called->number, sizeof(called->number), lv, 1); - - return 0; -} - -/* encode 'called party BCD number' */ -static int encode_called(struct msgb *msg, - const struct gsm_mncc_number *called) -{ - u_int8_t lv[18]; - int ret; - - /* octet 3 */ - lv[1] = called->plan; - lv[1] |= called->type << 4; - - /* octet 4..N, octet 2 */ - ret = encode_bcd_number(lv, sizeof(lv), 1, called->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, - const struct gsm_mncc_number *callerid) -{ - u_int8_t lv[13]; - int h_len = 1; - int ret; - - /* octet 3 */ - lv[1] = callerid->plan; - lv[1] |= callerid->type << 4; - - if (callerid->present || callerid->screen) { - /* octet 3a */ - lv[2] = callerid->screen; - lv[2] |= callerid->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, callerid->number); - if (ret < 0) - return ret; - - msgb_tlv_put(msg, ie, lv[0], lv+1); - - return 0; -} - -/* decode 'cause' */ -static int decode_cause(struct gsm_mncc_cause *cause, - const u_int8_t *lv) -{ - u_int8_t in_len = lv[0]; - int i; - - if (in_len < 2) - return -EINVAL; - - cause->diag_len = 0; - - /* octet 3 */ - cause->location = lv[1] & 0x0f; - cause->coding = (lv[1] & 0x60) >> 5; - - i = 1; - if (!(lv[i] & 0x80)) { - i++; /* octet 3a */ - if (in_len < i+1) - return 0; - cause->rec = 1; - cause->rec_val = lv[i] & 0x7f; - - } - i++; - - /* octet 4 */ - cause->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(cause->diag, lv + i, in_len - (i-1)); - cause->diag_len = in_len - (i-1); - - return 0; -} - -/* encode 'cause' */ -static int encode_cause(struct msgb *msg, int lv_only, - const struct gsm_mncc_cause *cause) -{ - u_int8_t lv[32+4]; - int i; - - if (cause->diag_len > 32) - return -EINVAL; - - /* octet 3 */ - lv[1] = cause->location; - lv[1] |= cause->coding << 5; - - i = 1; - if (cause->rec) { - i++; /* octet 3a */ - lv[i] = cause->rec_val; - } - lv[i] |= 0x80; /* end of octet 3 */ - - /* octet 4 */ - i++; - lv[i] = 0x80 | cause->value; - - /* octet 5-N */ - if (cause->diag_len) { - memcpy(lv + i, cause->diag, cause->diag_len); - i += cause->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, - const struct gsm_mncc_number *calling) -{ - return encode_callerid(msg, GSM48_IE_CALLING_BCD, calling); -} - -/* encode 'connected number' */ -static int encode_connected(struct msgb *msg, - const struct gsm_mncc_number *connected) -{ - return encode_callerid(msg, GSM48_IE_CONN_BCD, connected); -} - -/* encode 'redirecting number' */ -static int encode_redirecting(struct msgb *msg, - const struct gsm_mncc_number *redirecting) -{ - return encode_callerid(msg, GSM48_IE_REDIR_BCD, redirecting); -} - -/* decode 'facility' */ -static int decode_facility(struct gsm_mncc_facility *facility, - const u_int8_t *lv) -{ - u_int8_t in_len = lv[0]; - - if (in_len < 1) - return -EINVAL; - - if (in_len > sizeof(facility->info)) - return -EINVAL; - - memcpy(facility->info, lv+1, in_len); - facility->len = in_len; - - return 0; -} - -/* encode 'facility' */ -static int encode_facility(struct msgb *msg, int lv_only, - const struct gsm_mncc_facility *facility) -{ - u_int8_t lv[GSM_MAX_FACILITY + 1]; - - if (facility->len < 1 || facility->len > GSM_MAX_FACILITY) - return -EINVAL; - - memcpy(lv+1, facility->info, facility->len); - lv[0] = facility->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(struct gsm_mncc_progress *progress, - const u_int8_t *lv) -{ - u_int8_t in_len = lv[0]; - - if (in_len < 2) - return -EINVAL; - - progress->coding = (lv[1] & 0x60) >> 5; - progress->location = lv[1] & 0x0f; - progress->descr = lv[2] & 0x7f; - - return 0; -} - -/* encode 'progress' */ -static int encode_progress(struct msgb *msg, int lv_only, - const struct gsm_mncc_progress *p) -{ - u_int8_t lv[3]; - - lv[0] = 2; - lv[1] = 0x80 | ((p->coding & 0x3) << 5) | (p->location & 0xf); - lv[2] = 0x80 | (p->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(struct gsm_mncc_useruser *uu, - const u_int8_t *lv) -{ - u_int8_t in_len = lv[0]; - char *info = uu->info; - int info_len = sizeof(uu->info); - int i; - - if (in_len < 1) - return -EINVAL; - - uu->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(struct msgb *msg, int lv_only, - const struct gsm_mncc_useruser *uu) -{ - u_int8_t lv[GSM_MAX_USERUSER + 2]; - - if (strlen(uu->info) > GSM_MAX_USERUSER) - return -EINVAL; - - lv[0] = 1 + strlen(uu->info); - lv[1] = uu->proto; - memcpy(lv + 2, uu->info, strlen(uu->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(struct gsm_mncc_ssversion *ssv, - const u_int8_t *lv) -{ - u_int8_t in_len = lv[0]; - - if (in_len < 1 || in_len < sizeof(ssv->info)) - return -EINVAL; - - memcpy(ssv->info, lv + 1, in_len); - ssv->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; -} - /* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */ int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause) { @@ -827,9 +184,12 @@ int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause) gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT; gh->data[0] = cause; - DEBUGP(DMM, "-> LOCATION UPDATING REJECT on channel: %d\n", lchan->nr); + LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT " + "LAC=%u BTS=%u\n", lchan->subscr ? + subscr_name(lchan->subscr) : "unknown", + lchan->ts->trx->bts->location_area_code, lchan->ts->trx->bts->nr); - bts->network->stats.loc_upd_resp.reject++; + counter_inc(bts->network->stats.loc_upd_resp.reject); return gsm48_sendmsg(msg, NULL); } @@ -850,7 +210,7 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi) gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT; lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); - gsm0408_generate_lai(lai, bts->network->country_code, + gsm48_generate_lai(lai, bts->network->country_code, bts->network->network_code, bts->location_area_code); mid = msgb_put(msg, GSM48_MID_TMSI_LEN); @@ -858,7 +218,7 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi) DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n"); - bts->network->stats.loc_upd_resp.accept++; + counter_inc(bts->network->stats.loc_upd_resp.accept); return gsm48_sendmsg(msg, NULL); } @@ -979,13 +339,13 @@ static int mm_rx_loc_upd_req(struct msgb *msg) switch (lu->type) { case GSM48_LUPD_NORMAL: - bts->network->stats.loc_upd_type.normal++; + counter_inc(bts->network->stats.loc_upd_type.normal); break; case GSM48_LUPD_IMSI_ATT: - bts->network->stats.loc_upd_type.attach++; + counter_inc(bts->network->stats.loc_upd_type.attach); break; case GSM48_LUPD_PERIODIC: - bts->network->stats.loc_upd_type.periodic++; + counter_inc(bts->network->stats.loc_upd_type.periodic); break; } @@ -1168,20 +528,19 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan) } /* Section 9.2.2 */ -int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand) +int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand, int key_seq) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); struct gsm48_auth_req *ar = (struct gsm48_auth_req *) msgb_put(msg, sizeof(*ar)); - DEBUGP(DMM, "-> AUTH REQ\n"); + DEBUGP(DMM, "-> AUTH REQ (rand = %s)\n", hexdump(rand, 16)); msg->lchan = lchan; gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_AUTH_REQ; - /* Key Sequence: FIXME fixed to 0 */ - ar->key_seq = 0; + ar->key_seq = key_seq; /* 16 bytes RAND parameters */ if (rand) @@ -1315,7 +674,7 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg) DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ", mi_type, mi_string); - bts->network->stats.loc_upd_type.detach++; + counter_inc(bts->network->stats.loc_upd_type.detach); switch (mi_type) { case GSM_MI_TYPE_TMSI: @@ -1338,8 +697,7 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg) if (subscr) { subscr_update(subscr, msg->trx->bts, GSM_SUBSCRIBER_UPDATE_DETACHED); - DEBUGP(DMM, "Subscriber: %s\n", - subscr->name ? subscr->name : subscr->imsi); + DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr)); subscr->equipment.classmark1 = idi->classmark1; db_sync_equipment(&subscr->equipment); @@ -1387,7 +745,7 @@ static int gsm0408_rcv_mm(struct msgb *msg) case GSM48_MT_MM_TMSI_REALL_COMPL: DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", msg->lchan->subscr ? - msg->lchan->subscr->imsi : + subscr_name(msg->lchan->subscr) : "unknown subscriber"); break; case GSM48_MT_MM_IMSI_DETACH_IND: @@ -1400,7 +758,7 @@ static int gsm0408_rcv_mm(struct msgb *msg) DEBUGP(DMM, "AUTHENTICATION RESPONSE: Not implemented\n"); break; default: - fprintf(stderr, "Unknown GSM 04.08 MM msg type 0x%02x\n", + LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n", gh->msg_type); break; } @@ -1603,8 +961,8 @@ static int gsm0408_rcv_rr(struct msgb *msg) rc = gsm48_rx_rr_ho_fail(msg); break; default: - fprintf(stderr, "Unimplemented GSM 04.08 RR msg type 0x%02x\n", - gh->msg_type); + LOGP(DRR, LOGL_NOTICE, "Unimplemented " + "GSM 04.08 RR msg type 0x%02x\n", gh->msg_type); break; } @@ -2085,7 +1443,7 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) memset(&setup, 0, sizeof(struct gsm_mncc)); setup.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* emergency setup is identified by msg_type */ if (msg_type == GSM48_MT_CC_EMERG_SETUP) setup.emergency = 1; @@ -2101,31 +1459,31 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { setup.fields |= MNCC_F_BEARER_CAP; - decode_bearer_cap(&setup.bearer_cap, + gsm48_decode_bearer_cap(&setup.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { setup.fields |= MNCC_F_FACILITY; - decode_facility(&setup.facility, + gsm48_decode_facility(&setup.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* called party bcd number */ if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { setup.fields |= MNCC_F_CALLED; - decode_called(&setup.called, + gsm48_decode_called(&setup.called, TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { setup.fields |= MNCC_F_USERUSER; - decode_useruser(&setup.useruser, + gsm48_decode_useruser(&setup.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { setup.fields |= MNCC_F_SSVERSION; - decode_ssversion(&setup.ssversion, + gsm48_decode_ssversion(&setup.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } /* CLIR suppression */ @@ -2137,12 +1495,16 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) /* cc cap */ if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { setup.fields |= MNCC_F_CCCAP; - decode_cccap(&setup.cccap, + gsm48_decode_cccap(&setup.cccap, TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); } new_cc_state(trans, GSM_CSTATE_INITIATED); + LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n", + subscr_name(trans->subscr), trans->subscr->extension, + setup.called.number); + /* indicate setup to MNCC */ mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_IND, &setup); @@ -2193,28 +1555,28 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) /* bearer capability */ if (setup->fields & MNCC_F_BEARER_CAP) - encode_bearer_cap(msg, 0, &setup->bearer_cap); + gsm48_encode_bearer_cap(msg, 0, &setup->bearer_cap); /* facility */ if (setup->fields & MNCC_F_FACILITY) - encode_facility(msg, 0, &setup->facility); + gsm48_encode_facility(msg, 0, &setup->facility); /* progress */ if (setup->fields & MNCC_F_PROGRESS) - encode_progress(msg, 0, &setup->progress); + gsm48_encode_progress(msg, 0, &setup->progress); /* calling party BCD number */ if (setup->fields & MNCC_F_CALLING) - encode_calling(msg, &setup->calling); + gsm48_encode_calling(msg, &setup->calling); /* called party BCD number */ if (setup->fields & MNCC_F_CALLED) - encode_called(msg, &setup->called); + gsm48_encode_called(msg, &setup->called); /* user-user */ if (setup->fields & MNCC_F_USERUSER) - encode_useruser(msg, 0, &setup->useruser); + gsm48_encode_useruser(msg, 0, &setup->useruser); /* redirecting party BCD number */ if (setup->fields & MNCC_F_REDIRECTING) - encode_redirecting(msg, &setup->redirecting); + gsm48_encode_redirecting(msg, &setup->redirecting); /* signal */ if (setup->fields & MNCC_F_SIGNAL) - encode_signal(msg, setup->signal); + gsm48_encode_signal(msg, setup->signal); new_cc_state(trans, GSM_CSTATE_CALL_PRESENT); @@ -2233,7 +1595,7 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) memset(&call_conf, 0, sizeof(struct gsm_mncc)); call_conf.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); #if 0 /* repeat */ if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR)) @@ -2244,19 +1606,19 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { call_conf.fields |= MNCC_F_BEARER_CAP; - decode_bearer_cap(&call_conf.bearer_cap, + gsm48_decode_bearer_cap(&call_conf.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); } /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { call_conf.fields |= MNCC_F_CAUSE; - decode_cause(&call_conf.cause, + gsm48_decode_cause(&call_conf.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* cc cap */ if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { call_conf.fields |= MNCC_F_CCCAP; - decode_cccap(&call_conf.cccap, + gsm48_decode_cccap(&call_conf.cccap, TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); } @@ -2278,13 +1640,13 @@ static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg) /* bearer capability */ if (proceeding->fields & MNCC_F_BEARER_CAP) - encode_bearer_cap(msg, 0, &proceeding->bearer_cap); + gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap); /* facility */ if (proceeding->fields & MNCC_F_FACILITY) - encode_facility(msg, 0, &proceeding->facility); + gsm48_encode_facility(msg, 0, &proceeding->facility); /* progress */ if (proceeding->fields & MNCC_F_PROGRESS) - encode_progress(msg, 0, &proceeding->progress); + gsm48_encode_progress(msg, 0, &proceeding->progress); return gsm48_sendmsg(msg, trans); } @@ -2301,24 +1663,24 @@ static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) memset(&alerting, 0, sizeof(struct gsm_mncc)); alerting.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { alerting.fields |= MNCC_F_FACILITY; - decode_facility(&alerting.facility, + gsm48_decode_facility(&alerting.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* progress */ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { alerting.fields |= MNCC_F_PROGRESS; - decode_progress(&alerting.progress, + gsm48_decode_progress(&alerting.progress, TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { alerting.fields |= MNCC_F_SSVERSION; - decode_ssversion(&alerting.ssversion, + gsm48_decode_ssversion(&alerting.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } @@ -2338,13 +1700,13 @@ static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) /* facility */ if (alerting->fields & MNCC_F_FACILITY) - encode_facility(msg, 0, &alerting->facility); + gsm48_encode_facility(msg, 0, &alerting->facility); /* progress */ if (alerting->fields & MNCC_F_PROGRESS) - encode_progress(msg, 0, &alerting->progress); + gsm48_encode_progress(msg, 0, &alerting->progress); /* user-user */ if (alerting->fields & MNCC_F_USERUSER) - encode_useruser(msg, 0, &alerting->useruser); + gsm48_encode_useruser(msg, 0, &alerting->useruser); new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); @@ -2360,10 +1722,10 @@ static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) gh->msg_type = GSM48_MT_CC_PROGRESS; /* progress */ - encode_progress(msg, 1, &progress->progress); + gsm48_encode_progress(msg, 1, &progress->progress); /* user-user */ if (progress->fields & MNCC_F_USERUSER) - encode_useruser(msg, 0, &progress->useruser); + gsm48_encode_useruser(msg, 0, &progress->useruser); return gsm48_sendmsg(msg, trans); } @@ -2381,16 +1743,16 @@ static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) /* facility */ if (connect->fields & MNCC_F_FACILITY) - encode_facility(msg, 0, &connect->facility); + gsm48_encode_facility(msg, 0, &connect->facility); /* progress */ if (connect->fields & MNCC_F_PROGRESS) - encode_progress(msg, 0, &connect->progress); + gsm48_encode_progress(msg, 0, &connect->progress); /* connected number */ if (connect->fields & MNCC_F_CONNECTED) - encode_connected(msg, &connect->connected); + gsm48_encode_connected(msg, &connect->connected); /* user-user */ if (connect->fields & MNCC_F_USERUSER) - encode_useruser(msg, 0, &connect->useruser); + gsm48_encode_useruser(msg, 0, &connect->useruser); new_cc_state(trans, GSM_CSTATE_CONNECT_IND); @@ -2408,7 +1770,7 @@ static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) memset(&connect, 0, sizeof(struct gsm_mncc)); connect.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* use subscriber as connected party number */ if (trans->subscr) { connect.fields |= MNCC_F_CONNECTED; @@ -2420,19 +1782,19 @@ static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { connect.fields |= MNCC_F_FACILITY; - decode_facility(&connect.facility, + gsm48_decode_facility(&connect.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { connect.fields |= MNCC_F_USERUSER; - decode_useruser(&connect.useruser, + gsm48_decode_useruser(&connect.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { connect.fields |= MNCC_F_SSVERSION; - decode_ssversion(&connect.ssversion, + gsm48_decode_ssversion(&connect.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } @@ -2481,29 +1843,29 @@ static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) memset(&disc, 0, sizeof(struct gsm_mncc)); disc.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { disc.fields |= MNCC_F_CAUSE; - decode_cause(&disc.cause, + gsm48_decode_cause(&disc.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { disc.fields |= MNCC_F_FACILITY; - decode_facility(&disc.facility, + gsm48_decode_facility(&disc.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { disc.fields |= MNCC_F_USERUSER; - decode_useruser(&disc.useruser, + gsm48_decode_useruser(&disc.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { disc.fields |= MNCC_F_SSVERSION; - decode_ssversion(&disc.ssversion, + gsm48_decode_ssversion(&disc.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } @@ -2534,19 +1896,19 @@ static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) /* cause */ if (disc->fields & MNCC_F_CAUSE) - encode_cause(msg, 1, &disc->cause); + gsm48_encode_cause(msg, 1, &disc->cause); else - encode_cause(msg, 1, &default_cause); + gsm48_encode_cause(msg, 1, &default_cause); /* facility */ if (disc->fields & MNCC_F_FACILITY) - encode_facility(msg, 0, &disc->facility); + gsm48_encode_facility(msg, 0, &disc->facility); /* progress */ if (disc->fields & MNCC_F_PROGRESS) - encode_progress(msg, 0, &disc->progress); + gsm48_encode_progress(msg, 0, &disc->progress); /* user-user */ if (disc->fields & MNCC_F_USERUSER) - encode_useruser(msg, 0, &disc->useruser); + gsm48_encode_useruser(msg, 0, &disc->useruser); /* store disconnect cause for T306 expiry */ memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc)); @@ -2568,29 +1930,29 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) memset(&rel, 0, sizeof(struct gsm_mncc)); rel.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { rel.fields |= MNCC_F_CAUSE; - decode_cause(&rel.cause, + gsm48_decode_cause(&rel.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { rel.fields |= MNCC_F_FACILITY; - decode_facility(&rel.facility, + gsm48_decode_facility(&rel.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { rel.fields |= MNCC_F_USERUSER; - decode_useruser(&rel.useruser, + gsm48_decode_useruser(&rel.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { rel.fields |= MNCC_F_SSVERSION; - decode_ssversion(&rel.ssversion, + gsm48_decode_ssversion(&rel.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } @@ -2627,13 +1989,13 @@ static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) /* cause */ if (rel->fields & MNCC_F_CAUSE) - encode_cause(msg, 0, &rel->cause); + gsm48_encode_cause(msg, 0, &rel->cause); /* facility */ if (rel->fields & MNCC_F_FACILITY) - encode_facility(msg, 0, &rel->facility); + gsm48_encode_facility(msg, 0, &rel->facility); /* user-user */ if (rel->fields & MNCC_F_USERUSER) - encode_useruser(msg, 0, &rel->useruser); + gsm48_encode_useruser(msg, 0, &rel->useruser); trans->cc.T308_second = 0; memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc)); @@ -2656,29 +2018,29 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) memset(&rel, 0, sizeof(struct gsm_mncc)); rel.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { rel.fields |= MNCC_F_CAUSE; - decode_cause(&rel.cause, + gsm48_decode_cause(&rel.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { rel.fields |= MNCC_F_FACILITY; - decode_facility(&rel.facility, + gsm48_decode_facility(&rel.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { rel.fields |= MNCC_F_USERUSER; - decode_useruser(&rel.useruser, + gsm48_decode_useruser(&rel.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { rel.fields |= MNCC_F_SSVERSION; - decode_ssversion(&rel.ssversion, + gsm48_decode_ssversion(&rel.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } @@ -2720,13 +2082,13 @@ static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) /* cause */ if (rel->fields & MNCC_F_CAUSE) - encode_cause(msg, 0, &rel->cause); + gsm48_encode_cause(msg, 0, &rel->cause); /* facility */ if (rel->fields & MNCC_F_FACILITY) - encode_facility(msg, 0, &rel->facility); + gsm48_encode_facility(msg, 0, &rel->facility); /* user-user */ if (rel->fields & MNCC_F_USERUSER) - encode_useruser(msg, 0, &rel->useruser); + gsm48_encode_useruser(msg, 0, &rel->useruser); trans_free(trans); @@ -2742,17 +2104,17 @@ static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) memset(&fac, 0, sizeof(struct gsm_mncc)); fac.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { fac.fields |= MNCC_F_FACILITY; - decode_facility(&fac.facility, + gsm48_decode_facility(&fac.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { fac.fields |= MNCC_F_SSVERSION; - decode_ssversion(&fac.ssversion, + gsm48_decode_ssversion(&fac.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } @@ -2768,7 +2130,7 @@ static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) gh->msg_type = GSM48_MT_CC_FACILITY; /* facility */ - encode_facility(msg, 1, &fac->facility); + gsm48_encode_facility(msg, 1, &fac->facility); return gsm48_sendmsg(msg, trans); } @@ -2802,9 +2164,9 @@ static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) /* cause */ if (hold_rej->fields & MNCC_F_CAUSE) - encode_cause(msg, 1, &hold_rej->cause); + gsm48_encode_cause(msg, 1, &hold_rej->cause); else - encode_cause(msg, 1, &default_cause); + gsm48_encode_cause(msg, 1, &default_cause); return gsm48_sendmsg(msg, trans); } @@ -2839,9 +2201,9 @@ static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) /* cause */ if (retrieve_rej->fields & MNCC_F_CAUSE) - encode_cause(msg, 1, &retrieve_rej->cause); + gsm48_encode_cause(msg, 1, &retrieve_rej->cause); else - encode_cause(msg, 1, &default_cause); + gsm48_encode_cause(msg, 1, &default_cause); return gsm48_sendmsg(msg, trans); } @@ -2855,11 +2217,11 @@ static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) memset(&dtmf, 0, sizeof(struct gsm_mncc)); dtmf.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* keypad facility */ if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { dtmf.fields |= MNCC_F_KEYPAD; - decode_keypad(&dtmf.keypad, + gsm48_decode_keypad(&dtmf.keypad, TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); } @@ -2876,7 +2238,7 @@ static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) /* keypad */ if (dtmf->fields & MNCC_F_KEYPAD) - encode_keypad(msg, dtmf->keypad); + gsm48_encode_keypad(msg, dtmf->keypad); return gsm48_sendmsg(msg, trans); } @@ -2891,9 +2253,9 @@ static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) /* cause */ if (dtmf->fields & MNCC_F_CAUSE) - encode_cause(msg, 1, &dtmf->cause); + gsm48_encode_cause(msg, 1, &dtmf->cause); else - encode_cause(msg, 1, &default_cause); + gsm48_encode_cause(msg, 1, &default_cause); return gsm48_sendmsg(msg, trans); } @@ -2927,11 +2289,11 @@ static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) memset(&modify, 0, sizeof(struct gsm_mncc)); modify.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { modify.fields |= MNCC_F_BEARER_CAP; - decode_bearer_cap(&modify.bearer_cap, + gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); } @@ -2951,7 +2313,7 @@ static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) gsm48_start_cc_timer(trans, 0x323, GSM48_T323); /* bearer capability */ - encode_bearer_cap(msg, 1, &modify->bearer_cap); + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); @@ -2969,11 +2331,11 @@ static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg memset(&modify, 0, sizeof(struct gsm_mncc)); modify.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { modify.fields |= MNCC_F_BEARER_CAP; - decode_bearer_cap(&modify.bearer_cap, + gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); } @@ -2991,7 +2353,7 @@ static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; /* bearer capability */ - encode_bearer_cap(msg, 1, &modify->bearer_cap); + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); new_cc_state(trans, GSM_CSTATE_ACTIVE); @@ -3009,17 +2371,17 @@ static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) memset(&modify, 0, sizeof(struct gsm_mncc)); modify.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { modify.fields |= GSM48_IE_BEARER_CAP; - decode_bearer_cap(&modify.bearer_cap, + gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); } /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { modify.fields |= MNCC_F_CAUSE; - decode_cause(&modify.cause, + gsm48_decode_cause(&modify.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } @@ -3037,9 +2399,9 @@ static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; /* bearer capability */ - encode_bearer_cap(msg, 1, &modify->bearer_cap); + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); /* cause */ - encode_cause(msg, 1, &modify->cause); + gsm48_encode_cause(msg, 1, &modify->cause); new_cc_state(trans, GSM_CSTATE_ACTIVE); @@ -3055,7 +2417,7 @@ static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) gh->msg_type = GSM48_MT_CC_NOTIFY; /* notify */ - encode_notify(msg, notify->notify); + gsm48_encode_notify(msg, notify->notify); return gsm48_sendmsg(msg, trans); } @@ -3069,9 +2431,9 @@ static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) memset(¬ify, 0, sizeof(struct gsm_mncc)); notify.callref = trans->callref; -// tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len); +// tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len); if (payload_len >= 1) - decode_notify(¬ify.notify, gh->data); + gsm48_decode_notify(¬ify.notify, gh->data); return mncc_recvmsg(trans->subscr->net, trans, MNCC_NOTIFY_IND, ¬ify); } @@ -3086,10 +2448,10 @@ static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) /* user-user */ if (user->fields & MNCC_F_USERUSER) - encode_useruser(msg, 1, &user->useruser); + gsm48_encode_useruser(msg, 1, &user->useruser); /* more data */ if (user->more) - encode_more(msg); + gsm48_encode_more(msg); return gsm48_sendmsg(msg, trans); } @@ -3103,11 +2465,11 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) memset(&user, 0, sizeof(struct gsm_mncc)); user.callref = trans->callref; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { user.fields |= MNCC_F_USERUSER; - decode_useruser(&user.useruser, + gsm48_decode_useruser(&user.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* more data */ @@ -3445,7 +2807,7 @@ static int gsm0408_rcv_cc(struct msgb *msg) "Received '%s' from MS in state %d (%s)\n", lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, transaction_id, (lchan->subscr)?(lchan->subscr->extension):"-", - gsm0408_cc_msg_names[msg_type], trans?(trans->cc.state):0, + gsm48_cc_msg_names[msg_type], trans?(trans->cc.state):0, cc_state_names[trans?(trans->cc.state):0]); /* Create transaction */ @@ -3488,6 +2850,9 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id) struct gsm48_hdr *gh = msgb_l3(msg); u_int8_t pdisc = gh->proto_discr & 0x0f; int rc = 0; + + if (silent_call_reroute(msg)) + return silent_call_rx(msg); switch (pdisc) { case GSM48_PDISC_CC: @@ -3504,15 +2869,15 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id) break; case GSM48_PDISC_MM_GPRS: case GSM48_PDISC_SM_GPRS: - fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02x\n", - pdisc); + LOGP(DRLL, LOGL_NOTICE, "Unimplemented " + "GSM 04.08 discriminator 0x%02x\n", pdisc); break; case GSM48_PDISC_NC_SS: rc = handle_rcv_ussd(msg); break; default: - fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02x\n", - pdisc); + LOGP(DRLL, LOGL_NOTICE, "Unknown " + "GSM 04.08 discriminator 0x%02x\n", pdisc); break; } diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c index 9156a3909..68f34f409 100644 --- a/openbsc/src/gsm_04_08_utils.c +++ b/openbsc/src/gsm_04_08_utils.c @@ -28,7 +28,8 @@ #include <errno.h> #include <netinet/in.h> -#include <openbsc/msgb.h> +#include <osmocore/msgb.h> +#include <osmocore/gsm48.h> #include <openbsc/debug.h> #include <openbsc/gsm_04_08.h> #include <openbsc/transaction.h> @@ -42,75 +43,6 @@ * or should OpenBSC always act as RTP relay/proxy in between (0) ? */ int ipacc_rtp_direct = 1; - -const char *gsm0408_cc_msg_names[] = { - "unknown 0x00", - "ALERTING", - "CALL_PROC", - "PROGRESS", - "ESTAB", - "SETUP", - "ESTAB_CONF", - "CONNECT", - "CALL_CONF", - "START_CC", - "unknown 0x0a", - "RECALL", - "unknown 0x0c", - "unknown 0x0d", - "EMERG_SETUP", - "CONNECT_ACK", - "USER_INFO", - "unknown 0x11", - "unknown 0x12", - "MODIFY_REJECT", - "unknown 0x14", - "unknown 0x15", - "unknown 0x16", - "MODIFY", - "HOLD", - "HOLD_ACK", - "HOLD_REJ", - "unknown 0x1b", - "RETR", - "RETR_ACK", - "RETR_REJ", - "MODIFY_COMPL", - "unknown 0x20", - "unknown 0x21", - "unknown 0x22", - "unknown 0x23", - "unknown 0x24", - "DISCONNECT", - "unknown 0x26", - "unknown 0x27", - "unknown 0x28", - "unknown 0x29", - "RELEASE_COMPL", - "unknown 0x2b", - "unknown 0x2c", - "RELEASE", - "unknown 0x2e", - "unknown 0x2f", - "unknown 0x30", - "STOP_DTMF", - "STOP_DTMF_ACK", - "unknown 0x33", - "STATUS_ENQ", - "START_DTMF", - "START_DTMF_ACK", - "START_DTMF_REJ", - "unknown 0x38", - "CONG_CTRL", - "FACILITY", - "unknown 0x3b", - "STATUS", - "unknown 0x3c", - "NOTIFY", - "unknown 0x3f", -}; - - struct msgb *gsm48_msgb_alloc(void) { return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM, @@ -136,7 +68,7 @@ int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans) "Sending '%s' to MS.\n", msg->trx->bts->nr, msg->trx->nr, msg->lchan->ts->nr, gh->proto_discr & 0xf0, - gsm0408_cc_msg_names[gh->msg_type & 0x3f]); + gsm48_cc_msg_names[gh->msg_type & 0x3f]); else DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) " "Sending 0x%02x to MS.\n", msg->trx->bts->nr, @@ -149,94 +81,6 @@ int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans) return rsl_data_request(msg, 0); } -static void to_bcd(u_int8_t *bcd, u_int16_t val) -{ - bcd[2] = val % 10; - val = val / 10; - bcd[1] = val % 10; - val = val / 10; - bcd[0] = val % 10; - val = val / 10; -} - -static char bcd2char(u_int8_t bcd) -{ - if (bcd < 0xa) - return '0' + bcd; - else - return 'A' + (bcd - 0xa); -} - -/* only works for numbers in ascci */ -static u_int8_t char2bcd(char c) -{ - return c - 0x30; -} - - -void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc, - u_int16_t mnc, u_int16_t lac) -{ - u_int8_t bcd[3]; - - to_bcd(bcd, mcc); - lai48->digits[0] = bcd[0] | (bcd[1] << 4); - lai48->digits[1] = bcd[2]; - - to_bcd(bcd, mnc); - /* FIXME: do we need three-digit MNC? See Table 10.5.3 */ -#if 0 - lai48->digits[1] |= bcd[2] << 4; - lai48->digits[2] = bcd[0] | (bcd[1] << 4); -#else - lai48->digits[1] |= 0xf << 4; - lai48->digits[2] = bcd[1] | (bcd[2] << 4); -#endif - - lai48->lac = htons(lac); -} - -int gsm48_generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi) -{ - u_int32_t *tptr = (u_int32_t *) &buf[3]; - - buf[0] = GSM48_IE_MOBILE_ID; - buf[1] = GSM48_TMSI_LEN; - buf[2] = 0xf0 | GSM_MI_TYPE_TMSI; - *tptr = htonl(tmsi); - - return 7; -} - -int gsm48_generate_mid_from_imsi(u_int8_t *buf, const char *imsi) -{ - unsigned int length = strlen(imsi), i, off = 0; - u_int8_t odd = (length & 0x1) == 1; - - buf[0] = GSM48_IE_MOBILE_ID; - buf[2] = char2bcd(imsi[0]) << 4 | GSM_MI_TYPE_IMSI | (odd << 3); - - /* if the length is even we will fill half of the last octet */ - if (odd) - buf[1] = (length + 1) >> 1; - else - buf[1] = (length + 2) >> 1; - - for (i = 1; i < buf[1]; ++i) { - u_int8_t lower, upper; - - lower = char2bcd(imsi[++off]); - if (!odd && off + 1 == length) - upper = 0x0f; - else - upper = char2bcd(imsi[++off]) & 0x0f; - - buf[2 + i] = (upper << 4) | lower; - } - - return 2 + buf[1]; -} - /* Section 9.1.8 / Table 9.9 */ struct chreq { u_int8_t val; @@ -340,7 +184,7 @@ enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci) if ((ra & chr->mask) == chr->val) return ctype_by_chreq[chr->type]; } - fprintf(stderr, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra); + LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra); return GSM_LCHAN_SDCCH; } @@ -363,7 +207,7 @@ enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, in if ((ra & chr->mask) == chr->val) return reason_by_chreq[chr->type]; } - fprintf(stderr, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra); + LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra); return GSM_CHREQ_REASON_OTHER; } @@ -475,7 +319,7 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr) if (!msg->lchan->subscr) { msg->lchan->subscr = subscr; } else if (msg->lchan->subscr != subscr) { - DEBUGP(DRR, "<- Channel already owned by someone else?\n"); + LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n"); subscr_put(subscr); return -EINVAL; } else { @@ -490,7 +334,7 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr) bts->network->stats.paging.completed++; - dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data); + dispatch_signal(SS_PAGING, S_PAGING_SUCCEEDED, &sig_data); /* Stop paging on the bts we received the paging response */ paging_request_stop(msg->trx->bts, subscr, msg->lchan); @@ -596,7 +440,8 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, /* in case of multi rate we need to attach a config */ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { if (lchan->mr_conf.ver == 0) { - DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n"); + LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " + "without multirate config.\n"); } else { u_int8_t *data = msgb_put(msg, 4); data[0] = GSM48_IE_MUL_RATE_CFG; @@ -636,7 +481,8 @@ int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode) /* in case of multi rate we need to attach a config */ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { if (lchan->mr_conf.ver == 0) { - DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n"); + LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " + "without multirate config.\n"); } else { u_int8_t *data = msgb_put(msg, 4); data[0] = GSM48_IE_MUL_RATE_CFG; @@ -669,7 +515,7 @@ int gsm48_rx_rr_modif_ack(struct msgb *msg) DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n"); if (mod->mode != msg->lchan->tch_mode) { - DEBUGP(DRR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n", + LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n", msg->lchan->tch_mode, mod->mode); return -1; } @@ -710,6 +556,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg) u_int8_t *data = gh->data; struct gsm_bts *bts = msg->lchan->ts->trx->bts; struct bitvec *nbv = &bts->si_common.neigh_list; + struct gsm_meas_rep_cell *mrc; if (gh->msg_type != GSM48_MT_RR_MEAS_REP) return -EINVAL; @@ -731,42 +578,51 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg) return 0; /* an encoding nightmare in perfection */ - - rep->cell[0].rxlev = data[3] & 0x3f; - rep->cell[0].arfcn = bitvec_get_nth_set_bit(nbv, data[4] >> 2); - rep->cell[0].bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5); + mrc = &rep->cell[0]; + mrc->rxlev = data[3] & 0x3f; + mrc->neigh_idx = data[4] >> 3; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5); if (rep->num_cell < 2) return 0; - rep->cell[1].rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7); - rep->cell[1].arfcn = bitvec_get_nth_set_bit(nbv, (data[6] >> 2) & 0x1f); - rep->cell[1].bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4); + mrc = &rep->cell[1]; + mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7); + mrc->neigh_idx = (data[6] >> 2) & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4); if (rep->num_cell < 3) return 0; - rep->cell[2].rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6); - rep->cell[2].arfcn = bitvec_get_nth_set_bit(nbv, (data[8] >> 1) & 0x1f); - rep->cell[2].bsic = ((data[8] & 0x01) << 6) | (data[9] >> 3); + mrc = &rep->cell[2]; + mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6); + mrc->neigh_idx = (data[8] >> 1) & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3); if (rep->num_cell < 4) return 0; - rep->cell[3].rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5); - rep->cell[3].arfcn = bitvec_get_nth_set_bit(nbv, data[10] & 0x1f); - rep->cell[3].bsic = data[11] >> 2; + mrc = &rep->cell[3]; + mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5); + mrc->neigh_idx = data[10] & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = data[11] >> 2; if (rep->num_cell < 5) return 0; - rep->cell[4].rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4); - rep->cell[4].arfcn = bitvec_get_nth_set_bit(nbv, - ((data[12] & 0xf) << 1) | (data[13] >> 7)); - rep->cell[4].bsic = (data[13] >> 1) & 0x3f; + mrc = &rep->cell[4]; + mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4); + mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7); + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = (data[13] >> 1) & 0x3f; if (rep->num_cell < 6) return 0; - rep->cell[5].rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3); - rep->cell[5].arfcn = bitvec_get_nth_set_bit(nbv, - ((data[14] & 0x07) << 2) | (data[15] >> 6)); - rep->cell[5].bsic = data[15] & 0x3f; + mrc = &rep->cell[5]; + mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3); + mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6); + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = data[15] & 0x3f; return 0; } diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c index 579bb55d1..881c3755a 100644 --- a/openbsc/src/gsm_04_11.c +++ b/openbsc/src/gsm_04_11.c @@ -31,18 +31,18 @@ #include <time.h> #include <netinet/in.h> -#include <openbsc/msgb.h> -#include <openbsc/tlv.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> #include <openbsc/debug.h> #include <openbsc/gsm_data.h> #include <openbsc/gsm_subscriber.h> #include <openbsc/gsm_04_11.h> #include <openbsc/gsm_04_08.h> -#include <openbsc/gsm_utils.h> +#include <osmocore/gsm_utils.h> #include <openbsc/abis_rsl.h> #include <openbsc/signal.h> #include <openbsc/db.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/transaction.h> #include <openbsc/paging.h> #include <openbsc/bsc_rll.h> @@ -215,7 +215,8 @@ 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); + LOGP(DSMS, LOGL_ERROR, + "unbcdify got too big nibble: 0x%02X\n", value); ret = (value&0x0F)*10; ret += value>>4; @@ -311,7 +312,8 @@ static unsigned long gsm340_vp_relative_integer(u_int8_t *sms_vp) unsigned long minutes; vp = *(sms_vp); if (vp == 0) { - DEBUGP(DSMS, "reserved relative_integer validity period\n"); + LOGP(DSMS, LOGL_ERROR, + "reserved relative_integer validity period\n"); return gsm340_vp_default(); } minutes = vp/60; @@ -356,7 +358,8 @@ static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *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"); + LOGP(DSMS, LOGL_ERROR, + "Reserved enhanced validity period format\n"); return gsm340_vp_default(); } case GSM340_TP_VPF_NONE: @@ -373,7 +376,8 @@ enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs) if ((cgbits & 0xc) == 0) { if (cgbits & 2) - DEBUGP(DSMS, "Compressed SMS not supported yet\n"); + LOGP(DSMS, LOGL_NOTICE, + "Compressed SMS not supported yet\n"); switch ((dcs >> 2)&0x03) { case 0: @@ -403,7 +407,7 @@ enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs) static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms) { if (db_sms_store(gsms) != 0) { - DEBUGP(DSMS, "Failed to store SMS in Database\n"); + LOGP(DSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; } /* dispatch a signal to tell higher level about it */ @@ -422,7 +426,7 @@ static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len, oa[1] = 0xb9; /* networks-specific number, private numbering plan */ - len_in_bytes = encode_bcd_number(oa, oa_len, 1, subscr->extension); + len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, subscr->extension); /* GSM 03.40 tells us the length is in 'useful semi-octets' */ oa[0] = strlen(subscr->extension) & 0xff; @@ -497,7 +501,8 @@ static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms) memcpy(smsp, sms->user_data, sms->user_data_len); break; default: - DEBUGP(DSMS, "Unhandled Data Coding Scheme: 0x%02X\n", sms->data_coding_scheme); + LOGP(DSMS, LOGL_NOTICE, "Unhandled Data Coding Scheme: 0x%02X\n", + sms->data_coding_scheme); break; } @@ -517,7 +522,7 @@ static int gsm340_rx_tpdu(struct msgb *msg) u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */ int rc = 0; - bts->network->stats.sms.submitted++; + counter_inc(bts->network->stats.sms.submitted); gsms = sms_alloc(); if (!gsms) @@ -537,7 +542,7 @@ static int gsm340_rx_tpdu(struct msgb *msg) /* length in bytes of the destination address */ da_len_bytes = 2 + *smsp/2 + *smsp%2; if (da_len_bytes > 12) { - DEBUGP(DSMS, "Destination Address > 12 bytes ?!?\n"); + LOGP(DSMS, LOGL_ERROR, "Destination Address > 12 bytes ?!?\n"); rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; goto out; } @@ -546,7 +551,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 - 1; /* convert to real number */ - decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_addr), address_lv, 1); + gsm48_decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_addr), address_lv, 1); smsp += da_len_bytes; gsms->protocol_id = *smsp++; @@ -570,8 +575,8 @@ static int gsm340_rx_tpdu(struct msgb *msg) sms_vp = 0; break; default: - DEBUGP(DSMS, "SMS Validity period not implemented: 0x%02x\n", - sms_vpf); + LOGP(DSMS, LOGL_NOTICE, + "SMS Validity period not implemented: 0x%02x\n", sms_vpf); return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; } gsms->user_data_len = *smsp++; @@ -589,16 +594,17 @@ static int gsm340_rx_tpdu(struct msgb *msg) } } - DEBUGP(DSMS, "SMS:\nMTI: 0x%02x, VPF: 0x%02x, MR: 0x%02x " - "PID: 0x%02x, DCS: 0x%02x, DA: %s, UserDataLength: 0x%02x " - "UserData: \"%s\"\n", sms_mti, sms_vpf, gsms->msg_ref, - gsms->protocol_id, gsms->data_coding_scheme, - gsms->dest_addr, gsms->user_data_len, + gsms->sender = subscr_get(msg->lchan->subscr); + + LOGP(DSMS, LOGL_INFO, "RX SMS: Sender: %s, MTI: 0x%02x, VPF: 0x%02x, " + "MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, " + "UserDataLength: 0x%02x, UserData: \"%s\"\n", + subscr_name(gsms->sender), sms_mti, sms_vpf, gsms->msg_ref, + gsms->protocol_id, gsms->data_coding_scheme, gsms->dest_addr, + gsms->user_data_len, sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text : hexdump(gsms->user_data, gsms->user_data_len)); - gsms->sender = subscr_get(msg->lchan->subscr); - gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp); dispatch_signal(SS_SMS, 0, gsms); @@ -607,7 +613,7 @@ static int gsm340_rx_tpdu(struct msgb *msg) gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr); if (!gsms->receiver) { rc = 1; /* cause 1: unknown subscriber */ - bts->network->stats.sms.no_receiver++; + counter_inc(bts->network->stats.sms.no_receiver); goto out; } @@ -618,11 +624,11 @@ static int gsm340_rx_tpdu(struct msgb *msg) break; case GSM340_SMS_COMMAND_MS2SC: case GSM340_SMS_DELIVER_REP_MS2SC: - DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms_mti); + LOGP(DSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti); rc = GSM411_RP_CAUSE_IE_NOTEXIST; break; default: - DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms_mti); + LOGP(DSMS, LOGL_NOTICE, "Undefined MTI 0x%02x\n", sms_mti); rc = GSM411_RP_CAUSE_IE_NOTEXIST; break; } @@ -652,7 +658,7 @@ static int gsm411_send_rp_error(struct gsm_trans *trans, msgb_tv_put(msg, 1, cause); - DEBUGP(DSMS, "TX: SMS RP ERROR, cause %d (%s)\n", cause, + LOGP(DSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause, get_value_string(rp_cause_strs, cause)); return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ERROR_MT, msg_ref); @@ -668,10 +674,11 @@ static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans, int rc = 0; if (src_len && src) - DEBUGP(DSMS, "RP-DATA (MO) with SRC ?!?\n"); + LOGP(DSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n"); if (!dst_len || !dst || !tpdu_len || !tpdu) { - DEBUGP(DSMS, "RP-DATA (MO) without DST or TPDU ?!?\n"); + LOGP(DSMS, LOGL_ERROR, + "RP-DATA (MO) without DST or TPDU ?!?\n"); gsm411_send_rp_error(trans, rph->msg_ref, GSM411_RP_CAUSE_INV_MAND_INF); return -EIO; @@ -726,13 +733,13 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans, * transmitted */ if (!trans->sms.is_mt) { - DEBUGP(DSMS, "RX RP-ACK on a MO transfer ?\n"); + LOGP(DSMS, LOGL_ERROR, "RX RP-ACK on a MO transfer ?\n"); return gsm411_send_rp_error(trans, rph->msg_ref, GSM411_RP_CAUSE_MSG_INCOMP_STATE); } if (!sms) { - DEBUGP(DSMS, "RX RP-ACK but no sms in transaction?!?\n"); + LOGP(DSMS, LOGL_ERROR, "RX RP-ACK but no sms in transaction?!?\n"); return gsm411_send_rp_error(trans, rph->msg_ref, GSM411_RP_CAUSE_PROTOCOL_ERR); } @@ -745,14 +752,16 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans, sms_free(sms); trans->sms.sms = NULL; - /* free the transaction here */ - trans_free(trans); - /* check for more messages for this subscriber */ sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr); if (sms) gsm411_send_sms_lchan(msg->lchan, sms); - else + + /* free the transaction here */ + trans_free(trans); + + /* release channel if done */ + if (!sms) rsl_release_request(msg->lchan, trans->sms.link_id); return 0; @@ -761,6 +770,7 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans, static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, struct gsm411_rp_hdr *rph) { + struct gsm_network *net = trans->lchan->ts->trx->bts->network; struct gsm_sms *sms = trans->sms.sms; u_int8_t cause_len = rph->data[0]; u_int8_t cause = rph->data[1]; @@ -769,11 +779,12 @@ 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:%d (%s)\n", cause_len, cause, - get_value_string(rp_cause_strs, cause)); + LOGP(DSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n", + subscr_name(msg->lchan->subscr), cause_len, cause, + get_value_string(rp_cause_strs, cause)); if (!trans->sms.is_mt) { - DEBUGP(DSMS, "RX RP-ERR on a MO transfer ?\n"); + LOGP(DSMS, LOGL_ERROR, "RX RP-ERR on a MO transfer ?\n"); #if 0 return gsm411_send_rp_error(trans, rph->msg_ref, GSM411_RP_CAUSE_MSG_INCOMP_STATE); @@ -781,7 +792,8 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, } if (!sms) { - DEBUGP(DSMS, "RX RP-ERR, but no sms in transaction?!?\n"); + LOGP(DSMS, LOGL_ERROR, + "RX RP-ERR, but no sms in transaction?!?\n"); return -EINVAL; #if 0 return gsm411_send_rp_error(trans, rph->msg_ref, @@ -794,9 +806,9 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, * to store this in our database and wati for a SMMA message */ /* FIXME */ dispatch_signal(SS_SMS, S_SMS_MEM_EXCEEDED, trans->subscr); - trans->lchan->ts->trx->bts->network->stats.sms.rp_err_mem++; + counter_inc(net->stats.sms.rp_err_mem); } else - trans->lchan->ts->trx->bts->network->stats.sms.rp_err_other++; + counter_inc(net->stats.sms.rp_err_other); sms_free(sms); trans->sms.sms = NULL; @@ -858,7 +870,7 @@ static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh, rc = gsm411_rx_rp_error(msg, trans, rp_data); break; default: - DEBUGP(DSMS, "Invalid RP type 0x%02x\n", msg_type); + LOGP(DSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); rc = gsm411_send_rp_error(trans, rp_data->msg_ref, GSM411_RP_CAUSE_MSGTYPE_NOTEXIST); break; @@ -889,7 +901,7 @@ static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause) struct msgb *msg = gsm411_msgb_alloc(); u_int8_t *causep; - DEBUGP(DSMS, "TX CP-ERROR, cause %d (%s)\n", cause, + LOGP(DSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause, get_value_string(cp_cause_strs, cause)); causep = msgb_put(msg, 1); @@ -936,6 +948,33 @@ int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id) switch(msg_type) { case GSM411_MT_CP_DATA: DEBUGPC(DSMS, "RX SMS CP-DATA\n"); + + /* 5.4: For MO, if a CP-DATA is received for a new + * transaction, equals reception of an implicit + * last CP-ACK for previous transaction */ + if (trans->sms.cp_state == GSM411_CPS_IDLE) { + int i; + struct gsm_trans *ptrans; + + /* Scan through all remote initiated transactions */ + for (i=8; i<15; i++) { + if (i == transaction_id) + continue; + + ptrans = trans_find_by_id(lchan->subscr, + GSM48_PDISC_SMS, i); + if (!ptrans) + continue; + + DEBUGP(DSMS, "Implicit CP-ACK for trans_id=%x\n", i); + + /* Finish it for good */ + bsc_del_timer(&ptrans->sms.cp_timer); + ptrans->sms.cp_state = GSM411_CPS_IDLE; + trans_free(ptrans); + } + } + /* 5.2.3.1.3: MO state exists when SMC has received * CP-DATA, including sending of the assoc. CP-ACK */ /* 5.2.3.2.4: MT state exists when SMC has received @@ -1008,10 +1047,14 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms) struct gsm_trans *trans; u_int8_t *data, *rp_ud_len; u_int8_t msg_ref = 42; - u_int8_t transaction_id; + int transaction_id; int rc; - transaction_id = 4; /* FIXME: we always use 4 for now */ + transaction_id = trans_assign_trans_id(lchan->subscr, GSM48_PDISC_SMS, 0); + if (transaction_id == -1) { + LOGP(DSMS, LOGL_ERROR, "No available transaction ids\n"); + return -EBUSY; + } msg->lchan = lchan; @@ -1021,7 +1064,7 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms) trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS, transaction_id, new_callref++); if (!trans) { - DEBUGP(DSMS, "No memory for trans\n"); + LOGP(DSMS, LOGL_ERROR, "No memory for trans\n"); /* FIXME: send some error message */ return -ENOMEM; } @@ -1069,7 +1112,7 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms) DEBUGP(DSMS, "TX: SMS DELIVER\n"); - lchan->ts->trx->bts->network->stats.sms.delivered++; + counter_inc(lchan->ts->trx->bts->network->stats.sms.delivered); return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref); /* FIXME: enter 'wait for RP-ACK' state, start TR1N */ diff --git a/openbsc/src/gsm_04_80.c b/openbsc/src/gsm_04_80.c index d3b472f30..8271274f1 100644 --- a/openbsc/src/gsm_04_80.c +++ b/openbsc/src/gsm_04_80.c @@ -29,11 +29,11 @@ #include <string.h> #include <errno.h> -#include <openbsc/msgb.h> -#include <openbsc/tlv.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> #include <openbsc/debug.h> #include <openbsc/gsm_data.h> -#include <openbsc/gsm_utils.h> +#include <osmocore/gsm_utils.h> #include <openbsc/gsm_04_08.h> #include <openbsc/gsm_04_80.h> diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c index 8330b5190..bd331911e 100644 --- a/openbsc/src/gsm_data.c +++ b/openbsc/src/gsm_data.c @@ -1,4 +1,4 @@ -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> * * All Rights Reserved * @@ -26,23 +26,14 @@ #include <ctype.h> #include <openbsc/gsm_data.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> +#include <osmocore/gsm_utils.h> #include <openbsc/abis_nm.h> +#include <osmocore/statistics.h> void *tall_bsc_ctx; -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"; -} +static LLIST_HEAD(bts_models); void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr, u_int8_t e1_ts, u_int8_t e1_ts_ss) @@ -92,7 +83,7 @@ static const char *lchan_names[] = { [GSM_LCHAN_UNKNOWN] = "UNKNOWN", }; -const char *gsm_lchan_name(enum gsm_chan_t c) +const char *gsm_lchant_name(enum gsm_chan_t c) { if (c >= ARRAY_SIZE(lchan_names)) return "INVALID"; @@ -100,6 +91,20 @@ const char *gsm_lchan_name(enum gsm_chan_t c) return lchan_names[c]; } +static const struct value_string lchan_s_names[] = { + { LCHAN_S_NONE, "NONE" }, + { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" }, + { LCHAN_S_ACTIVE, "ACTIVE" }, + { LCHAN_S_INACTIVE, "INACTIVE" }, + { LCHAN_S_REL_REQ, "RELEASE REQUESTED" }, + { 0, NULL }, +}; + +const char *gsm_lchans_name(enum gsm_lchan_state s) +{ + return get_value_string(lchan_s_names, s); +} + static const char *chreq_names[] = { [GSM_CHREQ_REASON_EMERG] = "EMERGENCY", [GSM_CHREQ_REASON_PAG] = "PAGING", @@ -116,6 +121,29 @@ const char *gsm_chreq_name(enum gsm_chreq_reason_t c) return chreq_names[c]; } +static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type) +{ + struct gsm_bts_model *model; + + llist_for_each_entry(model, &bts_models, list) { + if (model->type == type) + return model; + } + + return NULL; +} + +int gsm_bts_model_register(struct gsm_bts_model *model) +{ + if (bts_model_find(model->type)) + return -EEXIST; + + tlv_def_patch(&model->nm_att_tlvdef, &nm_att_tlvdef); + llist_add_tail(&model->list, &bts_models); + return 0; +} + + struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) { struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx); @@ -126,6 +154,7 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) trx->bts = bts; trx->nr = bts->num_trx++; + trx->nm_state.administrative = NM_STATE_UNLOCKED; for (k = 0; k < TRX_NR_TS; k++) { struct gsm_bts_trx_ts *ts = &trx->ts[k]; @@ -145,6 +174,9 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) } } + if (trx->nr != 0) + trx->nominal_power = bts->c0->nominal_power; + llist_add_tail(&trx->list, &bts->trx_list); return trx; @@ -154,14 +186,21 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type, u_int8_t tsc, u_int8_t bsic) { struct gsm_bts *bts = talloc_zero(net, struct gsm_bts); + struct gsm_bts_model *model = bts_model_find(type); int i; if (!bts) return NULL; + if (!model && type != GSM_BTS_TYPE_UNKNOWN) { + talloc_free(bts); + return NULL; + } + bts->network = net; bts->nr = net->num_bts++; bts->type = type; + bts->model = model; bts->tsc = tsc; bts->bsic = bsic; bts->num_trx = 0; @@ -175,6 +214,10 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type, bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc; bts->si_common.cell_alloc.data_len = sizeof(bts->si_common.data.cell_alloc); + bts->si_common.rach_control.re = 1; /* no re-establishment */ + bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */ + bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ + bts->si_common.rach_control.t2 = 4; /* no emergency calls */ for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { bts->gprs.nsvc[i].bts = bts; @@ -206,6 +249,7 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c net->country_code = country_code; net->network_code = network_code; net->num_bts = 0; + net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED; net->T3101 = GSM_T3101_DEFAULT; net->T3113 = GSM_T3113_DEFAULT; /* FIXME: initialize all other timers! */ @@ -222,6 +266,32 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c INIT_LLIST_HEAD(&net->upqueue); INIT_LLIST_HEAD(&net->bts_list); + net->stats.chreq.total = counter_alloc("net.chreq.total"); + net->stats.chreq.no_channel = counter_alloc("net.chreq.no_channel"); + net->stats.handover.attempted = counter_alloc("net.handover.attempted"); + net->stats.handover.no_channel = counter_alloc("net.handover.no_channel"); + net->stats.handover.timeout = counter_alloc("net.handover.timeout"); + net->stats.handover.completed = counter_alloc("net.handover.completed"); + net->stats.handover.failed = counter_alloc("net.handover.failed"); + net->stats.loc_upd_type.attach = counter_alloc("net.loc_upd_type.attach"); + net->stats.loc_upd_type.normal = counter_alloc("net.loc_upd_type.normal"); + net->stats.loc_upd_type.periodic = counter_alloc("net.loc_upd_type.periodic"); + net->stats.loc_upd_type.detach = counter_alloc("net.imsi_detach.count"); + net->stats.loc_upd_resp.reject = counter_alloc("net.loc_upd_resp.reject"); + net->stats.loc_upd_resp.accept = counter_alloc("net.loc_upd_resp.accept"); + net->stats.paging.attempted = counter_alloc("net.paging.attempted"); + net->stats.paging.detached = counter_alloc("net.paging.detached"); + net->stats.paging.completed = counter_alloc("net.paging.completed"); + net->stats.paging.expired = counter_alloc("net.paging.expired"); + net->stats.sms.submitted = counter_alloc("net.sms.submitted"); + net->stats.sms.no_receiver = counter_alloc("net.sms.no_receiver"); + net->stats.sms.delivered = counter_alloc("net.sms.delivered"); + net->stats.sms.rp_err_mem = counter_alloc("net.sms.rp_err_mem"); + net->stats.sms.rp_err_other = counter_alloc("net.sms.rp_err_other"); + net->stats.call.dialled = counter_alloc("net.call.dialled"); + net->stats.call.alerted = counter_alloc("net.call.alerted"); + net->stats.call.connected = counter_alloc("net.call.connected"); + net->mncc_recv = mncc_recv; net->core_country_code = -1; @@ -282,6 +352,15 @@ struct gsm_bts_trx *gsm_bts_trx_num(struct gsm_bts *bts, int num) static char ts2str[255]; +char *gsm_trx_name(struct gsm_bts_trx *trx) +{ + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)", + trx->bts->nr, trx->nr); + + return ts2str; +} + + char *gsm_ts_name(struct gsm_bts_trx_ts *ts) { snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)", @@ -290,6 +369,16 @@ char *gsm_ts_name(struct gsm_bts_trx_ts *ts) return ts2str; } +char *gsm_lchan_name(struct gsm_lchan *lchan) +{ + struct gsm_bts_trx_ts *ts = lchan->ts; + + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)", + ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr); + + return ts2str; +} + static const char *bts_types[] = { [GSM_BTS_TYPE_UNKNOWN] = "unknown", [GSM_BTS_TYPE_BS11] = "bs11", @@ -313,6 +402,17 @@ const char *btstype2str(enum gsm_bts_type type) return bts_types[type]; } +struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr) +{ + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nr == nr) + return trx; + } + return NULL; +} + /* Search for a BTS in the given Location Area; optionally start searching * with start_bts (for continuing to search after the first result) */ struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, @@ -340,47 +440,6 @@ struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, return NULL; } -char *gsm_band_name(enum gsm_band band) -{ - switch (band) { - case GSM_BAND_400: - return "GSM400"; - case GSM_BAND_850: - return "GSM850"; - case GSM_BAND_900: - return "GSM900"; - case GSM_BAND_1800: - return "DCS1800"; - case GSM_BAND_1900: - return "PCS1900"; - } - return "invalid"; -} - -enum gsm_band gsm_band_parse(const char* mhz) -{ - while (*mhz && !isdigit(*mhz)) - mhz++; - - if (*mhz == '\0') - return -EINVAL; - - switch (atoi(mhz)) { - case 400: - return GSM_BAND_400; - case 850: - return GSM_BAND_850; - case 900: - return GSM_BAND_900; - case 1800: - return GSM_BAND_1800; - case 1900: - return GSM_BAND_1900; - default: - return -EINVAL; - } -} - static const char *gsm_auth_policy_names[] = { [GSM_AUTH_POLICY_CLOSED] = "closed", [GSM_AUTH_POLICY_ACCEPT_ALL] = "accept-all", @@ -440,3 +499,27 @@ struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan) return meas_rep; } + +int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) +{ + struct gsm_bts_model *model; + + model = bts_model_find(type); + if (!model) + return -EINVAL; + + bts->type = type; + bts->model = model; + + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + /* Set the default OML Stream ID to 0xff */ + bts->oml_tei = 0xff; + bts->c0->nominal_power = 23; + break; + case GSM_BTS_TYPE_BS11: + break; + } + + return 0; +} diff --git a/openbsc/src/gsm_subscriber.c b/openbsc/src/gsm_subscriber.c index c81b522d4..692508753 100644 --- a/openbsc/src/gsm_subscriber.c +++ b/openbsc/src/gsm_subscriber.c @@ -34,6 +34,14 @@ extern struct llist_head *subscr_bsc_active_subscriber(void); +char *subscr_name(struct gsm_subscriber *subscr) +{ + if (strlen(subscr->name)) + return subscr->name; + + return subscr->imsi; +} + struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net, u_int32_t tmsi) { @@ -100,12 +108,15 @@ int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason) s->net = bts->network; /* Indicate "attached to LAC" */ s->lac = bts->location_area_code; + LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n", + subscr_name(s), s->lac); dispatch_signal(SS_SUBSCR, S_SUBSCR_ATTACHED, s); break; case GSM_SUBSCRIBER_UPDATE_DETACHED: /* Only detach if we are currently in this area */ if (bts->location_area_code == s->lac) s->lac = GSM_LAC_RESERVED_DETACHED; + LOGP(DMM, LOGL_INFO, "Subscriber %s DETACHED\n", subscr_name(s)); dispatch_signal(SS_SUBSCR, S_SUBSCR_DETACHED, s); break; default: diff --git a/openbsc/src/gsm_subscriber_base.c b/openbsc/src/gsm_subscriber_base.c index 929834e42..48e4510a2 100644 --- a/openbsc/src/gsm_subscriber_base.c +++ b/openbsc/src/gsm_subscriber_base.c @@ -27,11 +27,10 @@ #include <string.h> #include <assert.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/gsm_subscriber.h> #include <openbsc/paging.h> #include <openbsc/debug.h> -#include <openbsc/paging.h> #include <openbsc/chan_alloc.h> LLIST_HEAD(active_subscribers); @@ -139,7 +138,7 @@ static void subscr_free(struct gsm_subscriber *subscr) struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr) { subscr->use_count++; - DEBUGP(DCC, "subscr %s usage increases usage to: %d\n", + DEBUGP(DREF, "subscr %s usage increases usage to: %d\n", subscr->extension, subscr->use_count); return subscr; } @@ -147,7 +146,7 @@ struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr) struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr) { subscr->use_count--; - DEBUGP(DCC, "subscr %s usage decreased usage to: %d\n", + DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n", subscr->extension, subscr->use_count); if (subscr->use_count <= 0) subscr_free(subscr); diff --git a/openbsc/src/gsm_utils.c b/openbsc/src/gsm_utils.c deleted file mode 100644 index 9439993db..000000000 --- a/openbsc/src/gsm_utils.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> - * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_utils.h> -#include <execinfo.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> - -/* GSM 03.38 6.2.1 Charachter packing */ -int gsm_7bit_decode(char *text, const u_int8_t *user_data, u_int8_t length) -{ - int i = 0; - int l = 0; - - /* FIXME: We need to account for user data headers here */ - i += l; - for (; i < length; i ++) - *(text ++) = - ((user_data[(i * 7 + 7) >> 3] << - (7 - ((i * 7 + 7) & 7))) | - (user_data[(i * 7) >> 3] >> - ((i * 7) & 7))) & 0x7f; - *text = '\0'; - - return i - l; -} - - -/* GSM 03.38 6.2.1 Charachter packing */ -int gsm_7bit_encode(u_int8_t *result, const char *data) -{ - int i,j = 0; - unsigned char ch1, ch2; - int shift = 0; - - for ( i=0; i<strlen(data); i++ ) { - - ch1 = data[i] & 0x7F; - ch1 = ch1 >> shift; - ch2 = data[(i+1)] & 0x7F; - ch2 = ch2 << (7-shift); - - ch1 = ch1 | ch2; - - result[j++] = ch1; - - shift++; - - if ((shift == 7) && (i+1<strlen(data))) { - shift = 0; - i++; - } - } - - return i; -} - -/* determine power control level for given dBm value, as indicated - * by the tables in chapter 4.1.1 of GSM TS 05.05 */ -int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm) -{ - switch (band) { - case GSM_BAND_400: - case GSM_BAND_900: - case GSM_BAND_850: - if (dbm >= 39) - return 0; - else if (dbm < 5) - return 19; - else { - /* we are guaranteed to have (5 <= dbm < 39) */ - return 2 + ((39 - dbm) / 2); - } - break; - case GSM_BAND_1800: - if (dbm >= 36) - return 29; - else if (dbm >= 34) - return 30; - else if (dbm >= 32) - return 31; - else if (dbm == 31) - return 0; - else { - /* we are guaranteed to have (0 <= dbm < 31) */ - return (30 - dbm) / 2; - } - break; - case GSM_BAND_1900: - if (dbm >= 33) - return 30; - else if (dbm >= 32) - return 31; - else if (dbm == 31) - return 0; - else { - /* we are guaranteed to have (0 <= dbm < 31) */ - return (30 - dbm) / 2; - } - break; - } - return -EINVAL; -} - -int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl) -{ - lvl &= 0x1f; - - switch (band) { - case GSM_BAND_400: - case GSM_BAND_900: - case GSM_BAND_850: - if (lvl < 2) - return 39; - else if (lvl < 20) - return 39 - ((lvl - 2) * 2) ; - else - return 5; - break; - case GSM_BAND_1800: - if (lvl < 16) - return 30 - (lvl * 2); - else if (lvl < 29) - return 0; - else - return 36 - ((lvl - 29) * 2); - break; - case GSM_BAND_1900: - if (lvl < 16) - return 30 - (lvl * 2); - else if (lvl < 30) - return -EINVAL; - else - return 33 - (lvl - 30); - break; - } - return -EINVAL; -} - -/* According to TS 08.05 Chapter 8.1.4 */ -int rxlev2dbm(u_int8_t rxlev) -{ - if (rxlev > 63) - rxlev = 63; - - return -110 + rxlev; -} - -/* According to TS 08.05 Chapter 8.1.4 */ -u_int8_t dbm2rxlev(int dbm) -{ - int rxlev = dbm + 110; - - if (rxlev > 63) - rxlev = 63; - else if (rxlev < 0) - rxlev = 0; - - return rxlev; -} - -void generate_backtrace() -{ - int i, nptrs; - void *buffer[100]; - char **strings; - - nptrs = backtrace(buffer, ARRAY_SIZE(buffer)); - printf("backtrace() returned %d addresses\n", nptrs); - - strings = backtrace_symbols(buffer, nptrs); - if (!strings) - return; - - for (i = 1; i < nptrs; i++) - printf("%s\n", strings[i]); - - free(strings); -} diff --git a/openbsc/src/handover_decision.c b/openbsc/src/handover_decision.c index b37cecddb..efafca6e2 100644 --- a/openbsc/src/handover_decision.c +++ b/openbsc/src/handover_decision.c @@ -25,14 +25,14 @@ #include <stdlib.h> #include <errno.h> -#include <openbsc/msgb.h> +#include <osmocore/msgb.h> #include <openbsc/debug.h> #include <openbsc/gsm_data.h> #include <openbsc/meas_rep.h> #include <openbsc/signal.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/handover.h> -#include <openbsc/gsm_utils.h> +#include <osmocore/gsm_utils.h> /* issue handover to a cell identified by ARFCN and BSIC */ static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, diff --git a/openbsc/src/handover_logic.c b/openbsc/src/handover_logic.c index d40374f10..6b56bad28 100644 --- a/openbsc/src/handover_logic.c +++ b/openbsc/src/handover_logic.c @@ -29,16 +29,16 @@ #include <time.h> #include <netinet/in.h> -#include <openbsc/msgb.h> +#include <osmocore/msgb.h> #include <openbsc/debug.h> #include <openbsc/gsm_data.h> -#include <openbsc/gsm_utils.h> +#include <osmocore/gsm_utils.h> #include <openbsc/gsm_subscriber.h> #include <openbsc/gsm_04_08.h> #include <openbsc/abis_rsl.h> #include <openbsc/chan_alloc.h> #include <openbsc/signal.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/transaction.h> #include <openbsc/rtp_proxy.h> @@ -97,12 +97,12 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n", old_lchan->ts->trx->bts->nr, bts->nr); - bts->network->stats.handover.attempted++; + counter_inc(bts->network->stats.handover.attempted); new_lchan = lchan_alloc(bts, old_lchan->type); if (!new_lchan) { LOGP(DHO, LOGL_NOTICE, "No free channel\n"); - bts->network->stats.handover.no_channel++; + counter_inc(bts->network->stats.handover.no_channel); return -ENOSPC; } @@ -144,9 +144,10 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) static void ho_T3103_cb(void *_ho) { struct bsc_handover *ho = _ho; + struct gsm_network *net = ho->new_lchan->ts->trx->bts->network; DEBUGP(DHO, "HO T3103 expired\n"); - ho->new_lchan->ts->trx->bts->network->stats.handover.timeout++; + counter_inc(net->stats.handover.timeout); lchan_free(ho->new_lchan); llist_del(&ho->list); @@ -207,6 +208,7 @@ static int ho_chan_activ_nack(struct gsm_lchan *new_lchan) /* GSM 04.08 HANDOVER COMPLETE has been received on new channel */ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) { + struct gsm_network *net = new_lchan->ts->trx->bts->network; struct bsc_handover *ho; ho = bsc_ho_by_new_lchan(new_lchan); @@ -215,7 +217,12 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) return -ENODEV; } - new_lchan->ts->trx->bts->network->stats.handover.completed++; + LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN " + "%u->%u\n", subscr_name(ho->old_lchan->subscr), + ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr, + ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn); + + counter_inc(net->stats.handover.completed); bsc_del_timer(&ho->T3103); @@ -235,6 +242,7 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) /* GSM 04.08 HANDOVER FAIL has been received */ static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan) { + struct gsm_network *net = old_lchan->ts->trx->bts->network; struct bsc_handover *ho; ho = bsc_ho_by_old_lchan(old_lchan); @@ -243,7 +251,7 @@ static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan) return -ENODEV; } - old_lchan->ts->trx->bts->network->stats.handover.failed++; + counter_inc(net->stats.handover.failed); bsc_del_timer(&ho->T3103); llist_del(&ho->list); @@ -276,8 +284,8 @@ static int ho_ipac_crcx_ack(struct gsm_lchan *new_lchan) ho = bsc_ho_by_new_lchan(new_lchan); if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return -ENODEV; + /* it is perfectly normal, we have CRCX even in non-HO cases */ + return 0; } if (ipacc_rtp_direct) { diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c index 84b5641ad..943a5e88d 100644 --- a/openbsc/src/input/ipaccess.c +++ b/openbsc/src/input/ipaccess.c @@ -32,9 +32,9 @@ #include <sys/ioctl.h> #include <arpa/inet.h> -#include <openbsc/select.h> -#include <openbsc/tlv.h> -#include <openbsc/msgb.h> +#include <osmocore/select.h> +#include <osmocore/tlv.h> +#include <osmocore/msgb.h> #include <openbsc/debug.h> #include <openbsc/gsm_data.h> #include <openbsc/abis_nm.h> @@ -42,7 +42,7 @@ #include <openbsc/subchan_demux.h> #include <openbsc/e1_input.h> #include <openbsc/ipaccess.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> /* data structure for one E1 interface with A-bis */ struct ia_e1_handle { @@ -224,7 +224,7 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg, &site_id, &bts_id, &trx_id); bts = find_bts_by_unitid(e1h->gsmnet, site_id, bts_id); if (!bts) { - DEBUGP(DINP, "Unable to find BTS configuration for " + LOGP(DINP, LOGL_ERROR, "Unable to find BTS configuration for " " %u/%u/%u, disconnecting\n", site_id, bts_id, trx_id); return -EIO; @@ -281,7 +281,8 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error) hh = (struct ipaccess_head *) msg->data; ret = recv(bfd->fd, msg->data, 3, 0); if (ret < 0) { - fprintf(stderr, "recv error %s\n", strerror(errno)); + if (errno != EAGAIN) + LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno)); msgb_free(msg); *error = ret; return NULL; @@ -298,7 +299,7 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error) len = ntohs(hh->len); ret = recv(bfd->fd, msg->l2h, len, 0); if (ret < len) { - fprintf(stderr, "short read!\n"); + LOGP(DINP, LOGL_ERROR, "short read!\n"); msgb_free(msg); *error = -EIO; return NULL; @@ -321,7 +322,13 @@ static int handle_ts1_read(struct bsc_fd *bfd) msg = ipaccess_read_msg(bfd, &error); if (!msg) { if (error == 0) { - fprintf(stderr, "BTS disappeared, dead socket\n"); + link = e1inp_lookup_sign_link(e1i_ts, IPAC_PROTO_OML, 0); + if (link) { + link->trx->bts->ip_access.flags = 0; + LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n", + link->trx->bts->nr); + } else + LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n"); e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL); e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML); bsc_unregister_fd(bfd); @@ -351,7 +358,8 @@ static int handle_ts1_read(struct bsc_fd *bfd) link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0); if (!link) { - printf("no matching signalling link for hh->proto=0x%02x\n", hh->proto); + LOGP(DINP, LOGL_ERROR, "no matching signalling link for " + "hh->proto=0x%02x\n", hh->proto); msgb_free(msg); return -EIO; } @@ -373,7 +381,7 @@ static int handle_ts1_read(struct bsc_fd *bfd) ret = abis_nm_rcvmsg(msg); break; default: - DEBUGP(DMI, "Unknown IP.access protocol proto=0x%02x\n", hh->proto); + LOGP(DINP, LOGL_NOTICE, "Unknown IP.access protocol proto=0x%02x\n", hh->proto); msgb_free(msg); break; } @@ -473,7 +481,7 @@ static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what) if (what & BSC_FD_WRITE) rc = handle_ts1_write(bfd); } else - fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type); + LOGP(DINP, LOGL_ERROR, "unknown E1 TS type %u\n", e1i_ts->type); return rc; } @@ -503,7 +511,8 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) perror("accept"); return ret; } - DEBUGP(DINP, "accept()ed new OML link from %s\n", inet_ntoa(sa.sin_addr)); + LOGP(DINP, LOGL_NOTICE, "accept()ed new OML link from %s\n", + inet_ntoa(sa.sin_addr)); line = talloc_zero(tall_bsc_ctx, struct e1inp_line); if (!line) { @@ -525,7 +534,7 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) bfd->when = BSC_FD_READ; ret = bsc_register_fd(bfd); if (ret < 0) { - fprintf(stderr, "could not register FD\n"); + LOGP(DINP, LOGL_ERROR, "could not register FD\n"); close(bfd->fd); talloc_free(line); return ret; @@ -561,13 +570,13 @@ static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) perror("accept"); return bfd->fd; } - DEBUGP(DINP, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr)); + LOGP(DINP, LOGL_NOTICE, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr)); bfd->priv_nr = 2; bfd->cb = ipaccess_fd_cb; bfd->when = BSC_FD_READ; ret = bsc_register_fd(bfd); if (ret < 0) { - fprintf(stderr, "could not register FD\n"); + LOGP(DINP, LOGL_ERROR, "could not register FD\n"); close(bfd->fd); talloc_free(bfd); return ret; @@ -589,6 +598,11 @@ static int make_sock(struct bsc_fd *bfd, u_int16_t port, bfd->when = BSC_FD_READ; //bfd->data = line; + if (bfd->fd < 0) { + LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n"); + return -EIO; + } + memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); @@ -598,20 +612,23 @@ static int make_sock(struct bsc_fd *bfd, u_int16_t port, ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); if (ret < 0) { - fprintf(stderr, "could not bind l2 socket %s\n", + LOGP(DINP, LOGL_ERROR, "could not bind l2 socket %s\n", strerror(errno)); + close(bfd->fd); return -EIO; } ret = listen(bfd->fd, 1); if (ret < 0) { perror("listen"); + close(bfd->fd); return ret; } ret = bsc_register_fd(bfd); if (ret < 0) { perror("register_listen_fd"); + close(bfd->fd); return ret; } return 0; @@ -630,11 +647,16 @@ int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) bfd->data = line; bfd->priv_nr = 1; + if (bfd->fd < 0) { + LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n"); + return -EIO; + } + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); if (ret < 0) { - fprintf(stderr, "could not connect socket\n"); + LOGP(DINP, LOGL_ERROR, "could not connect socket\n"); close(bfd->fd); return ret; } @@ -668,12 +690,12 @@ int ipaccess_setup(struct gsm_network *gsmnet) e1h->gsmnet = gsmnet; /* Listen for OML connections */ - ret = make_sock(&e1h->listen_fd, 3002, listen_fd_cb); + ret = make_sock(&e1h->listen_fd, IPA_TCP_PORT_OML, listen_fd_cb); if (ret < 0) return ret; /* Listen for RSL connections */ - ret = make_sock(&e1h->rsl_listen_fd, 3003, rsl_listen_fd_cb); + ret = make_sock(&e1h->rsl_listen_fd, IPA_TCP_PORT_RSL, rsl_listen_fd_cb); return ret; } diff --git a/openbsc/src/input/misdn.c b/openbsc/src/input/misdn.c index 135cfad48..56930d498 100644 --- a/openbsc/src/input/misdn.c +++ b/openbsc/src/input/misdn.c @@ -41,15 +41,15 @@ #define PF_ISDN AF_ISDN #endif -#include <openbsc/select.h> -#include <openbsc/msgb.h> +#include <osmocore/select.h> +#include <osmocore/msgb.h> #include <openbsc/debug.h> #include <openbsc/gsm_data.h> #include <openbsc/abis_nm.h> #include <openbsc/abis_rsl.h> #include <openbsc/subchan_demux.h> #include <openbsc/e1_input.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #define TS1_ALLOC_SIZE 300 diff --git a/openbsc/src/ipaccess-config.c b/openbsc/src/ipaccess-config.c deleted file mode 100644 index c50a46581..000000000 --- a/openbsc/src/ipaccess-config.c +++ /dev/null @@ -1,381 +0,0 @@ -/* ip.access nanoBTS configuration tool */ - -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <getopt.h> -#include <sys/types.h> - -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - - -#include <openbsc/select.h> -#include <openbsc/timer.h> -#include <openbsc/ipaccess.h> -#include <openbsc/gsm_data.h> -#include <openbsc/e1_input.h> -#include <openbsc/abis_nm.h> -#include <openbsc/signal.h> -#include <openbsc/debug.h> - -static struct gsm_network *gsmnet; - -static int net_listen_testnr; -static int restart; -static char *prim_oml_ip; -static char *unit_id; -static u_int16_t nv_flags; -static u_int16_t nv_mask; - -/* -static u_int8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 }; -static u_int8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 }; -*/ - -/* - * Callback function for NACK on the OML NM - * - * Currently we send the config requests but don't check the - * result. The nanoBTS will send us a NACK when we did something the - * BTS didn't like. - */ -static int ipacc_msg_nack(u_int8_t mt) -{ - fprintf(stderr, "Failure to set attribute. This seems fatal\n"); - exit(-1); - return 0; -} - -struct ipacc_ferr_elem { - int16_t freq_err; - u_int8_t freq_qual; - u_int8_t arfcn; -} __attribute__((packed)); - -struct ipacc_cusage_elem { - u_int16_t arfcn:10, - rxlev:6; -} __attribute__ ((packed)); - -static int test_rep(void *_msg) -{ - struct msgb *msg = _msg; - struct abis_om_fom_hdr *foh = msgb_l3(msg); - u_int16_t test_rep_len, ferr_list_len; - struct ipacc_ferr_elem *ife; - struct ipac_bcch_info binfo; - int i, rc; - - DEBUGP(DNM, "TEST REPORT: "); - - if (foh->data[0] != NM_ATT_TEST_NO || - foh->data[2] != NM_ATT_TEST_REPORT) - return -EINVAL; - - DEBUGPC(DNM, "test_no=0x%02x ", foh->data[1]); - /* data[2] == NM_ATT_TEST_REPORT */ - /* data[3..4]: test_rep_len */ - test_rep_len = ntohs(*(u_int16_t *) &foh->data[3]); - /* data[5]: ip.access test result */ - DEBUGPC(DNM, "test_res=%s\n", ipacc_testres_name(foh->data[5])); - - /* data[6]: ip.access nested IE. 3 == freq_err_list */ - switch (foh->data[6]) { - case NM_IPAC_EIE_FREQ_ERR_LIST: - /* data[7..8]: length of ferr_list */ - ferr_list_len = ntohs(*(u_int16_t *) &foh->data[7]); - - /* data[9...]: frequency error list elements */ - for (i = 0; i < ferr_list_len; i+= sizeof(*ife)) { - ife = (struct ipacc_ferr_elem *) (foh->data + 9 + i); - DEBUGP(DNM, "==> ARFCN %4u, Frequency Error %6hd\n", - ife->arfcn, ntohs(ife->freq_err)); - } - break; - case NM_IPAC_EIE_CHAN_USE_LIST: - /* data[7..8]: length of ferr_list */ - ferr_list_len = ntohs(*(u_int16_t *) &foh->data[7]); - - /* data[9...]: channel usage list elements */ - for (i = 0; i < ferr_list_len; i+= 2) { - u_int16_t *cu_ptr = (u_int16_t *)(foh->data + 9 + i); - u_int16_t cu = ntohs(*cu_ptr); - DEBUGP(DNM, "==> ARFCN %4u, RxLev %2u\n", - cu & 0x3ff, cu >> 10); - } - break; - case NM_IPAC_EIE_BCCH_INFO_TYPE: - break; - case NM_IPAC_EIE_BCCH_INFO: - rc = ipac_parse_bcch_info(&binfo, foh->data+6); - if (rc < 0) { - DEBUGP(DNM, "BCCH Info parsing failed\n"); - break; - } - DEBUGP(DNM, "==> ARFCN %u, RxLev %2u, RxQual %2u: %3d-%d, LAC %d CI %d\n", - binfo.arfcn, binfo.rx_lev, binfo.rx_qual, - binfo.cgi.mcc, binfo.cgi.mnc, - binfo.cgi.lac, binfo.cgi.ci); - break; - default: - break; - } - - return 0; -} - -static int nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - u_int8_t *msg_type; - - switch (signal) { - case S_NM_IPACC_NACK: - msg_type = signal_data; - return ipacc_msg_nack(*msg_type); - case S_NM_TEST_REP: - return test_rep(signal_data); - default: - break; - } - - return 0; -} - -static void bootstrap_om(struct gsm_bts *bts) -{ - int len; - static u_int8_t buf[1024]; - u_int8_t *cur = buf; - - printf("OML link established\n"); - - if (unit_id) { - len = strlen(unit_id); - if (len > sizeof(buf)-10) - return; - buf[0] = NM_ATT_IPACC_UNIT_ID; - buf[1] = (len+1) >> 8; - buf[2] = (len+1) & 0xff; - memcpy(buf+3, unit_id, len); - buf[3+len] = 0; - printf("setting Unit ID to '%s'\n", unit_id); - abis_nm_ipaccess_set_nvattr(bts, buf, 3+len+1); - } - if (prim_oml_ip) { - struct in_addr ia; - - if (!inet_aton(prim_oml_ip, &ia)) { - fprintf(stderr, "invalid IP address: %s\n", - prim_oml_ip); - return; - } - - /* 0x88 + IP + port */ - len = 1 + sizeof(ia) + 2; - - *cur++ = NM_ATT_IPACC_PRIM_OML_CFG_LIST; - *cur++ = (len) >> 8; - *cur++ = (len) & 0xff; - *cur++ = 0x88; - memcpy(cur, &ia, sizeof(ia)); - cur += sizeof(ia); - *cur++ = 0; - *cur++ = 0; - printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia)); - abis_nm_ipaccess_set_nvattr(bts, buf, 3+len); - } - if (nv_mask) { - len = 4; - - *cur++ = NM_ATT_IPACC_NV_FLAGS; - *cur++ = (len) >> 8; - *cur++ = (len) & 0xff; - *cur++ = nv_flags & 0xff; - *cur++ = nv_mask & 0xff; - *cur++ = nv_flags >> 8; - *cur++ = nv_mask >> 8; - printf("setting NV Flags/Mask to 0x%04x/0x%04x\n", - nv_flags, nv_mask); - abis_nm_ipaccess_set_nvattr(bts, buf, 3+len); - } - - if (restart) { - printf("restarting BTS\n"); - abis_nm_ipaccess_restart(bts); - } -} - -void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx) -{ - switch (event) { - case EVT_E1_TEI_UP: - switch (type) { - case E1INP_SIGN_OML: - bootstrap_om(trx->bts); - break; - case E1INP_SIGN_RSL: - /* FIXME */ - break; - default: - break; - } - break; - case EVT_E1_TEI_DN: - fprintf(stderr, "Lost some E1 TEI link\n"); - /* FIXME: deal with TEI or L1 link loss */ - break; - default: - break; - } -} - -int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, - struct gsm_nm_state *old_state, struct gsm_nm_state *new_state) -{ - if (evt == EVT_STATECHG_OPER && - obj_class == NM_OC_RADIO_CARRIER && - new_state->availability == 3 && - net_listen_testnr) { - struct gsm_bts_trx *trx = obj; - u_int8_t phys_config[] = { 0x02, 0x0a, 0x00, 0x01, 0x02 }; - abis_nm_perform_test(trx->bts, 2, 0, 0, 0xff, - net_listen_testnr, 1, - phys_config, sizeof(phys_config)); - } - return 0; -} - -static void print_usage(void) -{ - printf("Usage: ipaccess-config\n"); -} - -static void print_help(void) -{ - printf(" -u --unit-id UNIT_ID\n"); - printf(" -o --oml-ip ip\n"); - printf(" -r --restart\n"); - printf(" -n flags/mask\tSet NVRAM attributes.\n"); - printf(" -l --listen testnr \tPerform specified test number\n"); - printf(" -h --help this text\n"); - printf(" -s --stream-id ID\n"); -} - -int main(int argc, char **argv) -{ - struct gsm_bts *bts; - struct sockaddr_in sin; - int rc, option_index = 0, stream_id = 0xff; - - printf("ipaccess-config (C) 2009 by Harald Welte\n"); - printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); - - while (1) { - int c; - unsigned long ul; - char *slash; - static struct option long_options[] = { - { "unit-id", 1, 0, 'u' }, - { "oml-ip", 1, 0, 'o' }, - { "restart", 0, 0, 'r' }, - { "help", 0, 0, 'h' }, - { "listen", 1, 0, 'l' }, - { "stream-id", 1, 0, 's' }, - }; - - c = getopt_long(argc, argv, "u:o:rn:l:hs:", long_options, - &option_index); - - if (c == -1) - break; - - switch (c) { - case 'u': - unit_id = optarg; - break; - case 'o': - prim_oml_ip = optarg; - break; - case 'r': - restart = 1; - break; - case 'n': - slash = strchr(optarg, '/'); - if (!slash) - exit(2); - ul = strtoul(optarg, NULL, 16); - nv_flags = ul & 0xffff; - ul = strtoul(slash+1, NULL, 16); - nv_mask = ul & 0xffff; - break; - case 'l': - net_listen_testnr = atoi(optarg); - break; - case 's': - stream_id = atoi(optarg); - printf("foo: %d\n", stream_id); - break; - case 'h': - print_usage(); - print_help(); - exit(0); - } - }; - - if (optind >= argc) { - fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n"); - exit(2); - } - - gsmnet = gsm_network_init(1, 1, NULL); - if (!gsmnet) - exit(1); - - bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_TSC, - HARDCODED_BSIC); - bts->oml_tei = stream_id; - - register_signal_handler(SS_NM, nm_sig_cb, NULL); - printf("Trying to connect to ip.access BTS ...\n"); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - inet_aton(argv[optind], &sin.sin_addr); - rc = ia_config_connect(bts, &sin); - if (rc < 0) { - perror("Error connecting to the BTS"); - exit(1); - } - - while (1) { - rc = bsc_select_main(0); - if (rc < 0) - exit(3); - } - - exit(0); -} - diff --git a/openbsc/src/ipaccess/ipaccess-config.c b/openbsc/src/ipaccess/ipaccess-config.c new file mode 100644 index 000000000..037ed6000 --- /dev/null +++ b/openbsc/src/ipaccess/ipaccess-config.c @@ -0,0 +1,667 @@ +/* ip.access nanoBTS configuration tool */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2009 by Holger Hans Peter Freyther + * (C) 2009 by On Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + + +#include <osmocore/select.h> +#include <osmocore/timer.h> +#include <openbsc/ipaccess.h> +#include <openbsc/gsm_data.h> +#include <openbsc/e1_input.h> +#include <openbsc/abis_nm.h> +#include <openbsc/signal.h> +#include <openbsc/debug.h> +#include <osmocore/talloc.h> + +static struct gsm_network *gsmnet; + +static int net_listen_testnr; +static int restart; +static char *prim_oml_ip; +static char *unit_id; +static u_int16_t nv_flags; +static u_int16_t nv_mask; +static char *software = NULL; +static int sw_load_state = 0; +static int oml_state = 0; + +struct sw_load { + u_int8_t file_id[255]; + u_int8_t file_id_len; + + u_int8_t file_version[255]; + u_int8_t file_version_len; +}; + +static void *tall_ctx_config = NULL; +static struct sw_load *sw_load1 = NULL; +static struct sw_load *sw_load2 = NULL; + +/* +static u_int8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 }; +static u_int8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 }; +*/ + +/* + * Callback function for NACK on the OML NM + * + * Currently we send the config requests but don't check the + * result. The nanoBTS will send us a NACK when we did something the + * BTS didn't like. + */ +static int ipacc_msg_nack(u_int8_t mt) +{ + fprintf(stderr, "Failure to set attribute. This seems fatal\n"); + exit(-1); + return 0; +} + +static int ipacc_msg_ack(u_int8_t mt, struct gsm_bts *bts) +{ + if (sw_load_state == 1) { + fprintf(stderr, "The new software is activaed.\n"); + + if (restart) { + abis_nm_ipaccess_restart(bts); + } else { + exit(0); + } + } else if (oml_state == 1) { + fprintf(stderr, "Set the primary OML IP.\n"); + if (restart) { + abis_nm_ipaccess_restart(bts); + } else { + exit(0); + } + } + + return 0; +} + +struct ipacc_ferr_elem { + int16_t freq_err; + u_int8_t freq_qual; + u_int8_t arfcn; +} __attribute__((packed)); + +struct ipacc_cusage_elem { + u_int16_t arfcn:10, + rxlev:6; +} __attribute__ ((packed)); + +static int test_rep(void *_msg) +{ + struct msgb *msg = _msg; + struct abis_om_fom_hdr *foh = msgb_l3(msg); + u_int16_t test_rep_len, ferr_list_len; + struct ipacc_ferr_elem *ife; + struct ipac_bcch_info binfo; + int i, rc; + + DEBUGP(DNM, "TEST REPORT: "); + + if (foh->data[0] != NM_ATT_TEST_NO || + foh->data[2] != NM_ATT_TEST_REPORT) + return -EINVAL; + + DEBUGPC(DNM, "test_no=0x%02x ", foh->data[1]); + /* data[2] == NM_ATT_TEST_REPORT */ + /* data[3..4]: test_rep_len */ + test_rep_len = ntohs(*(u_int16_t *) &foh->data[3]); + /* data[5]: ip.access test result */ + DEBUGPC(DNM, "test_res=%s\n", ipacc_testres_name(foh->data[5])); + + /* data[6]: ip.access nested IE. 3 == freq_err_list */ + switch (foh->data[6]) { + case NM_IPAC_EIE_FREQ_ERR_LIST: + /* data[7..8]: length of ferr_list */ + ferr_list_len = ntohs(*(u_int16_t *) &foh->data[7]); + + /* data[9...]: frequency error list elements */ + for (i = 0; i < ferr_list_len; i+= sizeof(*ife)) { + ife = (struct ipacc_ferr_elem *) (foh->data + 9 + i); + DEBUGP(DNM, "==> ARFCN %4u, Frequency Error %6hd\n", + ife->arfcn, ntohs(ife->freq_err)); + } + break; + case NM_IPAC_EIE_CHAN_USE_LIST: + /* data[7..8]: length of ferr_list */ + ferr_list_len = ntohs(*(u_int16_t *) &foh->data[7]); + + /* data[9...]: channel usage list elements */ + for (i = 0; i < ferr_list_len; i+= 2) { + u_int16_t *cu_ptr = (u_int16_t *)(foh->data + 9 + i); + u_int16_t cu = ntohs(*cu_ptr); + DEBUGP(DNM, "==> ARFCN %4u, RxLev %2u\n", + cu & 0x3ff, cu >> 10); + } + break; + case NM_IPAC_EIE_BCCH_INFO_TYPE: + break; + case NM_IPAC_EIE_BCCH_INFO: + rc = ipac_parse_bcch_info(&binfo, foh->data+6); + if (rc < 0) { + DEBUGP(DNM, "BCCH Info parsing failed\n"); + break; + } + DEBUGP(DNM, "==> ARFCN %u, RxLev %2u, RxQual %2u: %3d-%d, LAC %d CI %d\n", + binfo.arfcn, binfo.rx_lev, binfo.rx_qual, + binfo.cgi.mcc, binfo.cgi.mnc, + binfo.cgi.lac, binfo.cgi.ci); + break; + default: + break; + } + + return 0; +} + +static int nm_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct ipacc_ack_signal_data *ipacc_data; + + switch (signal) { + case S_NM_IPACC_NACK: + ipacc_data = signal_data; + return ipacc_msg_nack(ipacc_data->msg_type); + case S_NM_IPACC_ACK: + ipacc_data = signal_data; + return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->bts); + case S_NM_TEST_REP: + return test_rep(signal_data); + case S_NM_IPACC_RESTART_ACK: + printf("The BTS has acked the restart. Exiting.\n"); + exit(0); + break; + case S_NM_IPACC_RESTART_NACK: + printf("The BTS has nacked the restart. Exiting.\n"); + exit(0); + break; + default: + break; + } + + return 0; +} + +/* callback function passed to the ABIS OML code */ +static int percent; +static int percent_old; +static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, + void *data, void *param) +{ + struct msgb *msg; + struct gsm_bts *bts; + + if (hook != GSM_HOOK_NM_SWLOAD) + return 0; + + bts = (struct gsm_bts *) data; + + switch (event) { + case NM_MT_LOAD_INIT_ACK: + fprintf(stdout, "Software Load Initiate ACK\n"); + break; + case NM_MT_LOAD_INIT_NACK: + fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); + exit(5); + break; + case NM_MT_LOAD_END_ACK: + fprintf(stderr, "LOAD END ACK..."); + /* now make it the default */ + sw_load_state = 1; + + msg = msgb_alloc(1024, "sw: nvattr"); + msg->l2h = msgb_put(msg, 3); + msg->l3h = &msg->l2h[3]; + + /* activate software */ + if (sw_load1) { + msgb_v_put(msg, NM_ATT_SW_DESCR); + msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load1->file_id_len, sw_load1->file_id); + msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load1->file_version_len, + sw_load1->file_version); + } + + if (sw_load2) { + msgb_v_put(msg, NM_ATT_SW_DESCR); + msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load2->file_id_len, sw_load2->file_id); + msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load2->file_version_len, + sw_load2->file_version); + } + + /* fill in the data */ + msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG; + msg->l2h[1] = msgb_l3len(msg) >> 8; + msg->l2h[2] = msgb_l3len(msg) & 0xff; + printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg)); + abis_nm_ipaccess_set_nvattr(bts->c0, msg->l2h, msgb_l2len(msg)); + msgb_free(msg); + break; + case NM_MT_LOAD_END_NACK: + fprintf(stderr, "ERROR: Software Load End NACK\n"); + exit(3); + break; + case NM_MT_ACTIVATE_SW_NACK: + fprintf(stderr, "ERROR: Activate Software NACK\n"); + exit(4); + break; + case NM_MT_ACTIVATE_SW_ACK: + break; + case NM_MT_LOAD_SEG_ACK: + percent = abis_nm_software_load_status(bts); + if (percent > percent_old) + printf("Software Download Progress: %d%%\n", percent); + percent_old = percent; + break; + case NM_MT_LOAD_ABORT: + fprintf(stderr, "ERROR: Load aborted by the BTS.\n"); + exit(6); + break; + } + return 0; +} + +static void bootstrap_om(struct gsm_bts *bts) +{ + int len; + static u_int8_t buf[1024]; + u_int8_t *cur = buf; + + printf("OML link established\n"); + + if (unit_id) { + len = strlen(unit_id); + if (len > sizeof(buf)-10) + return; + buf[0] = NM_ATT_IPACC_UNIT_ID; + buf[1] = (len+1) >> 8; + buf[2] = (len+1) & 0xff; + memcpy(buf+3, unit_id, len); + buf[3+len] = 0; + printf("setting Unit ID to '%s'\n", unit_id); + abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len+1); + } + if (prim_oml_ip) { + struct in_addr ia; + + if (!inet_aton(prim_oml_ip, &ia)) { + fprintf(stderr, "invalid IP address: %s\n", + prim_oml_ip); + return; + } + + /* 0x88 + IP + port */ + len = 1 + sizeof(ia) + 2; + + *cur++ = NM_ATT_IPACC_PRIM_OML_CFG_LIST; + *cur++ = (len) >> 8; + *cur++ = (len) & 0xff; + *cur++ = 0x88; + memcpy(cur, &ia, sizeof(ia)); + cur += sizeof(ia); + *cur++ = 0; + *cur++ = 0; + printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia)); + oml_state = 1; + abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len); + } + if (nv_mask) { + len = 4; + + *cur++ = NM_ATT_IPACC_NV_FLAGS; + *cur++ = (len) >> 8; + *cur++ = (len) & 0xff; + *cur++ = nv_flags & 0xff; + *cur++ = nv_mask & 0xff; + *cur++ = nv_flags >> 8; + *cur++ = nv_mask >> 8; + printf("setting NV Flags/Mask to 0x%04x/0x%04x\n", + nv_flags, nv_mask); + abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len); + } + + if (restart && !prim_oml_ip && !software) { + printf("restarting BTS\n"); + abis_nm_ipaccess_restart(bts); + } + +} + +void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx) +{ + switch (event) { + case EVT_E1_TEI_UP: + switch (type) { + case E1INP_SIGN_OML: + bootstrap_om(trx->bts); + break; + case E1INP_SIGN_RSL: + /* FIXME */ + break; + default: + break; + } + break; + case EVT_E1_TEI_DN: + fprintf(stderr, "Lost some E1 TEI link\n"); + /* FIXME: deal with TEI or L1 link loss */ + break; + default: + break; + } +} + +int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state) +{ + if (evt == EVT_STATECHG_OPER && + obj_class == NM_OC_RADIO_CARRIER && + new_state->availability == 3) { + struct gsm_bts_trx *trx = obj; + + if (net_listen_testnr) { + u_int8_t phys_config[] = { 0x02, 0x0a, 0x00, 0x01, 0x02 }; + abis_nm_perform_test(trx->bts, 2, 0, 0, 0xff, + net_listen_testnr, 1, + phys_config, sizeof(phys_config)); + } else if (software) { + int rc; + printf("Attempting software upload with '%s'\n", software); + rc = abis_nm_software_load(trx->bts, software, 19, 0, swload_cbfn, trx->bts); + if (rc < 0) { + fprintf(stderr, "Failed to start software load\n"); + exit(-3); + } + } + } + return 0; +} + +static struct sw_load *create_swload(struct sdp_header *header) +{ + struct sw_load *load; + + load = talloc_zero(tall_ctx_config, struct sw_load); + + strncpy((char *)load->file_id, header->firmware_info.sw_part, 20); + load->file_id_len = strlen(header->firmware_info.sw_part) + 1; + + strncpy((char *)load->file_version, header->firmware_info.version, 20); + load->file_version_len = strlen(header->firmware_info.version) + 1; + + return load; +} + +static void find_sw_load_params(const char *filename) +{ + struct stat stat; + struct sdp_header *header; + struct llist_head *entry; + int fd; + void *tall_firm_ctx = 0; + + entry = talloc_zero(tall_firm_ctx, struct llist_head); + INIT_LLIST_HEAD(entry); + + fd = open(filename, O_RDONLY); + if (!fd) { + perror("nada"); + return; + } + + /* verify the file */ + if (fstat(fd, &stat) == -1) { + perror("Can not stat the file"); + return; + } + + ipaccess_analyze_file(fd, stat.st_size, 0, entry); + if (close(fd) != 0) { + perror("Close failed.\n"); + return; + } + + /* try to find what we are looking for */ + llist_for_each_entry(header, entry, entry) { + if (ntohs(header->firmware_info.more_more_magic) == 0x1000) { + sw_load1 = create_swload(header); + } else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) { + sw_load2 = create_swload(header); + } + } + + talloc_free(tall_firm_ctx); +} + +static void analyze_firmware(const char *filename) +{ + struct stat stat; + struct sdp_header *header; + struct sdp_header_item *sub_entry; + struct llist_head *entry; + int fd; + void *tall_firm_ctx = 0; + + entry = talloc_zero(tall_firm_ctx, struct llist_head); + INIT_LLIST_HEAD(entry); + + printf("Opening possible firmware '%s'\n", filename); + fd = open(filename, O_RDONLY); + if (!fd) { + perror("nada"); + return; + } + + /* verify the file */ + if (fstat(fd, &stat) == -1) { + perror("Can not stat the file"); + return; + } + + ipaccess_analyze_file(fd, stat.st_size, 0, entry); + if (close(fd) != 0) { + perror("Close failed.\n"); + return; + } + + llist_for_each_entry(header, entry, entry) { + printf("Printing header information:\n"); + printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic)); + printf("header_length: %u\n", ntohl(header->firmware_info.header_length)); + printf("file_length: %u\n", ntohl(header->firmware_info.file_length)); + printf("sw_part: %.20s\n", header->firmware_info.sw_part); + printf("text1: %.64s\n", header->firmware_info.text1); + printf("time: %.12s\n", header->firmware_info.time); + printf("date: %.14s\n", header->firmware_info.date); + printf("text2: %.10s\n", header->firmware_info.text2); + printf("version: %.20s\n", header->firmware_info.version); + printf("subitems...\n"); + + llist_for_each_entry(sub_entry, &header->header_list, entry) { + printf("\tsomething1: %u\n", sub_entry->header_entry.something1); + printf("\ttext1: %.64s\n", sub_entry->header_entry.text1); + printf("\ttime: %.12s\n", sub_entry->header_entry.time); + printf("\tdate: %.14s\n", sub_entry->header_entry.date); + printf("\ttext2: %.10s\n", sub_entry->header_entry.text2); + printf("\tversion: %.20s\n", sub_entry->header_entry.version); + printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length)); + printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1)); + printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2)); + printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start)); + printf("\n\n"); + } + printf("\n\n"); + } + + talloc_free(tall_firm_ctx); +} + +static void print_usage(void) +{ + printf("Usage: ipaccess-config\n"); +} + +static void print_help(void) +{ + printf(" -u --unit-id UNIT_ID\n"); + printf(" -o --oml-ip ip\n"); + printf(" -r --restart\n"); + printf(" -n flags/mask\tSet NVRAM attributes.\n"); + printf(" -l --listen testnr \tPerform specified test number\n"); + printf(" -h --help this text\n"); + printf(" -s --stream-id ID\n"); + printf(" -d --software firmware\n"); + printf(" -f --firmware firmware Provide firmware information\n"); +} + +int main(int argc, char **argv) +{ + struct gsm_bts *bts; + struct sockaddr_in sin; + int rc, option_index = 0, stream_id = 0xff; + struct debug_target *stderr_target; + + debug_init(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + debug_set_all_filter(stderr_target, 1); + debug_set_log_level(stderr_target, 0); + debug_parse_category_mask(stderr_target, "DNM,0"); + bts_model_nanobts_init(); + + printf("ipaccess-config (C) 2009 by Harald Welte\n"); + printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); + + while (1) { + int c; + unsigned long ul; + char *slash; + static struct option long_options[] = { + { "unit-id", 1, 0, 'u' }, + { "oml-ip", 1, 0, 'o' }, + { "restart", 0, 0, 'r' }, + { "help", 0, 0, 'h' }, + { "listen", 1, 0, 'l' }, + { "stream-id", 1, 0, 's' }, + { "software", 1, 0, 'd' }, + { "firmware", 1, 0, 'f' }, + }; + + c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:", long_options, + &option_index); + + if (c == -1) + break; + + switch (c) { + case 'u': + unit_id = optarg; + break; + case 'o': + prim_oml_ip = optarg; + break; + case 'r': + restart = 1; + break; + case 'n': + slash = strchr(optarg, '/'); + if (!slash) + exit(2); + ul = strtoul(optarg, NULL, 16); + nv_flags = ul & 0xffff; + ul = strtoul(slash+1, NULL, 16); + nv_mask = ul & 0xffff; + break; + case 'l': + net_listen_testnr = atoi(optarg); + break; + case 's': + stream_id = atoi(optarg); + break; + case 'd': + software = strdup(optarg); + find_sw_load_params(optarg); + break; + case 'f': + analyze_firmware(optarg); + exit(0); + case 'h': + print_usage(); + print_help(); + exit(0); + } + }; + + if (optind >= argc) { + fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n"); + exit(2); + } + + gsmnet = gsm_network_init(1, 1, NULL); + if (!gsmnet) + exit(1); + + bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_TSC, + HARDCODED_BSIC); + /* ip.access supports up to 4 chained TRX */ + gsm_bts_trx_alloc(bts); + gsm_bts_trx_alloc(bts); + gsm_bts_trx_alloc(bts); + bts->oml_tei = stream_id; + + register_signal_handler(SS_NM, nm_sig_cb, NULL); + printf("Trying to connect to ip.access BTS ...\n"); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(argv[optind], &sin.sin_addr); + rc = ia_config_connect(bts, &sin); + if (rc < 0) { + perror("Error connecting to the BTS"); + exit(1); + } + + while (1) { + rc = bsc_select_main(0); + if (rc < 0) + exit(3); + } + + exit(0); +} + diff --git a/openbsc/src/ipaccess-find.c b/openbsc/src/ipaccess/ipaccess-find.c index f469b6788..01f8a2d8d 100644 --- a/openbsc/src/ipaccess-find.c +++ b/openbsc/src/ipaccess/ipaccess-find.c @@ -8,8 +8,8 @@ #include <arpa/inet.h> -#include <openbsc/select.h> -#include <openbsc/timer.h> +#include <osmocore/select.h> +#include <osmocore/timer.h> #include <openbsc/ipaccess.h> #include <openbsc/gsm_data.h> diff --git a/openbsc/src/ipaccess/ipaccess-firmware.c b/openbsc/src/ipaccess/ipaccess-firmware.c new file mode 100644 index 000000000..8aba50978 --- /dev/null +++ b/openbsc/src/ipaccess/ipaccess-firmware.c @@ -0,0 +1,123 @@ +/* Routines for parsing an ipacces SDP firmware file */ + +/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <openbsc/debug.h> +#include <openbsc/ipaccess.h> +#include <osmocore/talloc.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define PART_LENGTH 138 + +static_assert(sizeof(struct sdp_header_entry) == 138, right_entry); +static_assert(sizeof(struct sdp_firmware) == 160, _right_header_length); + +/* more magic, the second "int" in the header */ +static char more_magic[] = { 0x10, 0x02 }; + +int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned int base_offset, struct llist_head *list) +{ + struct sdp_firmware *firmware_header = 0; + struct sdp_header *header; + char buf[4096]; + int rc, i; + + rc = read(fd, buf, sizeof(*firmware_header)); + if (rc < 0) { + perror("Can not read header start."); + return -1; + } + + firmware_header = (struct sdp_firmware *) &buf[0]; + if (strncmp(firmware_header->magic, " SDP", 4) != 0) { + fprintf(stderr, "Wrong magic.\n"); + return -1; + } + + if (memcmp(firmware_header->more_magic, more_magic, 2) != 0) { + fprintf(stderr, "Wrong more magic. Got: 0x%x %x %x %x\n", + firmware_header->more_magic[0] & 0xff, firmware_header->more_magic[1] & 0xff, + firmware_header->more_magic[2] & 0xff, firmware_header->more_magic[3] & 0xff); + return -1; + } + + + if (!firmware_header) + return -1; + + if (ntohl(firmware_header->file_length) != st_size) { + fprintf(stderr, "The filesize and the header do not match.\n"); + return -1; + } + + /* add the firmware */ + header = talloc_zero(list, struct sdp_header); + header->firmware_info = *firmware_header; + INIT_LLIST_HEAD(&header->header_list); + llist_add(&header->entry, list); + + /* this semantic appears to be only the case for 0x0000 */ + if (firmware_header->more_more_magic != 0) + return -1; + + if (ntohs(firmware_header->part_length) % PART_LENGTH != 0) { + fprintf(stderr, "The part length seems to be wrong.\n"); + return -1; + } + + /* look into each firmware now */ + for (i = 0; i < ntohs(firmware_header->part_length) / PART_LENGTH; ++i) { + struct sdp_header_entry entry; + struct sdp_header_item *header_entry; + unsigned int offset = base_offset + sizeof(struct sdp_firmware); + offset += i * 138; + + if (lseek(fd, offset, SEEK_SET) != offset) { + fprintf(stderr, "Can not seek to the offset: %u.\n", offset); + return -1; + } + + rc = read(fd, &entry, sizeof(entry)); + if (rc != sizeof(entry)) { + fprintf(stderr, "Can not read the header entry.\n"); + return -1; + } + + /* now we need to find the SDP file... */ + offset = ntohl(entry.start) + 4 + base_offset; + if (lseek(fd, offset, SEEK_SET) != offset) { + perror("can't seek to sdp"); + return -1; + } + + header_entry = talloc_zero(header, struct sdp_header_item); + header_entry->header_entry = entry; + llist_add(&header_entry->entry, &header->header_list); + + ipaccess_analyze_file(fd, ntohl(entry.length), offset, list); + } + + return 0; +} + diff --git a/openbsc/src/ipaccess/ipaccess-proxy.c b/openbsc/src/ipaccess/ipaccess-proxy.c new file mode 100644 index 000000000..217e0bdf1 --- /dev/null +++ b/openbsc/src/ipaccess/ipaccess-proxy.c @@ -0,0 +1,1127 @@ +/* OpenBSC Abis/IP proxy ip.access nanoBTS */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <time.h> +#include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/select.h> +#include <osmocore/tlv.h> +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <openbsc/ipaccess.h> +#include <osmocore/talloc.h> + +static struct debug_target *stderr_target; + +/* one instance of an ip.access protocol proxy */ +struct ipa_proxy { + /* socket where we listen for incoming OML from BTS */ + struct bsc_fd oml_listen_fd; + /* socket where we listen for incoming RSL from BTS */ + struct bsc_fd rsl_listen_fd; + /* list of BTS's (struct ipa_bts_conn */ + struct llist_head bts_list; + /* the BSC reconnect timer */ + struct timer_list reconn_timer; +}; + +/* global pointer to the proxy structure */ +static struct ipa_proxy *ipp; + +struct ipa_proxy_conn { + struct bsc_fd fd; + struct llist_head tx_queue; + struct ipa_bts_conn *bts_conn; +}; + +#define MAX_TRX 4 + +/* represents a particular BTS in our proxy */ +struct ipa_bts_conn { + /* list of BTS's (ipa_proxy->bts_list) */ + struct llist_head list; + /* back pointer to the proxy which we belong to */ + struct ipa_proxy *ipp; + /* the unit ID as determined by CCM */ + struct { + u_int16_t site_id; + u_int16_t bts_id; + } unit_id; + + /* incoming connections from BTS */ + struct ipa_proxy_conn *oml_conn; + struct ipa_proxy_conn *rsl_conn[MAX_TRX]; + + /* outgoing connections to BSC */ + struct ipa_proxy_conn *bsc_oml_conn; + struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX]; + + /* UDP sockets for BTS and BSC injection */ + struct bsc_fd udp_bts_fd; + struct bsc_fd udp_bsc_fd; + + char *id_tags[0xff]; + u_int8_t *id_resp; + unsigned int id_resp_len; +}; + +enum ipp_fd_type { + OML_FROM_BTS = 1, + RSL_FROM_BTS = 2, + OML_TO_BSC = 3, + RSL_TO_BSC = 4, + UDP_TO_BTS = 5, + UDP_TO_BSC = 6, +}; + +/* some of the code against we link from OpenBSC needs this */ +void *tall_bsc_ctx; + +static char *listen_ipaddr; +static char *bsc_ipaddr; + +#define PROXY_ALLOC_SIZE 300 + +static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG }; +static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK }; +static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET, + 0x01, IPAC_IDTAG_UNIT, + 0x01, IPAC_IDTAG_MACADDR, + 0x01, IPAC_IDTAG_LOCATION1, + 0x01, IPAC_IDTAG_LOCATION2, + 0x01, IPAC_IDTAG_EQUIPVERS, + 0x01, IPAC_IDTAG_SWVERSION, + 0x01, IPAC_IDTAG_UNITNAME, + 0x01, IPAC_IDTAG_SERNR, + }; + +static const char *idtag_names[] = { + [IPAC_IDTAG_SERNR] = "Serial_Number", + [IPAC_IDTAG_UNITNAME] = "Unit_Name", + [IPAC_IDTAG_LOCATION1] = "Location_1", + [IPAC_IDTAG_LOCATION2] = "Location_2", + [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version", + [IPAC_IDTAG_SWVERSION] = "Software_Version", + [IPAC_IDTAG_IPADDR] = "IP_Address", + [IPAC_IDTAG_MACADDR] = "MAC_Address", + [IPAC_IDTAG_UNIT] = "Unit_ID", +}; + +static const char *ipac_idtag_name(int tag) +{ + if (tag >= ARRAY_SIZE(idtag_names)) + return "unknown"; + + return idtag_names[tag]; +} + +static int ipac_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len) +{ + u_int8_t t_len; + u_int8_t t_tag; + u_int8_t *cur = buf; + + while (cur < buf + len) { + t_len = *cur++; + t_tag = *cur++; + + DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur); + + dec->lv[t_tag].len = t_len; + dec->lv[t_tag].val = cur; + + cur += t_len; + } + return 0; +} + +static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id, + u_int16_t *trx_id) +{ + unsigned long ul; + char *endptr; + const char *nptr; + + nptr = str; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (site_id) + *site_id = ul & 0xffff; + + if (*endptr++ != '/') + return -EINVAL; + + nptr = endptr; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (bts_id) + *bts_id = ul & 0xffff; + + if (*endptr++ != '/') + return -EINVAL; + + nptr = endptr; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (trx_id) + *trx_id = ul & 0xffff; + + return 0; +} + +static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp, + u_int16_t site_id, + u_int16_t bts_id) +{ + struct ipa_bts_conn *ipbc; + + llist_for_each_entry(ipbc, &ipp->bts_list, list) { + if (ipbc->unit_id.site_id == site_id && + ipbc->unit_id.bts_id == bts_id) + return ipbc; + } + + return NULL; +} + +struct ipa_proxy_conn *alloc_conn(void) +{ + struct ipa_proxy_conn *ipc; + + ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn); + if (!ipc) + return NULL; + + INIT_LLIST_HEAD(&ipc->tx_queue); + + return ipc; +} + +static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp) +{ + unsigned int i, len; + + for (i = 0; i <= 0xff; i++) { + if (!TLVP_PRESENT(tlvp, i)) + continue; + + len = TLVP_LEN(tlvp, i); +#if 0 + if (!ipbc->id_tags[i]) + ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len); + else +#endif + ipbc->id_tags[i] = talloc_realloc_size(ipbc, + ipbc->id_tags[i], len); + if (!ipbc->id_tags[i]) + return -ENOMEM; + + memset(ipbc->id_tags[i], 0, len); + //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len); + } + return 0; +} + + +static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data); + +#define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id) + +static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line, + struct ipa_bts_conn *ipbc, u_int8_t trx_id) +{ + if (ipbc) + debugp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id, + ipbc->unit_id.bts_id, trx_id); + else + debugp2(ss, lvl, file, line, 0, "unknown "); +} + +/* UDP socket handling */ + +static int make_sock(struct bsc_fd *bfd, u_int16_t port, int proto, int priv_nr, + int (*cb)(struct bsc_fd *fd, unsigned int what), + void *data) +{ + struct sockaddr_in addr; + int ret, on = 1; + + bfd->fd = socket(AF_INET, SOCK_DGRAM, proto); + bfd->cb = cb; + bfd->when = BSC_FD_READ; + bfd->data = data; + bfd->priv_nr = priv_nr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not bind socket: %s\n", + strerror(errno)); + return -EIO; + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + perror("register UDP fd"); + return ret; + } + return 0; +} + +static int handle_udp_read(struct bsc_fd *bfd) +{ + struct ipa_bts_conn *ipbc = bfd->data; + struct ipa_proxy_conn *other_conn = NULL; + struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP"); + struct ipaccess_head *hh; + int ret; + + /* with UDP sockets, we cannot read partial packets but have to read + * all of it in one go */ + hh = (struct ipaccess_head *) msg->data; + ret = recv(bfd->fd, msg->data, msg->data_len, 0); + if (ret < 0) { + if (errno != EAGAIN) + LOGP(DINP, LOGL_ERROR, "recv error %s\n", strerror(errno)); + msgb_free(msg); + return ret; + } + if (ret == 0) { + DEBUGP(DINP, "UDP peer disappeared, dead socket\n"); + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + msgb_free(msg); + return -EIO; + } + if (ret < sizeof(*hh)) { + DEBUGP(DINP, "could not even read header!?!\n"); + msgb_free(msg); + return -EIO; + } + msgb_put(msg, ret); + msg->l2h = msg->data + sizeof(*hh); + DEBUGP(DMI, "UDP RX: %s\n", hexdump(msg->data, msg->len)); + + if (hh->len != msg->len - sizeof(*hh)) { + DEBUGP(DINP, "length (%u/%u) disagrees with header(%u)\n", + msg->len, msg->len - 3, hh->len); + msgb_free(msg); + return -EIO; + } + + switch (bfd->priv_nr & 0xff) { + case UDP_TO_BTS: + /* injection towards BTS */ + switch (hh->proto) { + case IPAC_PROTO_RSL: + /* FIXME: what to do about TRX > 0 */ + other_conn = ipbc->rsl_conn[0]; + break; + default: + DEBUGP(DINP, "Unknown protocol 0x%02x, sending to " + "OML FD\n", hh->proto); + /* fall through */ + case IPAC_PROTO_IPACCESS: + case IPAC_PROTO_OML: + other_conn = ipbc->oml_conn; + break; + } + break; + case UDP_TO_BSC: + /* injection towards BSC */ + switch (hh->proto) { + case IPAC_PROTO_RSL: + /* FIXME: what to do about TRX > 0 */ + other_conn = ipbc->bsc_rsl_conn[0]; + break; + default: + DEBUGP(DINP, "Unknown protocol 0x%02x, sending to " + "OML FD\n", hh->proto); + case IPAC_PROTO_IPACCESS: + case IPAC_PROTO_OML: + other_conn = ipbc->bsc_oml_conn; + break; + } + break; + default: + DEBUGP(DINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr); + break; + } + + if (other_conn) { + /* enqueue the message for TX on the respective FD */ + msgb_enqueue(&other_conn->tx_queue, msg); + other_conn->fd.when |= BSC_FD_WRITE; + } else + msgb_free(msg); + + return 0; +} + +static int handle_udp_write(struct bsc_fd *bfd) +{ + /* not implemented yet */ + bfd->when &= ~BSC_FD_WRITE; + + return -EIO; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int udp_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) + rc = handle_udp_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_udp_write(bfd); + + return rc; +} + + +static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct bsc_fd *bfd, + u_int16_t site_id, u_int16_t bts_id, + u_int16_t trx_id, struct tlv_parsed *tlvp, + struct msgb *msg) +{ + struct ipa_bts_conn *ipbc; + u_int16_t udp_port; + int ret = 0; + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(bsc_ipaddr, &sin.sin_addr); + + DEBUGP(DINP, "(%u/%u/%u) New BTS connection: ", + site_id, bts_id, trx_id); + + /* OML needs to be established before RSL */ + if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) { + DEBUGPC(DINP, "Not a OML connection ?!?\n"); + return -EIO; + } + + /* allocate new BTS connection data structure */ + ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn); + if (!ipbc) { + ret = -ENOMEM; + goto err_out; + } + + DEBUGPC(DINP, "Created BTS Conn data structure\n"); + ipbc->ipp = ipp; + ipbc->unit_id.site_id = site_id; + ipbc->unit_id.bts_id = bts_id; + ipbc->oml_conn = ipc; + ipc->bts_conn = ipbc; + + /* store the content of the ID TAGS for later reference */ + store_idtags(ipbc, tlvp); + ipbc->id_resp_len = msg->len; + ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len); + memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len); + + /* Create OML TCP connection towards BSC */ + sin.sin_port = htons(IPA_TCP_PORT_OML); + ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); + if (!ipbc->bsc_oml_conn) { + ret = -EIO; + goto err_bsc_conn; + } + + DEBUGP(DINP, "(%u/%u/%u) OML Connected to BSC\n", + site_id, bts_id, trx_id); + + /* Create UDP socket for BTS packet injection */ + udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100); + ret = make_sock(&ipbc->udp_bts_fd, udp_port, IPPROTO_UDP, + UDP_TO_BTS, udp_fd_cb, ipbc); + if (ret < 0) + goto err_udp_bts; + DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection " + "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port); + + /* Create UDP socket for BSC packet injection */ + udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100); + ret = make_sock(&ipbc->udp_bsc_fd, udp_port, IPPROTO_UDP, + UDP_TO_BSC, udp_fd_cb, ipbc); + if (ret < 0) + goto err_udp_bsc; + DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection " + "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port); + llist_add(&ipbc->list, &ipp->bts_list); + + return 0; + +err_udp_bsc: + bsc_unregister_fd(&ipbc->udp_bts_fd); +err_udp_bts: + bsc_unregister_fd(&ipbc->bsc_oml_conn->fd); + close(ipbc->bsc_oml_conn->fd.fd); + talloc_free(ipbc->bsc_oml_conn); + ipbc->bsc_oml_conn = NULL; +err_bsc_conn: + talloc_free(ipbc->id_resp); + talloc_free(ipbc); +#if 0 + bsc_unregister_fd(bfd); + close(bfd->fd); + talloc_free(bfd); +#endif +err_out: + return ret; +} + +static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg, + struct bsc_fd *bfd) +{ + struct tlv_parsed tlvp; + u_int8_t msg_type = *(msg->l2h); + u_int16_t site_id, bts_id, trx_id; + struct ipa_bts_conn *ipbc; + int ret = 0; + + switch (msg_type) { + case IPAC_MSGT_PING: + ret = write(bfd->fd, pong, sizeof(pong)); + if (ret < 0) + return ret; + if (ret < sizeof(pong)) { + DEBUGP(DINP, "short write\n"); + return -EIO; + } + break; + case IPAC_MSGT_PONG: + DEBUGP(DMI, "PONG!\n"); + break; + case IPAC_MSGT_ID_RESP: + DEBUGP(DMI, "ID_RESP "); + /* parse tags, search for Unit ID */ + ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2, + msgb_l2len(msg)-2); + DEBUGP(DMI, "\n"); + + if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) { + LOGP(DINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n"); + return -EIO; + } + + /* lookup BTS, create sign_link, ... */ + parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT), + &site_id, &bts_id, &trx_id); + ipbc = find_bts_by_unitid(ipp, site_id, bts_id); + if (!ipbc) { + /* We have not found an ipbc (per-bts proxy instance) + * for this BTS yet. The first connection of a new BTS must + * be a OML connection. We allocate the associated data structures, + * and try to connect to the remote end */ + + return ipbc_alloc_connect(ipc, bfd, site_id, bts_id, + trx_id, &tlvp, msg); + /* if this fails, the caller will clean up bfd */ + } else { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(bsc_ipaddr, &sin.sin_addr); + + DEBUGP(DINP, "Identified BTS %u/%u/%u\n", + site_id, bts_id, trx_id); + + if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) { + LOGP(DINP, LOGL_ERROR, "Second OML connection from " + "same BTS ?!?\n"); + return 0; + } + + if (trx_id > MAX_TRX) { + LOGP(DINP, LOGL_ERROR, "We don't support more " + "than %u TRX\n", MAX_TRX); + return -EINVAL; + } + + ipc->bts_conn = ipbc; + /* store TRX number in higher 8 bit of the bfd private number */ + bfd->priv_nr |= trx_id << 8; + ipbc->rsl_conn[trx_id] = ipc; + + /* Create RSL TCP connection towards BSC */ + sin.sin_port = htons(IPA_TCP_PORT_RSL); + ipbc->bsc_rsl_conn[trx_id] = + connect_bsc(&sin, RSL_TO_BSC | (trx_id << 8), ipbc); + if (!ipbc->bsc_oml_conn) + return -EIO; + DEBUGP(DINP, "(%u/%u/%u) Connected RSL to BSC\n", + site_id, bts_id, trx_id); + } + break; + case IPAC_MSGT_ID_GET: + DEBUGP(DMI, "ID_GET\n"); + if ((bfd->priv_nr & 0xff) != OML_TO_BSC && + (bfd->priv_nr & 0xff) != RSL_TO_BSC) { + DEBUGP(DINP, "IDentity REQuest from BTS ?!?\n"); + return -EIO; + } + ipbc = ipc->bts_conn; + if (!ipbc) { + DEBUGP(DINP, "ID_GET from BSC before we have ID_RESP from BTS\n"); + return -EIO; + } + ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len); + break; + case IPAC_MSGT_ID_ACK: + DEBUGP(DMI, "ID_ACK? -> ACK!\n"); + ret = write(bfd->fd, id_ack, sizeof(id_ack)); + break; + } + return 0; +} + +struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error) +{ + struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP"); + struct ipaccess_head *hh; + int len, ret = 0; + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + /* first read our 3-byte header */ + hh = (struct ipaccess_head *) msg->data; + ret = recv(bfd->fd, msg->data, 3, 0); + if (ret < 0) { + if (errno != EAGAIN) + LOGP(DINP, LOGL_ERROR, "recv error: %s\n", strerror(errno)); + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } + + msgb_put(msg, ret); + + /* then read te length as specified in header */ + msg->l2h = msg->data + sizeof(*hh); + len = ntohs(hh->len); + ret = recv(bfd->fd, msg->l2h, len, 0); + if (ret < len) { + LOGP(DINP, LOGL_ERROR, "short read!\n"); + msgb_free(msg); + *error = -EIO; + return NULL; + } + msgb_put(msg, ret); + + return msg; +} + +static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc, + unsigned int priv_nr) +{ + struct ipa_proxy_conn *bsc_conn; + unsigned int trx_id = priv_nr >> 8; + + switch (priv_nr & 0xff) { + case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ + bsc_conn = ipbc->bsc_oml_conn; + break; + case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ + bsc_conn = ipbc->bsc_rsl_conn[trx_id]; + break; + case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ + bsc_conn = ipbc->oml_conn; + break; + case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ + bsc_conn = ipbc->rsl_conn[trx_id]; + break; + default: + bsc_conn = NULL; + break; + } + return bsc_conn; +} + +static void reconn_tmr_cb(void *data) +{ + struct ipa_proxy *ipp = data; + struct ipa_bts_conn *ipbc; + struct sockaddr_in sin; + int i; + + DEBUGP(DINP, "Running reconnect timer\n"); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(bsc_ipaddr, &sin.sin_addr); + + llist_for_each_entry(ipbc, &ipp->bts_list, list) { + /* if OML to BSC is dead, try to restore it */ + if (ipbc->oml_conn && !ipbc->bsc_oml_conn) { + sin.sin_port = htons(IPA_TCP_PORT_OML); + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0); + LOGPC(DINP, LOGL_NOTICE, "OML Trying to reconnect\n"); + ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); + if (!ipbc->bsc_oml_conn) + goto reschedule; + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0); + LOGPC(DINP, LOGL_NOTICE, "OML Reconnected\n"); + } + /* if we (still) don't have a OML connection, skip RSL */ + if (!ipbc->oml_conn || !ipbc->bsc_oml_conn) + continue; + + for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) { + unsigned int priv_nr; + /* don't establish RSL links which we don't have */ + if (!ipbc->rsl_conn[i]) + continue; + if (ipbc->bsc_rsl_conn[i]) + continue; + priv_nr = ipbc->rsl_conn[i]->fd.priv_nr; + priv_nr &= ~0xff; + priv_nr |= RSL_TO_BSC; + sin.sin_port = htons(IPA_TCP_PORT_RSL); + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8); + LOGPC(DINP, LOGL_NOTICE, "RSL Trying to reconnect\n"); + ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc); + if (!ipbc->bsc_rsl_conn) + goto reschedule; + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8); + LOGPC(DINP, LOGL_NOTICE, "RSL Reconnected\n"); + } + } + return; + +reschedule: + bsc_schedule_timer(&ipp->reconn_timer, 5, 0); +} + +static void handle_dead_socket(struct bsc_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; /* local conn */ + struct ipa_proxy_conn *bsc_conn; /* remote conn */ + struct ipa_bts_conn *ipbc = ipc->bts_conn; + unsigned int trx_id = bfd->priv_nr >> 8; + struct msgb *msg, *msg2; + + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + + /* FIXME: clear tx_queue, remove all references, etc. */ + llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list) + msgb_free(msg); + + switch (bfd->priv_nr & 0xff) { + case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ + ipbc->oml_conn = NULL; + bsc_conn = ipbc->bsc_oml_conn; + /* close the connection to the BSC */ + bsc_unregister_fd(&bsc_conn->fd); + close(bsc_conn->fd.fd); + llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) + msgb_free(msg); + talloc_free(bsc_conn); + ipbc->bsc_oml_conn = NULL; + /* FIXME: do we need to delete the entire ipbc ? */ + break; + case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ + ipbc->rsl_conn[trx_id] = NULL; + bsc_conn = ipbc->bsc_rsl_conn[trx_id]; + /* close the connection to the BSC */ + bsc_unregister_fd(&bsc_conn->fd); + close(bsc_conn->fd.fd); + llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) + msgb_free(msg); + talloc_free(bsc_conn); + ipbc->bsc_rsl_conn[trx_id] = NULL; + break; + case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ + ipbc->bsc_oml_conn = NULL; + bsc_conn = ipbc->oml_conn; + /* start reconnect timer */ + bsc_schedule_timer(&ipp->reconn_timer, 5, 0); + break; + case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ + ipbc->bsc_rsl_conn[trx_id] = NULL; + bsc_conn = ipbc->rsl_conn[trx_id]; + /* start reconnect timer */ + bsc_schedule_timer(&ipp->reconn_timer, 5, 0); + break; + default: + bsc_conn = NULL; + break; + } + + talloc_free(ipc); +} + +static int handle_tcp_read(struct bsc_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; + struct ipa_bts_conn *ipbc = ipc->bts_conn; + struct ipa_proxy_conn *bsc_conn; + struct msgb *msg; + struct ipaccess_head *hh; + int ret = 0; + char *btsbsc; + + if ((bfd->priv_nr & 0xff) <= 2) + btsbsc = "BTS"; + else + btsbsc = "BSC"; + + msg = ipaccess_read_msg(bfd, &ret); + if (!msg) { + if (ret == 0) { + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); + LOGPC(DINP, LOGL_NOTICE, "%s disappeared, " + "dead socket\n", btsbsc); + handle_dead_socket(bfd); + } + return ret; + } + + msgb_put(msg, ret); + logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); + DEBUGPC(DMI, "RX<-%s: %s\n", btsbsc, hexdump(msg->data, msg->len)); + + hh = (struct ipaccess_head *) msg->data; + if (hh->proto == IPAC_PROTO_IPACCESS) { + ret = ipaccess_rcvmsg(ipc, msg, bfd); + if (ret < 0) { + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + talloc_free(bfd); + } + /* we do not forward the CCM protocol through the + * proxy but rather terminate it ourselves */ + msgb_free(msg); + return ret; + } + + if (!ipbc) { + LOGP(DINP, LOGL_ERROR, + "received %s packet but no ipc->bts_conn?!?\n", btsbsc); + msgb_free(msg); + return -EIO; + } + + bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr); + if (bsc_conn) { + /* enqueue packet towards BSC */ + msgb_enqueue(&bsc_conn->tx_queue, msg); + /* mark respective filedescriptor as 'we want to write' */ + bsc_conn->fd.when |= BSC_FD_WRITE; + } else { + logp_ipbc_uid(DINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8); + LOGPC(DINP, LOGL_INFO, "Dropping packet from %s, " + "since remote connection is dead\n", btsbsc); + msgb_free(msg); + } + + return ret; +} + +/* a TCP socket is ready to be written to */ +static int handle_tcp_write(struct bsc_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; + struct ipa_bts_conn *ipbc = ipc->bts_conn; + struct llist_head *lh; + struct msgb *msg; + char *btsbsc; + int ret; + + if ((bfd->priv_nr & 0xff) <= 2) + btsbsc = "BTS"; + else + btsbsc = "BSC"; + + + /* get the next msg for this timeslot */ + if (llist_empty(&ipc->tx_queue)) { + bfd->when &= ~BSC_FD_WRITE; + return 0; + } + lh = ipc->tx_queue.next; + llist_del(lh); + msg = llist_entry(lh, struct msgb, list); + + logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); + DEBUGPC(DMI, "TX %04x: %s\n", bfd->priv_nr, + hexdump(msg->data, msg->len)); + + ret = send(bfd->fd, msg->data, msg->len, 0); + msgb_free(msg); + + if (ret == 0) { + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); + LOGP(DINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc); + handle_dead_socket(bfd); + } + + return ret; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) { + rc = handle_tcp_read(bfd); + if (rc < 0) + return rc; + } + if (what & BSC_FD_WRITE) + rc = handle_tcp_write(bfd); + + return rc; +} + +/* callback of the listening filedescriptor */ +static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) +{ + int ret; + struct ipa_proxy_conn *ipc; + struct bsc_fd *bfd; + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + + if (!(what & BSC_FD_READ)) + return 0; + + ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (ret < 0) { + perror("accept"); + return ret; + } + DEBUGP(DINP, "accept()ed new %s link from %s\n", + (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL", + inet_ntoa(sa.sin_addr)); + + ipc = alloc_conn(); + if (!ipc) { + close(ret); + return -ENOMEM; + } + + bfd = &ipc->fd; + bfd->fd = ret; + bfd->data = ipc; + bfd->priv_nr = listen_bfd->priv_nr; + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ; + ret = bsc_register_fd(bfd); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not register FD\n"); + close(bfd->fd); + talloc_free(ipc); + return ret; + } + + /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ + ret = write(bfd->fd, id_req, sizeof(id_req)); + + return 0; +} + +static int make_listen_sock(struct bsc_fd *bfd, u_int16_t port, int priv_nr, + int (*cb)(struct bsc_fd *fd, unsigned int what)) +{ + struct sockaddr_in addr; + int ret, on = 1; + + bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + bfd->cb = cb; + bfd->when = BSC_FD_READ; + bfd->priv_nr = priv_nr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (!listen_ipaddr) + addr.sin_addr.s_addr = INADDR_ANY; + else + inet_aton(listen_ipaddr, &addr.sin_addr); + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not bind listen socket %s\n", + strerror(errno)); + return -EIO; + } + + ret = listen(bfd->fd, 1); + if (ret < 0) { + perror("listen"); + return ret; + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + perror("register_listen_fd"); + return ret; + } + return 0; +} + +/* Actively connect to a BSC. */ +static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data) +{ + struct ipa_proxy_conn *ipc; + struct bsc_fd *bfd; + int ret, on = 1; + + ipc = alloc_conn(); + if (!ipc) + return NULL; + + ipc->bts_conn = data; + + bfd = &ipc->fd; + bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ | BSC_FD_WRITE; + bfd->data = ipc; + bfd->priv_nr = priv_nr; + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not connect socket\n"); + close(bfd->fd); + talloc_free(ipc); + return NULL; + } + + /* pre-fill tx_queue with identity request */ + ret = bsc_register_fd(bfd); + if (ret < 0) { + close(bfd->fd); + talloc_free(ipc); + return NULL; + } + + return ipc; +} + +static int ipaccess_proxy_setup(void) +{ + int ret; + + ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy); + if (!ipp) + return -ENOMEM; + INIT_LLIST_HEAD(&ipp->bts_list); + ipp->reconn_timer.cb = reconn_tmr_cb; + ipp->reconn_timer.data = ipp; + + /* Listen for OML connections */ + ret = make_listen_sock(&ipp->oml_listen_fd, IPA_TCP_PORT_OML, + OML_FROM_BTS, listen_fd_cb); + if (ret < 0) + return ret; + + /* Listen for RSL connections */ + ret = make_listen_sock(&ipp->rsl_listen_fd, IPA_TCP_PORT_RSL, + RSL_FROM_BTS, listen_fd_cb); + + return ret; +} + +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report_full(tall_bsc_ctx, stderr); + break; + default: + break; + } +} + +int main(int argc, char **argv) +{ + int rc; + + listen_ipaddr = "192.168.100.11"; + bsc_ipaddr = "192.168.100.239"; + + tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy"); + + debug_init(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + debug_set_all_filter(stderr_target, 1); + debug_parse_category_mask(stderr_target, "DINP:DMI"); + + rc = ipaccess_proxy_setup(); + if (rc < 0) + exit(1); + + signal(SIGUSR1, &signal_handler); + signal(SIGABRT, &signal_handler); + + while (1) { + bsc_select_main(0); + } +} diff --git a/openbsc/src/mgcp/mgcp_main.c b/openbsc/src/mgcp/mgcp_main.c new file mode 100644 index 000000000..cea0ba427 --- /dev/null +++ b/openbsc/src/mgcp/mgcp_main.c @@ -0,0 +1,216 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The main method to drive it as a standalone process */ + +/* + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <limits.h> +#include <unistd.h> + +#include <sys/socket.h> + +#include <openbsc/debug.h> +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <openbsc/gsm_data.h> +#include <osmocore/select.h> +#include <openbsc/mgcp.h> +#include <openbsc/telnet_interface.h> + +/* this is here for the vty... it will never be called */ +void subscr_put() { abort(); } + +#define _GNU_SOURCE +#include <getopt.h> + +#warning "Make use of the rtp proxy code" + +static struct bsc_fd bfd; +static int first_request = 1; +static struct mgcp_config *cfg; + +static char *config_file = "mgcp.cfg"; + +/* used by msgb and mgcp */ +void *tall_bsc_ctx = NULL; + +static void print_help() +{ + printf("Some useful help...\n"); + printf(" -h --help is printing this text.\n"); + printf(" -c --config-file filename The config file to use.\n"); +} + +static void handle_options(int argc, char** argv) +{ + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"config-file", 1, 0, 'c'}, + {0, 0, 0, 0}, + }; + + c = getopt_long(argc, argv, "hc:", long_options, &option_index); + + if (c == -1) + break; + + switch(c) { + case 'h': + print_help(); + exit(0); + break; + case 'c': + config_file = talloc_strdup(tall_bsc_ctx, optarg); + break; + default: + /* ignore */ + break; + }; + } +} + +static int read_call_agent(struct bsc_fd *fd, unsigned int what) +{ + struct sockaddr_in addr; + socklen_t slen = sizeof(addr); + struct msgb *msg; + struct msgb *resp; + + msg = (struct msgb *) fd->data; + + /* read one less so we can use it as a \0 */ + int rc = recvfrom(bfd.fd, msg->data, msg->data_len - 1, 0, + (struct sockaddr *) &addr, &slen); + if (rc < 0) { + perror("Gateway failed to read"); + return -1; + } else if (slen > sizeof(addr)) { + fprintf(stderr, "Gateway received message from outerspace: %d %d\n", + slen, sizeof(addr)); + return -1; + } + + if (first_request) { + first_request = 0; + resp = mgcp_create_rsip(); + + if (resp) { + sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0, + (struct sockaddr *) &addr, sizeof(addr)); + msgb_free(resp); + } + return 0; + } + + /* handle message now */ + msg->l2h = msgb_put(msg, rc); + resp = mgcp_handle_message(cfg, msg); + msgb_reset(msg); + + if (resp) { + sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr)); + msgb_free(resp); + } + return 0; +} + + +int main(int argc, char** argv) +{ + struct gsm_network dummy_network; + struct sockaddr_in addr; + int on = 1, rc; + struct debug_target *stderr_target; + + tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent"); + + debug_init(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + debug_set_all_filter(stderr_target, 1); + + cfg = mgcp_config_alloc(); + if (!cfg) + return -1; + + handle_options(argc, argv); + + telnet_init(&dummy_network, 4243); + rc = mgcp_parse_config(config_file, cfg); + if (rc < 0) + return rc; + + + /* we need to bind a socket */ + if (rc == 0) { + bfd.when = BSC_FD_READ; + bfd.cb = read_call_agent; + bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); + if (bfd.fd < 0) { + perror("Gateway failed to listen"); + return -1; + } + + setsockopt(bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(cfg->source_port); + inet_aton(cfg->source_addr, &addr.sin_addr); + + if (bind(bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Gateway failed to bind"); + return -1; + } + + bfd.data = msgb_alloc(4096, "mgcp-msg"); + if (!bfd.data) { + fprintf(stderr, "Gateway memory error.\n"); + return -1; + } + + + if (bsc_register_fd(&bfd) != 0) { + DEBUGP(DMGCP, "Failed to register the fd\n"); + return -1; + } + + DEBUGP(DMGCP, "Configured for MGCP.\n"); + } + + /* initialisation */ + srand(time(NULL)); + + /* main loop */ + while (1) { + bsc_select_main(0); + } + + + return 0; +} diff --git a/openbsc/src/mgcp/mgcp_network.c b/openbsc/src/mgcp/mgcp_network.c new file mode 100644 index 000000000..b76ca4732 --- /dev/null +++ b/openbsc/src/mgcp/mgcp_network.c @@ -0,0 +1,255 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The protocol implementation */ + +/* + * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <string.h> +#include <unistd.h> +#include <endian.h> + +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> + +#include <openbsc/debug.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +#warning "Make use of the rtp proxy code" + +/* according to rtp_proxy.c RFC 3550 */ +struct rtp_hdr { +#if __BYTE_ORDER == __LITTLE_ENDIAN + u_int8_t csrc_count:4, + extension:1, + padding:1, + version:2; + u_int8_t payload_type:7, + marker:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + u_int8_t version:2, + padding:1, + extension:1, + csrc_count:4; + u_int8_t marker:1, + payload_type:7; +#endif + u_int16_t sequence; + u_int32_t timestamp; + u_int32_t ssrc; +} __attribute__((packed)); + + +enum { + DEST_NETWORK = 0, + DEST_BTS = 1, +}; + +enum { + PROTO_RTP, + PROTO_RTCP, +}; + + +static int udp_send(int fd, struct in_addr *addr, int port, char *buf, int len) +{ + struct sockaddr_in out; + out.sin_family = AF_INET; + out.sin_port = port; + memcpy(&out.sin_addr, addr, sizeof(*addr)); + + return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out)); +} + +static void patch_payload(int payload, char *data, int len) +{ + struct rtp_hdr *rtp_hdr; + + if (len < sizeof(*rtp_hdr)) + return; + + rtp_hdr = (struct rtp_hdr *) data; + rtp_hdr->payload_type = payload; +} + +/* + * There is data coming. We will have to figure out if it + * came from the BTS or the MediaGateway of the MSC. On top + * of that we need to figure out if it was RTP or RTCP. + * + * Currently we do not communicate with the BSC so we have + * no idea where the BTS is listening for RTP and need to + * do the classic routing trick. Wait for the first packet + * from the BTS and then go ahead. + */ +static int rtp_data_cb(struct bsc_fd *fd, unsigned int what) +{ + char buf[4096]; + struct sockaddr_in addr; + socklen_t slen = sizeof(addr); + struct mgcp_endpoint *endp; + struct mgcp_config *cfg; + int rc, dest, proto; + + endp = (struct mgcp_endpoint *) fd->data; + cfg = endp->cfg; + + rc = recvfrom(fd->fd, &buf, sizeof(buf), 0, + (struct sockaddr *) &addr, &slen); + if (rc < 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x\n", + ENDPOINT_NUMBER(endp)); + return -1; + } + + /* do not forward aynthing... maybe there is a packet from the bts */ + if (endp->ci == CI_UNUSED) { + LOGP(DMGCP, LOGL_ERROR, "Unknown message on endpoint: 0x%x\n", ENDPOINT_NUMBER(endp)); + return -1; + } + + /* + * Figure out where to forward it to. This code assumes that we + * have received the Connection Modify and know who is a legitimate + * partner. According to the spec we could attempt to forward even + * after the Create Connection but we will not as we are not really + * able to tell if this is legitimate. + */ + #warning "Slight spec violation. With connection mode recvonly we should attempt to forward." + dest = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0 && + (endp->net_rtp == addr.sin_port || endp->net_rtcp == addr.sin_port) + ? DEST_BTS : DEST_NETWORK; + proto = fd == &endp->local_rtp ? PROTO_RTP : PROTO_RTCP; + + /* We have no idea who called us, maybe it is the BTS. */ + if (dest == DEST_NETWORK && (endp->bts_rtp == 0 || cfg->forward_ip)) { + /* it was the BTS... */ + if (!cfg->bts_ip || memcmp(&addr.sin_addr, &cfg->bts_in, sizeof(cfg->bts_in)) == 0) { + if (fd == &endp->local_rtp) { + endp->bts_rtp = addr.sin_port; + } else { + endp->bts_rtcp = addr.sin_port; + } + + endp->bts = addr.sin_addr; + LOGP(DMGCP, LOGL_NOTICE, "Found BTS for endpoint: 0x%x on port: %d/%d\n", + ENDPOINT_NUMBER(endp), ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp)); + } + } + + /* dispatch */ + if (cfg->audio_loop) + dest = !dest; + + if (dest == DEST_NETWORK) { + patch_payload(endp->net_payload_type, buf, rc); + return udp_send(fd->fd, &endp->remote, + proto == PROTO_RTP ? endp->net_rtp : endp->net_rtcp, + buf, rc); + } else { + patch_payload(endp->bts_payload_type, buf, rc); + return udp_send(fd->fd, &endp->bts, + proto == PROTO_RTP ? endp->bts_rtp : endp->bts_rtcp, + buf, rc); + } +} + +static int create_bind(const char *source_addr, struct bsc_fd *fd, int port) +{ + struct sockaddr_in addr; + int on = 1; + + fd->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd->fd < 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to create UDP port.\n"); + return -1; + } + + setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + inet_aton(source_addr, &addr.sin_addr); + + if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + return -1; + } + + return 0; +} + +static int bind_rtp(struct mgcp_endpoint *endp) +{ + struct mgcp_config *cfg = endp->cfg; + + if (create_bind(cfg->source_addr, &endp->local_rtp, endp->rtp_port) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n", + cfg->source_addr, endp->rtp_port, ENDPOINT_NUMBER(endp)); + goto cleanup0; + } + + if (create_bind(cfg->source_addr, &endp->local_rtcp, endp->rtp_port + 1) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n", + cfg->source_addr, endp->rtp_port + 1, ENDPOINT_NUMBER(endp)); + goto cleanup1; + } + + endp->local_rtp.cb = rtp_data_cb; + endp->local_rtp.data = endp; + endp->local_rtp.when = BSC_FD_READ; + if (bsc_register_fd(&endp->local_rtp) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to register RTP port %d on 0x%x\n", + endp->rtp_port, ENDPOINT_NUMBER(endp)); + goto cleanup2; + } + + endp->local_rtcp.cb = rtp_data_cb; + endp->local_rtcp.data = endp; + endp->local_rtcp.when = BSC_FD_READ; + if (bsc_register_fd(&endp->local_rtcp) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to register RTCP port %d on 0x%x\n", + endp->rtp_port + 1, ENDPOINT_NUMBER(endp)); + goto cleanup3; + } + + return 0; + +cleanup3: + bsc_unregister_fd(&endp->local_rtp); +cleanup2: + close(endp->local_rtcp.fd); + endp->local_rtcp.fd = -1; +cleanup1: + close(endp->local_rtp.fd); + endp->local_rtp.fd = -1; +cleanup0: + return -1; +} + +int mgcp_bind_rtp_port(struct mgcp_endpoint *endp, int rtp_port) +{ + endp->rtp_port = rtp_port; + return bind_rtp(endp); +} diff --git a/openbsc/src/mgcp/mgcp_protocol.c b/openbsc/src/mgcp/mgcp_protocol.c new file mode 100644 index 000000000..f7ef5470d --- /dev/null +++ b/openbsc/src/mgcp/mgcp_protocol.c @@ -0,0 +1,745 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The protocol implementation */ + +/* + * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <limits.h> +#include <unistd.h> + +#include <openbsc/debug.h> +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <openbsc/gsm_data.h> +#include <osmocore/select.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +enum mgcp_connection_mode { + MGCP_CONN_NONE = 0, + MGCP_CONN_RECV_ONLY = 1, + MGCP_CONN_SEND_ONLY = 2, + MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY, +}; + +/** + * Macro for tokenizing MGCP messages and SDP in one go. + * + */ +#define MSG_TOKENIZE_START \ + line_start = 0; \ + for (i = 0; i < msgb_l3len(msg); ++i) { \ + /* we have a line end */ \ + if (msg->l3h[i] == '\n') { \ + /* skip the first line */ \ + if (line_start == 0) { \ + line_start = i + 1; \ + continue; \ + } \ + \ + /* check if we have a proper param */ \ + if (i - line_start == 1 && msg->l3h[line_start] == '\r') { \ + } else if (i - line_start > 2 \ + && islower(msg->l3h[line_start]) \ + && msg->l3h[line_start + 1] == '=') { \ + } else if (i - line_start < 3 \ + || msg->l3h[line_start + 1] != ':' \ + || msg->l3h[line_start + 2] != ' ') \ + goto error; \ + \ + msg->l3h[i] = '\0'; \ + if (msg->l3h[i-1] == '\r') \ + msg->l3h[i-1] = '\0'; + +#define MSG_TOKENIZE_END \ + line_start = i + 1; \ + } \ + } + + +struct mgcp_msg_ptr { + unsigned int start; + unsigned int length; +}; + +struct mgcp_request { + char *name; + struct msgb *(*handle_request) (struct mgcp_config *cfg, struct msgb *msg); + char *debug_name; +}; + +#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \ + { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME }, + +static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *msg); +static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg); +static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg); +static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg); + +static int generate_call_id(struct mgcp_config *cfg) +{ + int i; + + /* use the call id */ + ++cfg->last_call_id; + + /* handle wrap around */ + if (cfg->last_call_id == CI_UNUSED) + ++cfg->last_call_id; + + /* callstack can only be of size number_of_endpoints */ + /* verify that the call id is free, e.g. in case of overrun */ + for (i = 1; i < cfg->number_endpoints; ++i) + if (cfg->endpoints[i].ci == cfg->last_call_id) + return generate_call_id(cfg); + + return cfg->last_call_id; +} + +/* FIXIME/TODO: need to have a list of pending transactions and check that */ +static unsigned int generate_transaction_id() +{ + return abs(rand()); +} + +/* + * array of function pointers for handling various + * messages. In the future this might be binary sorted + * for performance reasons. + */ +static const struct mgcp_request mgcp_requests [] = { + MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint") + MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection") + MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection") + MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection") +}; + +static struct msgb *mgcp_msgb_alloc(void) +{ + struct msgb *msg; + msg = msgb_alloc_headroom(4096, 128, "MGCP msg"); + if (!msg) + LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n"); + + return msg; +} + +struct msgb *mgcp_create_response_with_data(int code, const char *msg, const char *trans, + const char *data) +{ + int len; + struct msgb *res; + + res = mgcp_msgb_alloc(); + if (!res) + return NULL; + + if (data) { + len = snprintf((char *) res->data, 2048, "%d %s\n%s", code, trans, data); + } else { + len = snprintf((char *) res->data, 2048, "%d %s\n", code, trans); + } + + res->l2h = msgb_put(res, len); + LOGP(DMGCP, LOGL_DEBUG, "Sending response: code: %d for '%s'\n", code, res->l2h); + return res; +} + +static struct msgb *create_response(int code, const char *msg, const char *trans) +{ + return mgcp_create_response_with_data(code, msg, trans, NULL); +} + +static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, + const char *msg, const char *trans_id) +{ + const char *addr = endp->cfg->local_ip; + char sdp_record[4096]; + + if (!addr) + addr = endp->cfg->source_addr; + + snprintf(sdp_record, sizeof(sdp_record) - 1, + "I: %d\n\n" + "v=0\r\n" + "c=IN IP4 %s\r\n" + "m=audio %d RTP/AVP %d\r\n" + "a=rtpmap:%d %s\r\n", + endp->ci, addr, endp->rtp_port, + endp->bts_payload_type, endp->bts_payload_type, + endp->cfg->audio_name); + return mgcp_create_response_with_data(200, msg, trans_id, sdp_record); +} + +/* send a static record */ +struct msgb *mgcp_create_rsip(void) +{ + struct msgb *msg; + int len; + + msg = mgcp_msgb_alloc(); + if (!msg) + return NULL; + + len = snprintf((char *) msg->data, 2048, + "RSIP %u *@mgw MGCP 1.0\n" + "RM: restart\n", generate_transaction_id()); + msg->l2h = msgb_put(msg, len); + return msg; +} + +/* + * handle incoming messages: + * - this can be a command (four letters, space, transaction id) + * - or a response (three numbers, space, transaction id) + */ +struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) +{ + int code; + struct msgb *resp = NULL; + + if (msg->len < 4) { + LOGP(DMGCP, LOGL_ERROR, "mgs too short: %d\n", msg->len); + return NULL; + } + + /* attempt to treat it as a response */ + if (sscanf((const char *)&msg->data[0], "%3d %*s", &code) == 1) { + LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code); + } else { + int i, handled = 0; + msg->l3h = &msg->l2h[4]; + for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) + if (strncmp(mgcp_requests[i].name, (const char *) &msg->data[0], 4) == 0) { + handled = 1; + resp = mgcp_requests[i].handle_request(cfg, msg); + break; + } + if (!handled) { + LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->data[0]); + } + } + + return resp; +} + +/* string tokenizer for the poor */ +static int find_msg_pointers(struct msgb *msg, struct mgcp_msg_ptr *ptrs, int ptrs_length) +{ + int i, found = 0; + + int whitespace = 1; + for (i = 0; i < msgb_l3len(msg) && ptrs_length > 0; ++i) { + /* if we have a space we found an end */ + if (msg->l3h[i] == ' ' || msg->l3h[i] == '\r' || msg->l3h[i] == '\n') { + if (!whitespace) { + ++found; + whitespace = 1; + ptrs->length = i - ptrs->start - 1; + ++ptrs; + --ptrs_length; + } else { + /* skip any number of whitespace */ + } + + /* line end... stop */ + if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') + break; + } else if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') { + /* line end, be done */ + break; + } else if (whitespace) { + whitespace = 0; + ptrs->start = i; + } + } + + if (ptrs_length == 0) + return -1; + return found; +} + +static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp) +{ + char *endptr = NULL; + unsigned int gw = INT_MAX; + + gw = strtoul(mgcp, &endptr, 16); + if (gw == 0 || gw >= cfg->number_endpoints || strcmp(endptr, "@mgw") != 0) { + LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp); + return NULL; + } + + return &cfg->endpoints[gw]; +} + +static int analyze_header(struct mgcp_config *cfg, struct msgb *msg, + struct mgcp_msg_ptr *ptr, int size, + const char **transaction_id, struct mgcp_endpoint **endp) +{ + int found; + + *transaction_id = "000000"; + + if (size < 3) { + LOGP(DMGCP, LOGL_ERROR, "Not enough space in ptr\n"); + return -1; + } + + found = find_msg_pointers(msg, ptr, size); + + if (found <= 3) { + LOGP(DMGCP, LOGL_ERROR, "Gateway: Not enough params. Found: %d\n", found); + return -1; + } + + /* + * replace the space with \0. the main method gurantess that + * we still have + 1 for null termination + */ + msg->l3h[ptr[3].start + ptr[3].length + 1] = '\0'; + msg->l3h[ptr[2].start + ptr[2].length + 1] = '\0'; + msg->l3h[ptr[1].start + ptr[1].length + 1] = '\0'; + msg->l3h[ptr[0].start + ptr[0].length + 1] = '\0'; + + if (strncmp("1.0", (const char *)&msg->l3h[ptr[3].start], 3) != 0 + || strncmp("MGCP", (const char *)&msg->l3h[ptr[2].start], 4) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Wrong MGCP version. Not handling: '%s' '%s'\n", + (const char *)&msg->l3h[ptr[3].start], + (const char *)&msg->l3h[ptr[2].start]); + return -1; + } + + *transaction_id = (const char *)&msg->l3h[ptr[0].start]; + *endp = find_endpoint(cfg, (const char *)&msg->l3h[ptr[1].start]); + return *endp == NULL; +} + +static int verify_call_id(const struct mgcp_endpoint *endp, + const char *callid) +{ + if (strcmp(endp->callid, callid) != 0) { + LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n", + ENDPOINT_NUMBER(endp), endp->callid, callid); + return -1; + } + + return 0; +} + +static int verify_ci(const struct mgcp_endpoint *endp, + const char *ci) +{ + if (atoi(ci) != endp->ci) { + LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %d != %s\n", + ENDPOINT_NUMBER(endp), endp->ci, ci); + return -1; + } + + return 0; +} + +static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found, response; + const char *trans_id; + struct mgcp_endpoint *endp; + + found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + response = 500; + else + response = 200; + + return create_response(response, "AUEP", trans_id); +} + +static int parse_conn_mode(const char* msg, int *conn_mode) +{ + int ret = 0; + if (strcmp(msg, "recvonly") == 0) + *conn_mode = MGCP_CONN_RECV_ONLY; + else if (strcmp(msg, "sendrecv") == 0) + *conn_mode = MGCP_CONN_RECV_SEND; + else { + LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg); + ret = -1; + } + + return ret; +} + +static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found, i, line_start; + const char *trans_id; + struct mgcp_endpoint *endp; + int error_code = 500; + int port; + + found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + return create_response(500, "CRCX", trans_id); + + if (endp->ci != CI_UNUSED) { + LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n", ENDPOINT_NUMBER(endp)); + return create_response(500, "CRCX", trans_id); + } + + /* parse CallID C: and LocalParameters L: */ + MSG_TOKENIZE_START + switch (msg->l3h[line_start]) { + case 'L': + endp->local_options = talloc_strdup(cfg->endpoints, + (const char *)&msg->l3h[line_start + 3]); + break; + case 'C': + endp->callid = talloc_strdup(cfg->endpoints, + (const char *)&msg->l3h[line_start + 3]); + break; + case 'M': + if (parse_conn_mode((const char *)&msg->l3h[line_start + 3], + &endp->conn_mode) != 0) { + error_code = 517; + goto error2; + } + break; + default: + LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", + msg->l3h[line_start], msg->l3h[line_start], + ENDPOINT_NUMBER(endp)); + break; + } + MSG_TOKENIZE_END + + /* initialize */ + endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0; + + /* set to zero until we get the info */ + memset(&endp->remote, 0, sizeof(endp->remote)); + + /* bind to the port now */ + port = rtp_calculate_port(ENDPOINT_NUMBER(endp), cfg->rtp_base_port); + if (cfg->early_bind) + endp->rtp_port = port; + else if (mgcp_bind_rtp_port(endp, port) != 0) + goto error2; + + /* assign a local call identifier or fail */ + endp->ci = generate_call_id(cfg); + if (endp->ci == CI_UNUSED) + goto error2; + + endp->bts_payload_type = cfg->audio_payload; + + /* policy CB */ + if (cfg->policy_cb) { + switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, trans_id)) { + case MGCP_POLICY_REJECT: + LOGP(DMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n", + ENDPOINT_NUMBER(endp)); + mgcp_free_endp(endp); + return create_response(500, "CRCX", trans_id); + break; + case MGCP_POLICY_DEFER: + /* stop processing */ + return NULL; + break; + case MGCP_POLICY_CONT: + /* just continue */ + break; + } + } + + LOGP(DMGCP, LOGL_NOTICE, "Creating endpoint on: 0x%x CI: %u port: %u\n", + ENDPOINT_NUMBER(endp), endp->ci, endp->rtp_port); + if (cfg->change_cb) + cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, endp->rtp_port); + + return create_response_with_sdp(endp, "CRCX", trans_id); +error: + LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n", + hexdump(msg->l3h, msgb_l3len(msg)), + ENDPOINT_NUMBER(endp), line_start, i); + return create_response(error_code, "CRCX", trans_id); + +error2: + LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp)); + return create_response(error_code, "CRCX", trans_id); +} + +static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found, i, line_start; + const char *trans_id; + struct mgcp_endpoint *endp; + int error_code = 500; + + found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + return create_response(error_code, "MDCX", trans_id); + + if (endp->ci == CI_UNUSED) { + LOGP(DMGCP, LOGL_ERROR, "Endpoint is not holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp)); + return create_response(error_code, "MDCX", trans_id); + } + + MSG_TOKENIZE_START + switch (msg->l3h[line_start]) { + case 'C': { + if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + } + case 'I': { + if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + } + case 'L': + /* skip */ + break; + case 'M': + if (parse_conn_mode((const char *)&msg->l3h[line_start + 3], + &endp->conn_mode) != 0) { + error_code = 517; + goto error3; + } + break; + case '\0': + /* SDP file begins */ + break; + case 'a': + case 'o': + case 's': + case 't': + case 'v': + /* skip these SDP attributes */ + break; + case 'm': { + int port; + int payload; + const char *param = (const char *)&msg->l3h[line_start]; + + if (sscanf(param, "m=audio %d RTP/AVP %d", &port, &payload) == 2) { + endp->net_rtp = htons(port); + endp->net_rtcp = htons(port + 1); + endp->net_payload_type = payload; + } + break; + } + case 'c': { + char ipv4[16]; + const char *param = (const char *)&msg->l3h[line_start]; + + if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) { + inet_aton(ipv4, &endp->remote); + } + break; + } + default: + LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", + msg->l3h[line_start], msg->l3h[line_start], + ENDPOINT_NUMBER(endp)); + break; + } + MSG_TOKENIZE_END + + /* policy CB */ + if (cfg->policy_cb) { + switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, trans_id)) { + case MGCP_POLICY_REJECT: + LOGP(DMGCP, LOGL_NOTICE, "MDCX rejected by policy on 0x%x\n", + ENDPOINT_NUMBER(endp)); + return create_response(500, "MDCX", trans_id); + break; + case MGCP_POLICY_DEFER: + /* stop processing */ + return NULL; + break; + case MGCP_POLICY_CONT: + /* just continue */ + break; + } + } + + /* modify */ + LOGP(DMGCP, LOGL_NOTICE, "Modified endpoint on: 0x%x Server: %s:%u\n", + ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), ntohs(endp->net_rtp)); + if (cfg->change_cb) + cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, endp->rtp_port); + return create_response_with_sdp(endp, "MDCX", trans_id); + +error: + LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d %d\n", + hexdump(msg->l3h, msgb_l3len(msg)), + ENDPOINT_NUMBER(endp), line_start, i, msg->l3h[line_start]); + return create_response(error_code, "MDCX", trans_id); + +error3: + return create_response(error_code, "MDCX", trans_id); +} + +static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found, i, line_start; + const char *trans_id; + struct mgcp_endpoint *endp; + int error_code = 500; + + found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + return create_response(error_code, "DLCX", trans_id); + + if (endp->ci == CI_UNUSED) { + LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp)); + return create_response(error_code, "DLCX", trans_id); + } + + MSG_TOKENIZE_START + switch (msg->l3h[line_start]) { + case 'C': { + if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + } + case 'I': { + if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + } + default: + LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", + msg->l3h[line_start], msg->l3h[line_start], + ENDPOINT_NUMBER(endp)); + break; + } + MSG_TOKENIZE_END + + /* policy CB */ + if (cfg->policy_cb) { + switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, trans_id)) { + case MGCP_POLICY_REJECT: + LOGP(DMGCP, LOGL_NOTICE, "DLCX rejected by policy on 0x%x\n", + ENDPOINT_NUMBER(endp)); + return create_response(500, "DLCX", trans_id); + break; + case MGCP_POLICY_DEFER: + /* stop processing */ + return NULL; + break; + case MGCP_POLICY_CONT: + /* just continue */ + break; + } + } + + /* free the connection */ + mgcp_free_endp(endp); + if (cfg->change_cb) + cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, endp->rtp_port); + + return create_response(250, "DLCX", trans_id); + +error: + LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n", + hexdump(msg->l3h, msgb_l3len(msg)), + ENDPOINT_NUMBER(endp), line_start, i); + return create_response(error_code, "DLCX", trans_id); + +error3: + return create_response(error_code, "DLCX", trans_id); +} + +struct mgcp_config *mgcp_config_alloc(void) +{ + struct mgcp_config *cfg; + + cfg = talloc_zero(NULL, struct mgcp_config); + if (!cfg) { + LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n"); + return NULL; + } + + cfg->source_port = 2427; + cfg->source_addr = talloc_strdup(cfg, "0.0.0.0"); + cfg->audio_name = talloc_strdup(cfg, "GSM-EFR/8000"); + cfg->audio_payload = 97; + cfg->rtp_base_port = RTP_PORT_DEFAULT; + + return cfg; +} + +int mgcp_endpoints_allocate(struct mgcp_config *cfg) +{ + int i; + + /* Initialize all endpoints */ + cfg->endpoints = _talloc_zero_array(cfg, + sizeof(struct mgcp_endpoint), + cfg->number_endpoints, "endpoints"); + if (!cfg->endpoints) + return -1; + + for (i = 0; i < cfg->number_endpoints; ++i) { + cfg->endpoints[i].local_rtp.fd = -1; + cfg->endpoints[i].local_rtcp.fd = -1; + cfg->endpoints[i].ci = CI_UNUSED; + cfg->endpoints[i].cfg = cfg; + cfg->endpoints[i].net_payload_type = -1; + cfg->endpoints[i].bts_payload_type = -1; + } + + return 0; +} + +void mgcp_free_endp(struct mgcp_endpoint *endp) +{ + LOGP(DMGCP, LOGL_NOTICE, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp)); + endp->ci= CI_UNUSED; + + if (endp->callid) { + talloc_free(endp->callid); + endp->callid = NULL; + } + + if (endp->local_options) { + talloc_free(endp->local_options); + endp->callid = NULL; + } + + if (!endp->cfg->early_bind) { + bsc_unregister_fd(&endp->local_rtp); + bsc_unregister_fd(&endp->local_rtcp); + } + + endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0; + endp->net_payload_type = endp->bts_payload_type = -1; +} diff --git a/openbsc/src/mgcp/mgcp_vty.c b/openbsc/src/mgcp/mgcp_vty.c new file mode 100644 index 000000000..f13b3cfa7 --- /dev/null +++ b/openbsc/src/mgcp/mgcp_vty.c @@ -0,0 +1,339 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The protocol implementation */ + +/* + * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <sys/types.h> + +#include <osmocore/talloc.h> + +#include <openbsc/debug.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +#include <vty/command.h> +#include <vty/vty.h> + +static struct mgcp_config *g_cfg = NULL; + +/* + * vty code for mgcp below + */ +struct cmd_node mgcp_node = { + MGCP_NODE, + "%s(mgcp)#", + 1, +}; + +static int config_write_mgcp(struct vty *vty) +{ + vty_out(vty, "mgcp%s", VTY_NEWLINE); + if (g_cfg->local_ip) + vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE); + if (g_cfg->bts_ip) + vty_out(vty, " bts ip %s%s", g_cfg->bts_ip, VTY_NEWLINE); + vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE); + vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE); + vty_out(vty, " bind early %u%s", !!g_cfg->early_bind, VTY_NEWLINE); + vty_out(vty, " rtp base %u%s", g_cfg->rtp_base_port, VTY_NEWLINE); + vty_out(vty, " sdp audio payload number %u%s", g_cfg->audio_payload, VTY_NEWLINE); + vty_out(vty, " sdp audio payload name %s%s", g_cfg->audio_name, VTY_NEWLINE); + vty_out(vty, " loop %u%s", !!g_cfg->audio_loop, VTY_NEWLINE); + vty_out(vty, " endpoints %u%s", g_cfg->number_endpoints, VTY_NEWLINE); + if (g_cfg->forward_ip) + vty_out(vty, " forward audio ip %s%s", g_cfg->forward_ip, VTY_NEWLINE); + if (g_cfg->forward_port != 0) + vty_out(vty, " forward audio port %d%s", g_cfg->forward_port, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp", + SHOW_STR "Display information about the MGCP Media Gateway") +{ + int i; + + vty_out(vty, "MGCP is up and running with %u endpoints:%s", g_cfg->number_endpoints - 1, VTY_NEWLINE); + for (i = 1; i < g_cfg->number_endpoints; ++i) { + struct mgcp_endpoint *endp = &g_cfg->endpoints[i]; + vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u%s", + i, endp->ci, + ntohs(endp->net_rtp), ntohs(endp->net_rtcp), + ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp), VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp, + cfg_mgcp_cmd, + "mgcp", + "Configure the MGCP") +{ + vty->node = MGCP_NODE; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_local_ip, + cfg_mgcp_local_ip_cmd, + "local ip IP", + "Set the IP to be used in SDP records") +{ + if (g_cfg->local_ip) + talloc_free(g_cfg->local_ip); + g_cfg->local_ip = talloc_strdup(g_cfg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bts_ip, + cfg_mgcp_bts_ip_cmd, + "bts ip IP", + "Set the IP of the BTS for RTP forwarding") +{ + if (g_cfg->bts_ip) + talloc_free(g_cfg->bts_ip); + g_cfg->bts_ip = talloc_strdup(g_cfg, argv[0]); + inet_aton(g_cfg->bts_ip, &g_cfg->bts_in); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bind_ip, + cfg_mgcp_bind_ip_cmd, + "bind ip IP", + "Bind the MGCP to this local addr") +{ + if (g_cfg->source_addr) + talloc_free(g_cfg->source_addr); + g_cfg->source_addr = talloc_strdup(g_cfg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bind_port, + cfg_mgcp_bind_port_cmd, + "bind port <0-65534>", + "Bind the MGCP to this port") +{ + unsigned int port = atoi(argv[0]); + if (port > 65534) { + vty_out(vty, "%% wrong bind port '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + g_cfg->source_port = port; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bind_early, + cfg_mgcp_bind_early_cmd, + "bind early (0|1)", + "Bind all RTP ports early") +{ + unsigned int bind = atoi(argv[0]); + if (bind != 0 && bind != 1) { + vty_out(vty, "%% param must be 0 or 1.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + g_cfg->early_bind = bind == 1; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_rtp_base_port, + cfg_mgcp_rtp_base_port_cmd, + "rtp base <0-65534>", + "Base port to use") +{ + unsigned int port = atoi(argv[0]); + if (port > 65534) { + vty_out(vty, "%% wrong base port '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + g_cfg->rtp_base_port = port; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_sdp_payload_number, + cfg_mgcp_sdp_payload_number_cmd, + "sdp audio payload number <1-255>", + "Set the audio codec to use") +{ + unsigned int payload = atoi(argv[0]); + if (payload > 255) { + vty_out(vty, "%% wrong payload number '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + g_cfg->audio_payload = payload; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_sdp_payload_name, + cfg_mgcp_sdp_payload_name_cmd, + "sdp audio payload name NAME", + "Set the audio name to use") +{ + if (g_cfg->audio_name) + talloc_free(g_cfg->audio_name); + g_cfg->audio_name = talloc_strdup(g_cfg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_loop, + cfg_mgcp_loop_cmd, + "loop (0|1)", + "Loop the audio") +{ + g_cfg->audio_loop = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_number_endp, + cfg_mgcp_number_endp_cmd, + "number endpoints <0-65534>", + "The number of endpoints to allocate. This is not dynamic.") +{ + /* + 1 as we start counting at one */ + g_cfg->number_endpoints = atoi(argv[0]) + 1; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_forward_ip, + cfg_mgcp_forward_ip_cmd, + "forward audio ip IP", + "Forward packets from and to the IP. This disables most of the MGCP feature.") +{ + if (g_cfg->forward_ip) + talloc_free(g_cfg->forward_ip); + g_cfg->forward_ip = talloc_strdup(g_cfg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_forward_port, + cfg_mgcp_forward_port_cmd, + "forward audio port <1-15000>", + "Forward packets from and to the port. This disables most of the MGCP feature.") +{ + g_cfg->forward_port = atoi(argv[0]); + return CMD_SUCCESS; +} + +int mgcp_vty_init(void) +{ + install_element(VIEW_NODE, &show_mgcp_cmd); + + install_element(CONFIG_NODE, &cfg_mgcp_cmd); + install_node(&mgcp_node, config_write_mgcp); + install_default(MGCP_NODE); + install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd); + install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd); + install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd); + install_element(MGCP_NODE, &cfg_mgcp_loop_cmd); + install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd); + install_element(MGCP_NODE, &cfg_mgcp_forward_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_forward_port_cmd); + return 0; +} + +int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg) +{ + int i, rc; + + g_cfg = cfg; + rc = vty_read_config_file(config_file); + if (rc < 0) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); + return rc; + } + + + if (!g_cfg->bts_ip) + fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n"); + + if (mgcp_endpoints_allocate(g_cfg) != 0) { + fprintf(stderr, "Failed to allocate endpoints: %d. Quitting.\n", g_cfg->number_endpoints); + return -1; + } + + /* + * This application supports two modes. + * 1.) a true MGCP gateway with support for AUEP, CRCX, MDCX, DLCX + * 2.) plain forwarding of RTP packets on the endpoints. + * both modes are mutual exclusive + */ + if (g_cfg->forward_ip) { + int port = g_cfg->rtp_base_port; + if (g_cfg->forward_port != 0) + port = g_cfg->forward_port; + + if (!g_cfg->early_bind) { + LOGP(DMGCP, LOGL_NOTICE, "Forwarding requires early bind.\n"); + return -1; + } + + /* + * Store the forward IP and assign a ci. For early bind + * the sockets will be created after this. + */ + for (i = 1; i < g_cfg->number_endpoints; ++i) { + struct mgcp_endpoint *endp = &g_cfg->endpoints[i]; + inet_aton(g_cfg->forward_ip, &endp->remote); + endp->ci = CI_UNUSED + 23; + endp->net_rtp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port)); + endp->net_rtcp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port) + 1); + } + + LOGP(DMGCP, LOGL_NOTICE, "Configured for Audio Forwarding.\n"); + } + + /* early bind */ + if (g_cfg->early_bind) { + for (i = 1; i < g_cfg->number_endpoints; ++i) { + struct mgcp_endpoint *endp = &g_cfg->endpoints[i]; + int rtp_port; + + rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), g_cfg->rtp_base_port); + if (mgcp_bind_rtp_port(endp, rtp_port) != 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port); + return -1; + } + } + } + + return !!g_cfg->forward_ip; +} + +struct gsm_network; +int bsc_vty_init(struct gsm_network *dummy) +{ + cmd_init(1); + vty_init(); + + mgcp_vty_init(); + return 0; +} + diff --git a/openbsc/src/mncc.c b/openbsc/src/mncc.c index 15e2978e6..01d59aad1 100644 --- a/openbsc/src/mncc.c +++ b/openbsc/src/mncc.c @@ -28,7 +28,7 @@ #include <openbsc/gsm_04_08.h> #include <openbsc/debug.h> #include <openbsc/mncc.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/gsm_data.h> #include <openbsc/transaction.h> #include <openbsc/rtp_proxy.h> diff --git a/openbsc/src/msgb.c b/openbsc/src/msgb.c deleted file mode 100644 index 914b36ae7..000000000 --- a/openbsc/src/msgb.c +++ /dev/null @@ -1,104 +0,0 @@ -/* (C) 2008 by Harald Welte <laforge@gnumonks.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - - -#include <unistd.h> -#include <string.h> -#include <stdlib.h> -#include <sys/types.h> - -#include <openbsc/msgb.h> -#include <openbsc/gsm_data.h> -#include <openbsc/talloc.h> -#include <openbsc/debug.h> - -static void *tall_msgb_ctx; - -struct msgb *msgb_alloc(u_int16_t size, const char *name) -{ - struct msgb *msg; - - msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name); - - if (!msg) { - LOGP(DRSL, LOGL_FATAL, "unable to allocate msgb\n"); - return NULL; - } - - msg->data_len = size; - msg->len = 0; - msg->data = msg->_data; - - msg->head = msg->data; - msg->data = msg->data; - /* reset tail pointer */ - msg->tail = msg->data; - //msg->end = msg->tail + size; - - return msg; -} - -void msgb_free(struct msgb *m) -{ - talloc_free(m); -} - -void msgb_enqueue(struct llist_head *queue, struct msgb *msg) -{ - llist_add_tail(&msg->list, queue); -} - -struct msgb *msgb_dequeue(struct llist_head *queue) -{ - struct llist_head *lh; - - if (llist_empty(queue)) - return NULL; - - lh = queue->next; - llist_del(lh); - - return llist_entry(lh, struct msgb, list); -} - -void msgb_reset(struct msgb *msg) -{ - msg->len = 0; - msg->len = 0; - msg->data = msg->_data; - - msg->head = msg->data; - msg->data = msg->data; - /* reset tail pointer */ - msg->tail = msg->data; - - /* reset pointers */ - msg->bts_link = NULL; - msg->trx = NULL; - msg->lchan = NULL; - msg->l2h = NULL; - msg->l3h = NULL; - msg->smsh = NULL; - msg->l4h = NULL; -} - -static __attribute__((constructor)) void on_dso_load_trau_msgb(void) -{ - tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 1, "msgb"); -} diff --git a/openbsc/src/nat/bsc_filter.c b/openbsc/src/nat/bsc_filter.c index ad2f6138f..051e53087 100644 --- a/openbsc/src/nat/bsc_filter.c +++ b/openbsc/src/nat/bsc_filter.c @@ -24,9 +24,10 @@ #include <openbsc/bsc_nat.h> #include <openbsc/bssap.h> #include <openbsc/ipaccess.h> -#include <openbsc/talloc.h> #include <openbsc/debug.h> +#include <osmocore/talloc.h> + #include <sccp/sccp.h> /* diff --git a/openbsc/src/nat/bsc_nat.c b/openbsc/src/nat/bsc_nat.c index 6ce78be7f..09df4dcb6 100644 --- a/openbsc/src/nat/bsc_nat.c +++ b/openbsc/src/nat/bsc_nat.c @@ -36,19 +36,20 @@ #include <getopt.h> #include <openbsc/debug.h> -#include <openbsc/msgb.h> #include <openbsc/bsc_msc.h> #include <openbsc/bsc_nat.h> #include <openbsc/bssap.h> #include <openbsc/ipaccess.h> #include <openbsc/abis_nm.h> -#include <openbsc/talloc.h> #include <openbsc/telnet_interface.h> +#include <osmocore/talloc.h> + #include <vty/vty.h> #include <sccp/sccp.h> +struct debug_target *stderr_target; static const char *config_file = "bsc-nat.cfg"; static char *msc_address = "127.0.0.1"; static struct in_addr local_addr; @@ -309,7 +310,7 @@ static int forward_sccp_to_bts(struct msgb *msg) if (!bsc) return -1; if (!bsc->authenticated) { - LOGP(DNAT, LOGL_ERRO, "Selected BSC not authenticated.\n"); + LOGP(DNAT, LOGL_ERROR, "Selected BSC not authenticated.\n"); return -1; } @@ -707,16 +708,16 @@ static void handle_options(int argc, char** argv) print_help(); exit(0); case 's': - debug_use_color(0); + debug_set_use_color(stderr_target, 0); break; case 'd': - debug_parse_category_mask(optarg); + debug_parse_category_mask(stderr_target, optarg); break; case 'c': config_file = strdup(optarg); break; case 'T': - debug_timestamp(1); + debug_set_print_timestamp(stderr_target, 1); break; case 'm': msc_address = strdup(optarg); @@ -749,6 +750,11 @@ int main(int argc, char** argv) { int rc; + debug_init(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + debug_set_all_filter(stderr_target, 1); + /* parse options */ local_addr.s_addr = INADDR_ANY; handle_options(argc, argv); diff --git a/openbsc/src/nat/bsc_nat_vty.c b/openbsc/src/nat/bsc_nat_vty.c index 24ef39835..5430670c9 100644 --- a/openbsc/src/nat/bsc_nat_vty.c +++ b/openbsc/src/nat/bsc_nat_vty.c @@ -25,7 +25,8 @@ #include <openbsc/bsc_nat.h> #include <openbsc/gsm_04_08.h> -#include <openbsc/talloc.h> + +#include <osmocore/talloc.h> #include <sccp/sccp.h> diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c index c64bffdbd..9c978bee6 100644 --- a/openbsc/src/paging.c +++ b/openbsc/src/paging.c @@ -40,7 +40,7 @@ #include <assert.h> #include <openbsc/paging.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/debug.h> #include <openbsc/signal.h> #include <openbsc/abis_rsl.h> @@ -212,9 +212,9 @@ static void paging_T3113_expired(void *data) cbfn = req->cbfn; paging_remove_request(&req->bts->paging, req); - req->bts->network->stats.paging.expired++; + counter_inc(req->bts->network->stats.paging.expired); - dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data); + dispatch_signal(SS_PAGING, S_PAGING_EXPIRED, &sig_data); if (cbfn) cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL, cbfn_param); @@ -256,7 +256,7 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, struct gsm_bts *bts = NULL; int num_pages = 0; - network->stats.paging.attempted++; + counter_inc(network->stats.paging.attempted); /* start paging subscriber on all BTS within Location Area */ do { @@ -265,6 +265,11 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, bts = gsm_bts_by_lac(network, subscr->lac, bts); if (!bts) break; + + /* skip all currently inactive TRX */ + if (!trx_is_usable(bts->c0)) + continue; + num_pages++; /* Trigger paging, pass any error to caller */ @@ -274,7 +279,7 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, } while (1); if (num_pages == 0) - network->stats.paging.detached++; + counter_inc(network->stats.paging.detached); return num_pages; } diff --git a/openbsc/src/rest_octets.c b/openbsc/src/rest_octets.c index 6c8c40314..a57e7dffe 100644 --- a/openbsc/src/rest_octets.c +++ b/openbsc/src/rest_octets.c @@ -27,7 +27,7 @@ #include <errno.h> #include <openbsc/gsm_data.h> -#include <openbsc/bitvec.h> +#include <osmocore/bitvec.h> #include <openbsc/rest_octets.h> /* generate SI1 rest octets */ @@ -46,7 +46,7 @@ int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos) bitvec_set_bit(&bv, L); bitvec_spare_padding(&bv, 7); - return 0; + return bv.data_len; } /* Append selection parameters to bitvec */ @@ -125,7 +125,8 @@ int rest_octets_si3(u_int8_t *data, const struct gsm48_si_ro_info *si3) /* GPRS Indicator */ append_gprs_ind(&bv, &si3->gprs_ind); - return bitvec_spare_padding(&bv, (bv.data_len*8)-1); + bitvec_spare_padding(&bv, (bv.data_len*8)-1); + return bv.data_len; } static int append_lsa_params(struct bitvec *bv, @@ -178,7 +179,7 @@ int rest_octets_si4(u_int8_t *data, const struct gsm48_si_ro_info *si4) bitvec_set_bit(&bv, si4->break_ind ? H : L); } - return 0; + return bv.data_len; } /* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a: @@ -390,5 +391,6 @@ int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13) } } } - return bitvec_spare_padding(&bv, (bv.data_len*8)-1); + bitvec_spare_padding(&bv, (bv.data_len*8)-1); + return bv.data_len; } diff --git a/openbsc/src/rrlp.c b/openbsc/src/rrlp.c index d4665d570..35044518c 100644 --- a/openbsc/src/rrlp.c +++ b/openbsc/src/rrlp.c @@ -89,14 +89,12 @@ static int paging_sig_cb(unsigned int subsys, unsigned int signal, struct paging_signal_data *psig_data = signal_data; switch (signal) { - case S_PAGING_COMPLETED: - /* paging might have "completed' unsucessfully, - * in this case we don't have a lchan */ - if (!psig_data->lchan) - break; + case S_PAGING_SUCCEEDED: /* A subscriber has attached. */ send_rrlp_req(psig_data->lchan); break; + case S_PAGING_EXPIRED: + break; } return 0; } diff --git a/openbsc/src/rs232.c b/openbsc/src/rs232.c index a58472364..36af59cbf 100644 --- a/openbsc/src/rs232.c +++ b/openbsc/src/rs232.c @@ -28,8 +28,8 @@ #include <termios.h> #include <fcntl.h> -#include <openbsc/select.h> -#include <openbsc/msgb.h> +#include <osmocore/select.h> +#include <osmocore/msgb.h> #include <openbsc/debug.h> #include <openbsc/gsm_data.h> #include <openbsc/rs232.h> diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c index 0f4e32799..9f2e2fd76 100644 --- a/openbsc/src/rtp_proxy.c +++ b/openbsc/src/rtp_proxy.c @@ -19,6 +19,7 @@ * */ +#include <endian.h> #include <errno.h> #include <unistd.h> #include <sys/socket.h> @@ -29,10 +30,10 @@ #include <time.h> /* clock() */ #include <sys/utsname.h> /* uname() */ -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/gsm_data.h> -#include <openbsc/msgb.h> -#include <openbsc/select.h> +#include <osmocore/msgb.h> +#include <osmocore/select.h> #include <openbsc/debug.h> #include <openbsc/rtp_proxy.h> @@ -63,12 +64,21 @@ struct rtcp_hdr { /* according to RFC 3550 */ struct rtp_hdr { +#if __BYTE_ORDER == __LITTLE_ENDIAN u_int8_t csrc_count:4, extension:1, padding:1, version:2; u_int8_t payload_type:7, marker:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + u_int8_t version:2, + padding:1, + extension:1, + csrc_count:4; + u_int8_t marker:1, + payload_type:7; +#endif u_int16_t sequence; u_int32_t timestamp; u_int32_t ssrc; @@ -240,7 +250,8 @@ int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame) if (abs(frame_diff) > 1) { long int frame_diff_excess = frame_diff - 1; - DEBUGP(DMUX, "Correcting frame difference of %ld frames\n", frame_diff_excess); + LOGP(DMUX, LOGL_NOTICE, + "Correcting frame difference of %ld frames\n", frame_diff_excess); rs->transmit.sequence += frame_diff_excess; rs->transmit.timestamp += frame_diff_excess * duration; } diff --git a/openbsc/src/sccp/sccp.c b/openbsc/src/sccp/sccp.c index 8b3a487a0..b1da2c721 100644 --- a/openbsc/src/sccp/sccp.c +++ b/openbsc/src/sccp/sccp.c @@ -2,7 +2,7 @@ * SCCP management code * * (C) 2009, 2010 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2009, 2010 by on-waves.com + * (C) 2009, 2010 by On-Waves * * All Rights Reserved * @@ -24,11 +24,12 @@ #include <string.h> +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <osmocore/talloc.h> + #include <sccp/sccp.h> -#include <openbsc/debug.h> -#include <openbsc/talloc.h> -#include <openbsc/linuxlist.h> static void *tall_sccp_ctx; static LLIST_HEAD(sccp_connections); @@ -208,7 +209,7 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result * static const u_int32_t called_offset = offsetof(struct sccp_connection_request, variable_called); - struct sccp_connection_request *req = (struct sccp_connection_request *)msgb->data; + struct sccp_connection_request *req = (struct sccp_connection_request *)msgb->l2h; struct sccp_optional_data optional_data; /* header check */ @@ -459,6 +460,7 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result) msgb->l3h = &udt->data[udt->variable_data]; + result->data_len = msgb_l3len(msgb); if (msgb_l3len(msgb) != msgb->l3h[-1]) { DEBUGP(DSCCP, "msgb is truncated is: %u should: %u\n", @@ -469,6 +471,25 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result) return 0; } +static int _sccp_parse_it(struct msgb *msgb, struct sccp_parse_result *result) +{ + static const u_int32_t header_size = sizeof(struct sccp_data_it); + + struct sccp_data_it *it; + + if (msgb_l2len(msgb) < header_size) { + DEBUGP(DSCCP, "msgb < header_size %u %u\n", + msgb_l2len(msgb), header_size); + return -1; + } + + it = (struct sccp_data_it *) msgb->l2h; + result->data_len = 0; + result->source_local_reference = &it->source_local_reference; + result->destination_local_reference = &it->destination_local_reference; + return 0; +} + /* * Send UDT. Currently we have a fixed address... @@ -1305,8 +1326,12 @@ int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result) case SCCP_MSG_TYPE_UDT: return _sccp_parse_udt(msg, result); break; + case SCCP_MSG_TYPE_IT: + return _sccp_parse_it(msg, result); + break; }; + LOGP(DSCCP, LOGL_ERROR, "Unimplemented MSG Type: 0x%x\n", type); return -1; } diff --git a/openbsc/src/select.c b/openbsc/src/select.c deleted file mode 100644 index c11f3a511..000000000 --- a/openbsc/src/select.c +++ /dev/null @@ -1,118 +0,0 @@ -/* select filedescriptor handling, taken from: - * userspace logging daemon for the iptables ULOG target - * of the linux 2.4 netfilter subsystem. - * - * (C) 2000-2009 by Harald Welte <laforge@gnumonks.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <fcntl.h> -#include <openbsc/select.h> -#include <openbsc/linuxlist.h> -#include <openbsc/timer.h> - -static int maxfd = 0; -static LLIST_HEAD(bsc_fds); -static int unregistered_count; - -int bsc_register_fd(struct bsc_fd *fd) -{ - int flags; - - /* make FD nonblocking */ - flags = fcntl(fd->fd, F_GETFL); - if (flags < 0) - return flags; - flags |= O_NONBLOCK; - flags = fcntl(fd->fd, F_SETFL, flags); - if (flags < 0) - return flags; - - /* Register FD */ - if (fd->fd > maxfd) - maxfd = fd->fd; - - llist_add_tail(&fd->list, &bsc_fds); - - return 0; -} - -void bsc_unregister_fd(struct bsc_fd *fd) -{ - unregistered_count++; - llist_del(&fd->list); -} - -int bsc_select_main(int polling) -{ - struct bsc_fd *ufd, *tmp; - fd_set readset, writeset, exceptset; - int work = 0, rc; - struct timeval no_time = {0, 0}; - - FD_ZERO(&readset); - FD_ZERO(&writeset); - FD_ZERO(&exceptset); - - /* prepare read and write fdsets */ - llist_for_each_entry(ufd, &bsc_fds, list) { - if (ufd->when & BSC_FD_READ) - FD_SET(ufd->fd, &readset); - - if (ufd->when & BSC_FD_WRITE) - FD_SET(ufd->fd, &writeset); - - if (ufd->when & BSC_FD_EXCEPT) - FD_SET(ufd->fd, &exceptset); - } - - bsc_timer_check(); - - if (!polling) - bsc_prepare_timers(); - rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : bsc_nearest_timer()); - if (rc < 0) - return 0; - - /* fire timers */ - bsc_update_timers(); - - /* call registered callback functions */ -restart: - unregistered_count = 0; - llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) { - int flags = 0; - - if (FD_ISSET(ufd->fd, &readset)) - flags |= BSC_FD_READ; - - if (FD_ISSET(ufd->fd, &writeset)) - flags |= BSC_FD_WRITE; - - if (FD_ISSET(ufd->fd, &exceptset)) - flags |= BSC_FD_EXCEPT; - - if (flags) { - work = 1; - ufd->cb(ufd, flags); - } - /* ugly, ugly hack. If more than one filedescriptors were - * unregistered, they might have been consecutive and - * llist_for_each_entry_safe() is no longer safe */ - if (unregistered_count > 1) - goto restart; - } - return work; -} diff --git a/openbsc/src/signal.c b/openbsc/src/signal.c deleted file mode 100644 index e04cadf73..000000000 --- a/openbsc/src/signal.c +++ /dev/null @@ -1,83 +0,0 @@ -/* Generic signalling/notification infrastructure */ -/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <openbsc/signal.h> -#include <openbsc/talloc.h> -#include <stdlib.h> -#include <string.h> - - -void *tall_sigh_ctx; -static LLIST_HEAD(signal_handler_list); - -struct signal_handler { - struct llist_head entry; - unsigned int subsys; - signal_cbfn *cbfn; - void *data; -}; - - -int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data) -{ - struct signal_handler *sig_data; - - sig_data = talloc(tall_sigh_ctx, struct signal_handler); - if (!sig_data) - return -ENOMEM; - - memset(sig_data, 0, sizeof(*sig_data)); - - sig_data->subsys = subsys; - sig_data->data = data; - sig_data->cbfn = cbfn; - - /* FIXME: check if we already have a handler for this subsys/cbfn/data */ - - llist_add_tail(&sig_data->entry, &signal_handler_list); - - return 0; -} - -void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data) -{ - struct signal_handler *handler; - - llist_for_each_entry(handler, &signal_handler_list, entry) { - if (handler->cbfn == cbfn && handler->data == data - && subsys == handler->subsys) { - llist_del(&handler->entry); - talloc_free(handler); - break; - } - } -} - - -void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data) -{ - struct signal_handler *handler; - - llist_for_each_entry(handler, &signal_handler_list, entry) { - if (handler->subsys != subsys) - continue; - (*handler->cbfn)(subsys, signal, handler->data, signal_data); - } -} diff --git a/openbsc/src/silent_call.c b/openbsc/src/silent_call.c index 82b656327..cada24e66 100644 --- a/openbsc/src/silent_call.c +++ b/openbsc/src/silent_call.c @@ -25,7 +25,7 @@ #include <unistd.h> #include <errno.h> -#include <openbsc/msgb.h> +#include <osmocore/msgb.h> #include <openbsc/signal.h> #include <openbsc/debug.h> #include <openbsc/paging.h> @@ -34,6 +34,7 @@ #include <openbsc/abis_rsl.h> #include <openbsc/chan_alloc.h> +/* paging of the requested subscriber has completed */ static int paging_cb_silent(unsigned int hooknum, unsigned int event, struct msgb *msg, void *_lchan, void *_data) { @@ -53,6 +54,7 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event, case GSM_PAGING_SUCCEEDED: DEBUGPC(DSMS, "success, using Timeslot %u on ARFCN %u\n", lchan->ts->nr, lchan->ts->trx->arfcn); + lchan->silent_call = 1; /* increment lchan reference count */ dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata); use_lchan(lchan); @@ -69,15 +71,58 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event, return rc; } -int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data) +/* receive a layer 3 message from a silent call */ +int silent_call_rx(struct msgb *msg) +{ + /* FIXME: do something like sending it through a UDP port */ + return 0; +} + +struct msg_match { + u_int8_t pdisc; + u_int8_t msg_type; +}; + +/* list of messages that are handled inside OpenBSC, even in a silent call */ +static const struct msg_match silent_call_accept[] = { + { GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST }, + { GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_REQ }, +}; + +/* decide if we need to reroute a message as part of a silent call */ +int silent_call_reroute(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t pdisc = gh->proto_discr & 0x0f; + int i; + + /* if we're not part of a silent call, never reroute */ + if (!msg->lchan->silent_call) + return 0; + + /* check if we are a special message that is handled in openbsc */ + for (i = 0; i < ARRAY_SIZE(silent_call_accept); i++) { + if (silent_call_accept[i].pdisc == pdisc && + silent_call_accept[i].msg_type == gh->msg_type) + return 0; + } + + /* otherwise, reroute */ + return 1; +} + + +/* initiate a silent call with a given subscriber */ +int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type) { int rc; - rc = paging_request(subscr->net, subscr, RSL_CHANNEED_TCH_F, + rc = paging_request(subscr->net, subscr, type, paging_cb_silent, data); return rc; } +/* end a silent call with a given subscriber */ int gsm_silent_call_stop(struct gsm_subscriber *subscr) { struct gsm_lchan *lchan; @@ -86,7 +131,10 @@ int gsm_silent_call_stop(struct gsm_subscriber *subscr) if (!lchan) return -EINVAL; - /* FIXME: did we actually establish a silent call for this guy? */ + /* did we actually establish a silent call for this guy? */ + if (!lchan->silent_call) + return -EINVAL; + put_lchan(lchan); return 0; diff --git a/openbsc/src/subchan_demux.c b/openbsc/src/subchan_demux.c index 63be533df..0d6c1febe 100644 --- a/openbsc/src/subchan_demux.c +++ b/openbsc/src/subchan_demux.c @@ -28,7 +28,7 @@ #include <openbsc/subchan_demux.h> #include <openbsc/trau_frame.h> #include <openbsc/debug.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/gsm_data.h> void *tall_tqe_ctx; diff --git a/openbsc/src/system_information.c b/openbsc/src/system_information.c index 7625da637..a9df0ba26 100644 --- a/openbsc/src/system_information.c +++ b/openbsc/src/system_information.c @@ -31,13 +31,29 @@ #include <openbsc/gsm_data.h> #include <openbsc/abis_rsl.h> #include <openbsc/rest_octets.h> -#include <openbsc/bitvec.h> +#include <osmocore/bitvec.h> #include <openbsc/debug.h> #define GSM48_CELL_CHAN_DESC_SIZE 16 #define GSM_MACBLOCK_LEN 23 #define GSM_MACBLOCK_PADDING 0x2b +/* verify the sizes of the system information type structs */ + +/* rest octets are not part of the struct */ +static_assert(sizeof(struct gsm48_system_information_type_header) == 3, _si_header_size); +static_assert(sizeof(struct gsm48_rach_control) == 3, _si_rach_control); +static_assert(sizeof(struct gsm48_system_information_type_1) == 22, _si1_size); +static_assert(sizeof(struct gsm48_system_information_type_2) == 23, _si2_size); +static_assert(sizeof(struct gsm48_system_information_type_3) == 19, _si3_size); +static_assert(sizeof(struct gsm48_system_information_type_4) == 13, _si4_size); + +/* bs11 forgot the l2 len, 0-6 rest octets */ +static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size); +static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size); + +static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size); + /* Frequency Lists as per TS 04.08 10.5.2.13 */ /* 10.5.2.13.2: Bit map 0 format */ @@ -98,7 +114,7 @@ static int freq_list_bmrel_set_arfcn(u_int8_t *chan_list, unsigned int arfcn) static int bitvec2freq_list(u_int8_t *chan_list, struct bitvec *bv, const struct gsm_bts *bts) { - int i, rc, min = 1024, max = 0; + int i, rc, min = 1024, max = -1; memset(chan_list, 0, 16); @@ -128,6 +144,12 @@ static int bitvec2freq_list(u_int8_t *chan_list, struct bitvec *bv, } } + if (max == -1) { + /* Empty set, use 'bit map 0 format' */ + chan_list[0] = 0; + return 0; + } + if ((max - min) > 111) { LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, " "distance > 111\n", min, max); @@ -200,9 +222,8 @@ static int generate_si1(u_int8_t *output, struct gsm_bts *bts) si1->rach_control = bts->si_common.rach_control; /* SI1 Rest Octets (10.5.2.32), contains NCH position */ - rest_octets_si1(si1->rest_octets, NULL); - - return GSM_MACBLOCK_LEN; + rc = rest_octets_si1(si1->rest_octets, NULL); + return sizeof(*si1) + rc; } static int generate_si2(u_int8_t *output, struct gsm_bts *bts) @@ -225,7 +246,7 @@ static int generate_si2(u_int8_t *output, struct gsm_bts *bts) si2->ncc_permitted = bts->si_common.ncc_permitted; si2->rach_control = bts->si_common.rach_control; - return GSM_MACBLOCK_LEN; + return sizeof(*si2); } struct gsm48_si_ro_info si_info = { @@ -254,6 +275,7 @@ struct gsm48_si_ro_info si_info = { static int generate_si3(u_int8_t *output, struct gsm_bts *bts) { + int rc; struct gsm48_system_information_type_3 *si3 = (struct gsm48_system_information_type_3 *) output; @@ -265,9 +287,9 @@ static int generate_si3(u_int8_t *output, struct gsm_bts *bts) si3->header.system_information = GSM48_MT_RR_SYSINFO_3; si3->cell_identity = htons(bts->cell_identity); - gsm0408_generate_lai(&si3->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); + gsm48_generate_lai(&si3->lai, bts->network->country_code, + bts->network->network_code, + bts->location_area_code); si3->control_channel_desc = bts->si_common.chan_desc; si3->cell_options = bts->si_common.cell_options; si3->cell_sel_par = bts->si_common.cell_sel_par; @@ -277,13 +299,14 @@ static int generate_si3(u_int8_t *output, struct gsm_bts *bts) CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME Power Offset, 2ter Indicator, Early Classmark Sending, Scheduling if and WHERE, GPRS Indicator, SI13 position */ - rest_octets_si3(si3->rest_octets, &si_info); + rc = rest_octets_si3(si3->rest_octets, &si_info); - return GSM_MACBLOCK_LEN; + return sizeof(*si3) + rc; } static int generate_si4(u_int8_t *output, struct gsm_bts *bts) { + int rc; struct gsm48_system_information_type_4 *si4 = (struct gsm48_system_information_type_4 *) output; @@ -296,9 +319,9 @@ static int generate_si4(u_int8_t *output, struct gsm_bts *bts) si4->header.skip_indicator = 0; si4->header.system_information = GSM48_MT_RR_SYSINFO_4; - gsm0408_generate_lai(&si4->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); + gsm48_generate_lai(&si4->lai, bts->network->country_code, + bts->network->network_code, + bts->location_area_code); si4->cell_sel_par = bts->si_common.cell_sel_par; si4->rach_control = bts->si_common.rach_control; @@ -309,9 +332,9 @@ static int generate_si4(u_int8_t *output, struct gsm_bts *bts) /* SI4 Rest Octets (10.5.2.35), containing Optional Power offset, GPRS Indicator, Cell Identity, LSA ID, Selection Parameter */ - rest_octets_si4(si4->data, &si_info); + rc = rest_octets_si4(si4->data, &si_info); - return GSM_MACBLOCK_LEN; + return sizeof(*si4) + rc; } static int generate_si5(u_int8_t *output, struct gsm_bts *bts) @@ -361,9 +384,9 @@ static int generate_si6(u_int8_t *output, struct gsm_bts *bts) si6->skip_indicator = 0; si6->system_information = GSM48_MT_RR_SYSINFO_6; si6->cell_identity = htons(bts->cell_identity); - gsm0408_generate_lai(&si6->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); + gsm48_generate_lai(&si6->lai, bts->network->country_code, + bts->network->network_code, + bts->location_area_code); si6->cell_options = bts->si_common.cell_options; si6->ncc_permitted = bts->si_common.ncc_permitted; @@ -418,7 +441,7 @@ static int generate_si13(u_int8_t *output, struct gsm_bts *bts) si13->header.l2_plen = ret & 0xff; - return GSM_MACBLOCK_LEN; + return sizeof (*si13) + ret; } int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type) diff --git a/openbsc/src/talloc.c b/openbsc/src/talloc.c deleted file mode 100644 index d8213238e..000000000 --- a/openbsc/src/talloc.c +++ /dev/null @@ -1,1805 +0,0 @@ -/* - Samba Unix SMB/CIFS implementation. - - Samba trivial allocation library - new interface - - NOTE: Please read talloc_guide.txt for full documentation - - Copyright (C) Andrew Tridgell 2004 - Copyright (C) Stefan Metzmacher 2006 - - ** NOTE! The following LGPL license applies to the talloc - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see <http://www.gnu.org/licenses/>. -*/ - -/* - inspired by http://swapped.cc/halloc/ -*/ - -#ifdef _SAMBA_BUILD_ -#include "version.h" -#if (SAMBA_VERSION_MAJOR<4) -#include "includes.h" -/* This is to circumvent SAMBA3's paranoid malloc checker. Here in this file - * we trust ourselves... */ -#ifdef malloc -#undef malloc -#endif -#ifdef realloc -#undef realloc -#endif -#define _TALLOC_SAMBA3 -#endif /* (SAMBA_VERSION_MAJOR<4) */ -#endif /* _SAMBA_BUILD_ */ - -#ifndef _TALLOC_SAMBA3 -//#include "replace.h" -#include <sys/types.h> -#include <unistd.h> -#include <stdio.h> -#include <stdbool.h> -#define __USE_GNU -#include <string.h> -#undef __USE_GNU -#include <openbsc/talloc.h> -#define MIN(x,y) ((x) < (y) ? (x) : (y)) -#endif /* not _TALLOC_SAMBA3 */ - -/* use this to force every realloc to change the pointer, to stress test - code that might not cope */ -#define ALWAYS_REALLOC 0 - - -#define MAX_TALLOC_SIZE 0x10000000 -#define TALLOC_MAGIC 0xe814ec70 -#define TALLOC_FLAG_FREE 0x01 -#define TALLOC_FLAG_LOOP 0x02 -#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */ -#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */ -#define TALLOC_MAGIC_REFERENCE ((const char *)1) - -/* by default we abort when given a bad pointer (such as when talloc_free() is called - on a pointer that came from malloc() */ -#ifndef TALLOC_ABORT -#define TALLOC_ABORT(reason) abort() -#endif - -#ifndef discard_const_p -#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) -# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) -#else -# define discard_const_p(type, ptr) ((type *)(ptr)) -#endif -#endif - -/* these macros gain us a few percent of speed on gcc */ -#if (__GNUC__ >= 3) -/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 - as its first argument */ -#ifndef likely -#define likely(x) __builtin_expect(!!(x), 1) -#endif -#ifndef unlikely -#define unlikely(x) __builtin_expect(!!(x), 0) -#endif -#else -#ifndef likely -#define likely(x) (x) -#endif -#ifndef unlikely -#define unlikely(x) (x) -#endif -#endif - -#ifdef __APPLE__ -/* taken from http://insanecoding.blogspot.com/2007/03/methods-for-safe-string-handling.html */ -size_t strnlen(const char *s, size_t n) -{ - const char *p = (const char *)memchr(s, 0, n); - return(p ? p-s : n); -} -#endif - -/* this null_context is only used if talloc_enable_leak_report() or - talloc_enable_leak_report_full() is called, otherwise it remains - NULL -*/ -static void *null_context; -static void *autofree_context; - -struct talloc_reference_handle { - struct talloc_reference_handle *next, *prev; - void *ptr; -}; - -typedef int (*talloc_destructor_t)(void *); - -struct talloc_chunk { - struct talloc_chunk *next, *prev; - struct talloc_chunk *parent, *child; - struct talloc_reference_handle *refs; - talloc_destructor_t destructor; - const char *name; - size_t size; - unsigned flags; - - /* - * "pool" has dual use: - * - * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool" - * marks the end of the currently allocated area. - * - * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool" - * is a pointer to the struct talloc_chunk of the pool that it was - * allocated from. This way children can quickly find the pool to chew - * from. - */ - void *pool; -}; - -/* 16 byte alignment seems to keep everyone happy */ -#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) -#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) - -static void (*talloc_abort_fn)(const char *reason); - -void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) -{ - talloc_abort_fn = abort_fn; -} - -static void talloc_abort(const char *reason) -{ - if (!talloc_abort_fn) { - TALLOC_ABORT(reason); - } - - talloc_abort_fn(reason); -} - -static void talloc_abort_double_free(void) -{ - talloc_abort("Bad talloc magic value - double free"); -} - -static void talloc_abort_unknown_value(void) -{ - talloc_abort("Bad talloc magic value - unknown value"); -} - -/* panic if we get a bad magic value */ -static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) -{ - const char *pp = (const char *)ptr; - struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); - if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { - if (tc->flags & TALLOC_FLAG_FREE) { - talloc_abort_double_free(); - } else { - talloc_abort_unknown_value(); - } - } - return tc; -} - -/* hook into the front of the list */ -#define _TLIST_ADD(list, p) \ -do { \ - if (!(list)) { \ - (list) = (p); \ - (p)->next = (p)->prev = NULL; \ - } else { \ - (list)->prev = (p); \ - (p)->next = (list); \ - (p)->prev = NULL; \ - (list) = (p); \ - }\ -} while (0) - -/* remove an element from a list - element doesn't have to be in list. */ -#define _TLIST_REMOVE(list, p) \ -do { \ - if ((p) == (list)) { \ - (list) = (p)->next; \ - if (list) (list)->prev = NULL; \ - } else { \ - if ((p)->prev) (p)->prev->next = (p)->next; \ - if ((p)->next) (p)->next->prev = (p)->prev; \ - } \ - if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ -} while (0) - - -/* - return the parent chunk of a pointer -*/ -static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr) -{ - struct talloc_chunk *tc; - - if (unlikely(ptr == NULL)) { - return NULL; - } - - tc = talloc_chunk_from_ptr(ptr); - while (tc->prev) tc=tc->prev; - - return tc->parent; -} - -void *talloc_parent(const void *ptr) -{ - struct talloc_chunk *tc = talloc_parent_chunk(ptr); - return tc? TC_PTR_FROM_CHUNK(tc) : NULL; -} - -/* - find parents name -*/ -const char *talloc_parent_name(const void *ptr) -{ - struct talloc_chunk *tc = talloc_parent_chunk(ptr); - return tc? tc->name : NULL; -} - -/* - A pool carries an in-pool object count count in the first 16 bytes. - bytes. This is done to support talloc_steal() to a parent outside of the - pool. The count includes the pool itself, so a talloc_free() on a pool will - only destroy the pool if the count has dropped to zero. A talloc_free() of a - pool member will reduce the count, and eventually also call free(3) on the - pool memory. - - The object count is not put into "struct talloc_chunk" because it is only - relevant for talloc pools and the alignment to 16 bytes would increase the - memory footprint of each talloc chunk by those 16 bytes. -*/ - -#define TALLOC_POOL_HDR_SIZE 16 - -static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc) -{ - return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk)); -} - -/* - Allocate from a pool -*/ - -static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent, - size_t size) -{ - struct talloc_chunk *pool_ctx = NULL; - size_t space_left; - struct talloc_chunk *result; - size_t chunk_size; - - if (parent == NULL) { - return NULL; - } - - if (parent->flags & TALLOC_FLAG_POOL) { - pool_ctx = parent; - } - else if (parent->flags & TALLOC_FLAG_POOLMEM) { - pool_ctx = (struct talloc_chunk *)parent->pool; - } - - if (pool_ctx == NULL) { - return NULL; - } - - space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size) - - ((char *)pool_ctx->pool); - - /* - * Align size to 16 bytes - */ - chunk_size = ((size + 15) & ~15); - - if (space_left < chunk_size) { - return NULL; - } - - result = (struct talloc_chunk *)pool_ctx->pool; - -#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) - VALGRIND_MAKE_MEM_UNDEFINED(result, size); -#endif - - pool_ctx->pool = (void *)((char *)result + chunk_size); - - result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM; - result->pool = pool_ctx; - - *talloc_pool_objectcount(pool_ctx) += 1; - - return result; -} - -/* - Allocate a bit of memory as a child of an existing pointer -*/ -static inline void *__talloc(const void *context, size_t size) -{ - struct talloc_chunk *tc = NULL; - - if (unlikely(context == NULL)) { - context = null_context; - } - - if (unlikely(size >= MAX_TALLOC_SIZE)) { - return NULL; - } - - if (context != NULL) { - tc = talloc_alloc_pool(talloc_chunk_from_ptr(context), - TC_HDR_SIZE+size); - } - - if (tc == NULL) { - tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); - if (unlikely(tc == NULL)) return NULL; - tc->flags = TALLOC_MAGIC; - tc->pool = NULL; - } - - tc->size = size; - tc->destructor = NULL; - tc->child = NULL; - tc->name = NULL; - tc->refs = NULL; - - if (likely(context)) { - struct talloc_chunk *parent = talloc_chunk_from_ptr(context); - - if (parent->child) { - parent->child->parent = NULL; - tc->next = parent->child; - tc->next->prev = tc; - } else { - tc->next = NULL; - } - tc->parent = parent; - tc->prev = NULL; - parent->child = tc; - } else { - tc->next = tc->prev = tc->parent = NULL; - } - - return TC_PTR_FROM_CHUNK(tc); -} - -/* - * Create a talloc pool - */ - -void *talloc_pool(const void *context, size_t size) -{ - void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE); - struct talloc_chunk *tc; - - if (unlikely(result == NULL)) { - return NULL; - } - - tc = talloc_chunk_from_ptr(result); - - tc->flags |= TALLOC_FLAG_POOL; - tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE; - - *talloc_pool_objectcount(tc) = 1; - -#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) - VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size); -#endif - - return result; -} - -/* - setup a destructor to be called on free of a pointer - the destructor should return 0 on success, or -1 on failure. - if the destructor fails then the free is failed, and the memory can - be continued to be used -*/ -void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - tc->destructor = destructor; -} - -/* - increase the reference count on a piece of memory. -*/ -int talloc_increase_ref_count(const void *ptr) -{ - if (unlikely(!talloc_reference(null_context, ptr))) { - return -1; - } - return 0; -} - -/* - helper for talloc_reference() - - this is referenced by a function pointer and should not be inline -*/ -static int talloc_reference_destructor(struct talloc_reference_handle *handle) -{ - struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); - _TLIST_REMOVE(ptr_tc->refs, handle); - return 0; -} - -/* - more efficient way to add a name to a pointer - the name must point to a - true string constant -*/ -static inline void _talloc_set_name_const(const void *ptr, const char *name) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - tc->name = name; -} - -/* - internal talloc_named_const() -*/ -static inline void *_talloc_named_const(const void *context, size_t size, const char *name) -{ - void *ptr; - - ptr = __talloc(context, size); - if (unlikely(ptr == NULL)) { - return NULL; - } - - _talloc_set_name_const(ptr, name); - - return ptr; -} - -/* - make a secondary reference to a pointer, hanging off the given context. - the pointer remains valid until both the original caller and this given - context are freed. - - the major use for this is when two different structures need to reference the - same underlying data, and you want to be able to free the two instances separately, - and in either order -*/ -void *_talloc_reference(const void *context, const void *ptr) -{ - struct talloc_chunk *tc; - struct talloc_reference_handle *handle; - if (unlikely(ptr == NULL)) return NULL; - - tc = talloc_chunk_from_ptr(ptr); - handle = (struct talloc_reference_handle *)_talloc_named_const(context, - sizeof(struct talloc_reference_handle), - TALLOC_MAGIC_REFERENCE); - if (unlikely(handle == NULL)) return NULL; - - /* note that we hang the destructor off the handle, not the - main context as that allows the caller to still setup their - own destructor on the context if they want to */ - talloc_set_destructor(handle, talloc_reference_destructor); - handle->ptr = discard_const_p(void, ptr); - _TLIST_ADD(tc->refs, handle); - return handle->ptr; -} - - -/* - internal talloc_free call -*/ -static inline int _talloc_free(void *ptr) -{ - struct talloc_chunk *tc; - - if (unlikely(ptr == NULL)) { - return -1; - } - - tc = talloc_chunk_from_ptr(ptr); - - if (unlikely(tc->refs)) { - int is_child; - /* check this is a reference from a child or grantchild - * back to it's parent or grantparent - * - * in that case we need to remove the reference and - * call another instance of talloc_free() on the current - * pointer. - */ - is_child = talloc_is_parent(tc->refs, ptr); - _talloc_free(tc->refs); - if (is_child) { - return _talloc_free(ptr); - } - return -1; - } - - if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { - /* we have a free loop - stop looping */ - return 0; - } - - if (unlikely(tc->destructor)) { - talloc_destructor_t d = tc->destructor; - if (d == (talloc_destructor_t)-1) { - return -1; - } - tc->destructor = (talloc_destructor_t)-1; - if (d(ptr) == -1) { - tc->destructor = d; - return -1; - } - tc->destructor = NULL; - } - - if (tc->parent) { - _TLIST_REMOVE(tc->parent->child, tc); - if (tc->parent->child) { - tc->parent->child->parent = tc->parent; - } - } else { - if (tc->prev) tc->prev->next = tc->next; - if (tc->next) tc->next->prev = tc->prev; - } - - tc->flags |= TALLOC_FLAG_LOOP; - - while (tc->child) { - /* we need to work out who will own an abandoned child - if it cannot be freed. In priority order, the first - choice is owner of any remaining reference to this - pointer, the second choice is our parent, and the - final choice is the null context. */ - void *child = TC_PTR_FROM_CHUNK(tc->child); - const void *new_parent = null_context; - if (unlikely(tc->child->refs)) { - struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); - if (p) new_parent = TC_PTR_FROM_CHUNK(p); - } - if (unlikely(_talloc_free(child) == -1)) { - if (new_parent == null_context) { - struct talloc_chunk *p = talloc_parent_chunk(ptr); - if (p) new_parent = TC_PTR_FROM_CHUNK(p); - } - talloc_steal(new_parent, child); - } - } - - tc->flags |= TALLOC_FLAG_FREE; - - if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) { - struct talloc_chunk *pool; - unsigned int *pool_object_count; - - pool = (tc->flags & TALLOC_FLAG_POOL) - ? tc : (struct talloc_chunk *)tc->pool; - - pool_object_count = talloc_pool_objectcount(pool); - - if (*pool_object_count == 0) { - talloc_abort("Pool object count zero!"); - } - - *pool_object_count -= 1; - - if (*pool_object_count == 0) { - free(pool); - } - } - else { - free(tc); - } - return 0; -} - -/* - move a lump of memory from one talloc context to another return the - ptr on success, or NULL if it could not be transferred. - passing NULL as ptr will always return NULL with no side effects. -*/ -void *_talloc_steal(const void *new_ctx, const void *ptr) -{ - struct talloc_chunk *tc, *new_tc; - - if (unlikely(!ptr)) { - return NULL; - } - - if (unlikely(new_ctx == NULL)) { - new_ctx = null_context; - } - - tc = talloc_chunk_from_ptr(ptr); - - if (unlikely(new_ctx == NULL)) { - if (tc->parent) { - _TLIST_REMOVE(tc->parent->child, tc); - if (tc->parent->child) { - tc->parent->child->parent = tc->parent; - } - } else { - if (tc->prev) tc->prev->next = tc->next; - if (tc->next) tc->next->prev = tc->prev; - } - - tc->parent = tc->next = tc->prev = NULL; - return discard_const_p(void, ptr); - } - - new_tc = talloc_chunk_from_ptr(new_ctx); - - if (unlikely(tc == new_tc || tc->parent == new_tc)) { - return discard_const_p(void, ptr); - } - - if (tc->parent) { - _TLIST_REMOVE(tc->parent->child, tc); - if (tc->parent->child) { - tc->parent->child->parent = tc->parent; - } - } else { - if (tc->prev) tc->prev->next = tc->next; - if (tc->next) tc->next->prev = tc->prev; - } - - tc->parent = new_tc; - if (new_tc->child) new_tc->child->parent = NULL; - _TLIST_ADD(new_tc->child, tc); - - return discard_const_p(void, ptr); -} - - - -/* - remove a secondary reference to a pointer. This undo's what - talloc_reference() has done. The context and pointer arguments - must match those given to a talloc_reference() -*/ -static inline int talloc_unreference(const void *context, const void *ptr) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - struct talloc_reference_handle *h; - - if (unlikely(context == NULL)) { - context = null_context; - } - - for (h=tc->refs;h;h=h->next) { - struct talloc_chunk *p = talloc_parent_chunk(h); - if (p == NULL) { - if (context == NULL) break; - } else if (TC_PTR_FROM_CHUNK(p) == context) { - break; - } - } - if (h == NULL) { - return -1; - } - - return _talloc_free(h); -} - -/* - remove a specific parent context from a pointer. This is a more - controlled varient of talloc_free() -*/ -int talloc_unlink(const void *context, void *ptr) -{ - struct talloc_chunk *tc_p, *new_p; - void *new_parent; - - if (ptr == NULL) { - return -1; - } - - if (context == NULL) { - context = null_context; - } - - if (talloc_unreference(context, ptr) == 0) { - return 0; - } - - if (context == NULL) { - if (talloc_parent_chunk(ptr) != NULL) { - return -1; - } - } else { - if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { - return -1; - } - } - - tc_p = talloc_chunk_from_ptr(ptr); - - if (tc_p->refs == NULL) { - return _talloc_free(ptr); - } - - new_p = talloc_parent_chunk(tc_p->refs); - if (new_p) { - new_parent = TC_PTR_FROM_CHUNK(new_p); - } else { - new_parent = NULL; - } - - if (talloc_unreference(new_parent, ptr) != 0) { - return -1; - } - - talloc_steal(new_parent, ptr); - - return 0; -} - -/* - add a name to an existing pointer - va_list version -*/ -static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); - -static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - tc->name = talloc_vasprintf(ptr, fmt, ap); - if (likely(tc->name)) { - _talloc_set_name_const(tc->name, ".name"); - } - return tc->name; -} - -/* - add a name to an existing pointer -*/ -const char *talloc_set_name(const void *ptr, const char *fmt, ...) -{ - const char *name; - va_list ap; - va_start(ap, fmt); - name = talloc_set_name_v(ptr, fmt, ap); - va_end(ap); - return name; -} - - -/* - create a named talloc pointer. Any talloc pointer can be named, and - talloc_named() operates just like talloc() except that it allows you - to name the pointer. -*/ -void *talloc_named(const void *context, size_t size, const char *fmt, ...) -{ - va_list ap; - void *ptr; - const char *name; - - ptr = __talloc(context, size); - if (unlikely(ptr == NULL)) return NULL; - - va_start(ap, fmt); - name = talloc_set_name_v(ptr, fmt, ap); - va_end(ap); - - if (unlikely(name == NULL)) { - _talloc_free(ptr); - return NULL; - } - - return ptr; -} - -/* - return the name of a talloc ptr, or "UNNAMED" -*/ -const char *talloc_get_name(const void *ptr) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { - return ".reference"; - } - if (likely(tc->name)) { - return tc->name; - } - return "UNNAMED"; -} - - -/* - check if a pointer has the given name. If it does, return the pointer, - otherwise return NULL -*/ -void *talloc_check_name(const void *ptr, const char *name) -{ - const char *pname; - if (unlikely(ptr == NULL)) return NULL; - pname = talloc_get_name(ptr); - if (likely(pname == name || strcmp(pname, name) == 0)) { - return discard_const_p(void, ptr); - } - return NULL; -} - -static void talloc_abort_type_missmatch(const char *location, - const char *name, - const char *expected) -{ - const char *reason; - - reason = talloc_asprintf(NULL, - "%s: Type mismatch: name[%s] expected[%s]", - location, - name?name:"NULL", - expected); - if (!reason) { - reason = "Type mismatch"; - } - - talloc_abort(reason); -} - -void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location) -{ - const char *pname; - - if (unlikely(ptr == NULL)) { - talloc_abort_type_missmatch(location, NULL, name); - return NULL; - } - - pname = talloc_get_name(ptr); - if (likely(pname == name || strcmp(pname, name) == 0)) { - return discard_const_p(void, ptr); - } - - talloc_abort_type_missmatch(location, pname, name); - return NULL; -} - -/* - this is for compatibility with older versions of talloc -*/ -void *talloc_init(const char *fmt, ...) -{ - va_list ap; - void *ptr; - const char *name; - - /* - * samba3 expects talloc_report_depth_cb(NULL, ...) - * reports all talloc'ed memory, so we need to enable - * null_tracking - */ - talloc_enable_null_tracking(); - - ptr = __talloc(NULL, 0); - if (unlikely(ptr == NULL)) return NULL; - - va_start(ap, fmt); - name = talloc_set_name_v(ptr, fmt, ap); - va_end(ap); - - if (unlikely(name == NULL)) { - _talloc_free(ptr); - return NULL; - } - - return ptr; -} - -/* - this is a replacement for the Samba3 talloc_destroy_pool functionality. It - should probably not be used in new code. It's in here to keep the talloc - code consistent across Samba 3 and 4. -*/ -void talloc_free_children(void *ptr) -{ - struct talloc_chunk *tc; - - if (unlikely(ptr == NULL)) { - return; - } - - tc = talloc_chunk_from_ptr(ptr); - - while (tc->child) { - /* we need to work out who will own an abandoned child - if it cannot be freed. In priority order, the first - choice is owner of any remaining reference to this - pointer, the second choice is our parent, and the - final choice is the null context. */ - void *child = TC_PTR_FROM_CHUNK(tc->child); - const void *new_parent = null_context; - if (unlikely(tc->child->refs)) { - struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); - if (p) new_parent = TC_PTR_FROM_CHUNK(p); - } - if (unlikely(_talloc_free(child) == -1)) { - if (new_parent == null_context) { - struct talloc_chunk *p = talloc_parent_chunk(ptr); - if (p) new_parent = TC_PTR_FROM_CHUNK(p); - } - talloc_steal(new_parent, child); - } - } - - if ((tc->flags & TALLOC_FLAG_POOL) - && (*talloc_pool_objectcount(tc) == 1)) { - tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE); -#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) - VALGRIND_MAKE_MEM_NOACCESS( - tc->pool, tc->size - TALLOC_POOL_HDR_SIZE); -#endif - } -} - -/* - Allocate a bit of memory as a child of an existing pointer -*/ -void *_talloc(const void *context, size_t size) -{ - return __talloc(context, size); -} - -/* - externally callable talloc_set_name_const() -*/ -void talloc_set_name_const(const void *ptr, const char *name) -{ - _talloc_set_name_const(ptr, name); -} - -/* - create a named talloc pointer. Any talloc pointer can be named, and - talloc_named() operates just like talloc() except that it allows you - to name the pointer. -*/ -void *talloc_named_const(const void *context, size_t size, const char *name) -{ - return _talloc_named_const(context, size, name); -} - -/* - free a talloc pointer. This also frees all child pointers of this - pointer recursively - - return 0 if the memory is actually freed, otherwise -1. The memory - will not be freed if the ref_count is > 1 or the destructor (if - any) returns non-zero -*/ -int talloc_free(void *ptr) -{ - return _talloc_free(ptr); -} - - - -/* - A talloc version of realloc. The context argument is only used if - ptr is NULL -*/ -void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) -{ - struct talloc_chunk *tc; - void *new_ptr; - bool malloced = false; - - /* size zero is equivalent to free() */ - if (unlikely(size == 0)) { - _talloc_free(ptr); - return NULL; - } - - if (unlikely(size >= MAX_TALLOC_SIZE)) { - return NULL; - } - - /* realloc(NULL) is equivalent to malloc() */ - if (ptr == NULL) { - return _talloc_named_const(context, size, name); - } - - tc = talloc_chunk_from_ptr(ptr); - - /* don't allow realloc on referenced pointers */ - if (unlikely(tc->refs)) { - return NULL; - } - - /* don't let anybody try to realloc a talloc_pool */ - if (unlikely(tc->flags & TALLOC_FLAG_POOL)) { - return NULL; - } - - /* don't shrink if we have less than 1k to gain */ - if ((size < tc->size) && ((tc->size - size) < 1024)) { - tc->size = size; - return ptr; - } - - /* by resetting magic we catch users of the old memory */ - tc->flags |= TALLOC_FLAG_FREE; - -#if ALWAYS_REALLOC - new_ptr = malloc(size + TC_HDR_SIZE); - if (new_ptr) { - memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); - free(tc); - } -#else - if (tc->flags & TALLOC_FLAG_POOLMEM) { - - new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE); - *talloc_pool_objectcount((struct talloc_chunk *) - (tc->pool)) -= 1; - - if (new_ptr == NULL) { - new_ptr = malloc(TC_HDR_SIZE+size); - malloced = true; - } - - if (new_ptr) { - memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE); - } - } - else { - new_ptr = realloc(tc, size + TC_HDR_SIZE); - } -#endif - if (unlikely(!new_ptr)) { - tc->flags &= ~TALLOC_FLAG_FREE; - return NULL; - } - - tc = (struct talloc_chunk *)new_ptr; - tc->flags &= ~TALLOC_FLAG_FREE; - if (malloced) { - tc->flags &= ~TALLOC_FLAG_POOLMEM; - } - if (tc->parent) { - tc->parent->child = tc; - } - if (tc->child) { - tc->child->parent = tc; - } - - if (tc->prev) { - tc->prev->next = tc; - } - if (tc->next) { - tc->next->prev = tc; - } - - tc->size = size; - _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name); - - return TC_PTR_FROM_CHUNK(tc); -} - -/* - a wrapper around talloc_steal() for situations where you are moving a pointer - between two structures, and want the old pointer to be set to NULL -*/ -void *_talloc_move(const void *new_ctx, const void *_pptr) -{ - const void **pptr = discard_const_p(const void *,_pptr); - void *ret = _talloc_steal(new_ctx, *pptr); - (*pptr) = NULL; - return ret; -} - -/* - return the total size of a talloc pool (subtree) -*/ -size_t talloc_total_size(const void *ptr) -{ - size_t total = 0; - struct talloc_chunk *c, *tc; - - if (ptr == NULL) { - ptr = null_context; - } - if (ptr == NULL) { - return 0; - } - - tc = talloc_chunk_from_ptr(ptr); - - if (tc->flags & TALLOC_FLAG_LOOP) { - return 0; - } - - tc->flags |= TALLOC_FLAG_LOOP; - - total = tc->size; - for (c=tc->child;c;c=c->next) { - total += talloc_total_size(TC_PTR_FROM_CHUNK(c)); - } - - tc->flags &= ~TALLOC_FLAG_LOOP; - - return total; -} - -/* - return the total number of blocks in a talloc pool (subtree) -*/ -size_t talloc_total_blocks(const void *ptr) -{ - size_t total = 0; - struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); - - if (tc->flags & TALLOC_FLAG_LOOP) { - return 0; - } - - tc->flags |= TALLOC_FLAG_LOOP; - - total++; - for (c=tc->child;c;c=c->next) { - total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c)); - } - - tc->flags &= ~TALLOC_FLAG_LOOP; - - return total; -} - -/* - return the number of external references to a pointer -*/ -size_t talloc_reference_count(const void *ptr) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - struct talloc_reference_handle *h; - size_t ret = 0; - - for (h=tc->refs;h;h=h->next) { - ret++; - } - return ret; -} - -/* - report on memory usage by all children of a pointer, giving a full tree view -*/ -void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, - void (*callback)(const void *ptr, - int depth, int max_depth, - int is_ref, - void *private_data), - void *private_data) -{ - struct talloc_chunk *c, *tc; - - if (ptr == NULL) { - ptr = null_context; - } - if (ptr == NULL) return; - - tc = talloc_chunk_from_ptr(ptr); - - if (tc->flags & TALLOC_FLAG_LOOP) { - return; - } - - callback(ptr, depth, max_depth, 0, private_data); - - if (max_depth >= 0 && depth >= max_depth) { - return; - } - - tc->flags |= TALLOC_FLAG_LOOP; - for (c=tc->child;c;c=c->next) { - if (c->name == TALLOC_MAGIC_REFERENCE) { - struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c); - callback(h->ptr, depth + 1, max_depth, 1, private_data); - } else { - talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data); - } - } - tc->flags &= ~TALLOC_FLAG_LOOP; -} - -static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f) -{ - const char *name = talloc_get_name(ptr); - FILE *f = (FILE *)_f; - - if (is_ref) { - fprintf(f, "%*sreference to: %s\n", depth*4, "", name); - return; - } - - if (depth == 0) { - fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", - (max_depth < 0 ? "full " :""), name, - (unsigned long)talloc_total_size(ptr), - (unsigned long)talloc_total_blocks(ptr)); - return; - } - - fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", - depth*4, "", - name, - (unsigned long)talloc_total_size(ptr), - (unsigned long)talloc_total_blocks(ptr), - (int)talloc_reference_count(ptr), ptr); - -#if 0 - fprintf(f, "content: "); - if (talloc_total_size(ptr)) { - int tot = talloc_total_size(ptr); - int i; - - for (i = 0; i < tot; i++) { - if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) { - fprintf(f, "%c", ((char *)ptr)[i]); - } else { - fprintf(f, "~%02x", ((char *)ptr)[i]); - } - } - } - fprintf(f, "\n"); -#endif -} - -/* - report on memory usage by all children of a pointer, giving a full tree view -*/ -void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) -{ - talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); - fflush(f); -} - -/* - report on memory usage by all children of a pointer, giving a full tree view -*/ -void talloc_report_full(const void *ptr, FILE *f) -{ - talloc_report_depth_file(ptr, 0, -1, f); -} - -/* - report on memory usage by all children of a pointer -*/ -void talloc_report(const void *ptr, FILE *f) -{ - talloc_report_depth_file(ptr, 0, 1, f); -} - -/* - report on any memory hanging off the null context -*/ -static void talloc_report_null(void) -{ - if (talloc_total_size(null_context) != 0) { - talloc_report(null_context, stderr); - } -} - -/* - report on any memory hanging off the null context -*/ -static void talloc_report_null_full(void) -{ - if (talloc_total_size(null_context) != 0) { - talloc_report_full(null_context, stderr); - } -} - -/* - enable tracking of the NULL context -*/ -void talloc_enable_null_tracking(void) -{ - if (null_context == NULL) { - null_context = _talloc_named_const(NULL, 0, "null_context"); - } -} - -/* - disable tracking of the NULL context -*/ -void talloc_disable_null_tracking(void) -{ - _talloc_free(null_context); - null_context = NULL; -} - -/* - enable leak reporting on exit -*/ -void talloc_enable_leak_report(void) -{ - talloc_enable_null_tracking(); - atexit(talloc_report_null); -} - -/* - enable full leak reporting on exit -*/ -void talloc_enable_leak_report_full(void) -{ - talloc_enable_null_tracking(); - atexit(talloc_report_null_full); -} - -/* - talloc and zero memory. -*/ -void *_talloc_zero(const void *ctx, size_t size, const char *name) -{ - void *p = _talloc_named_const(ctx, size, name); - - if (p) { - memset(p, '\0', size); - } - - return p; -} - -/* - memdup with a talloc. -*/ -void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) -{ - void *newp = _talloc_named_const(t, size, name); - - if (likely(newp)) { - memcpy(newp, p, size); - } - - return newp; -} - -static inline char *__talloc_strlendup(const void *t, const char *p, size_t len) -{ - char *ret; - - ret = (char *)__talloc(t, len + 1); - if (unlikely(!ret)) return NULL; - - memcpy(ret, p, len); - ret[len] = 0; - - _talloc_set_name_const(ret, ret); - return ret; -} - -/* - strdup with a talloc -*/ -char *talloc_strdup(const void *t, const char *p) -{ - if (unlikely(!p)) return NULL; - return __talloc_strlendup(t, p, strlen(p)); -} - -/* - strndup with a talloc -*/ -char *talloc_strndup(const void *t, const char *p, size_t n) -{ - if (unlikely(!p)) return NULL; - return __talloc_strlendup(t, p, strnlen(p, n)); -} - -static inline char *__talloc_strlendup_append(char *s, size_t slen, - const char *a, size_t alen) -{ - char *ret; - - ret = talloc_realloc(NULL, s, char, slen + alen + 1); - if (unlikely(!ret)) return NULL; - - /* append the string and the trailing \0 */ - memcpy(&ret[slen], a, alen); - ret[slen+alen] = 0; - - _talloc_set_name_const(ret, ret); - return ret; -} - -/* - * Appends at the end of the string. - */ -char *talloc_strdup_append(char *s, const char *a) -{ - if (unlikely(!s)) { - return talloc_strdup(NULL, a); - } - - if (unlikely(!a)) { - return s; - } - - return __talloc_strlendup_append(s, strlen(s), a, strlen(a)); -} - -/* - * Appends at the end of the talloc'ed buffer, - * not the end of the string. - */ -char *talloc_strdup_append_buffer(char *s, const char *a) -{ - size_t slen; - - if (unlikely(!s)) { - return talloc_strdup(NULL, a); - } - - if (unlikely(!a)) { - return s; - } - - slen = talloc_get_size(s); - if (likely(slen > 0)) { - slen--; - } - - return __talloc_strlendup_append(s, slen, a, strlen(a)); -} - -/* - * Appends at the end of the string. - */ -char *talloc_strndup_append(char *s, const char *a, size_t n) -{ - if (unlikely(!s)) { - return talloc_strdup(NULL, a); - } - - if (unlikely(!a)) { - return s; - } - - return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n)); -} - -/* - * Appends at the end of the talloc'ed buffer, - * not the end of the string. - */ -char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) -{ - size_t slen; - - if (unlikely(!s)) { - return talloc_strdup(NULL, a); - } - - if (unlikely(!a)) { - return s; - } - - slen = talloc_get_size(s); - if (likely(slen > 0)) { - slen--; - } - - return __talloc_strlendup_append(s, slen, a, strnlen(a, n)); -} - -#ifndef HAVE_VA_COPY -#ifdef HAVE___VA_COPY -#define va_copy(dest, src) __va_copy(dest, src) -#else -#define va_copy(dest, src) (dest) = (src) -#endif -#endif - -char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) -{ - int len; - char *ret; - va_list ap2; - char c; - - /* this call looks strange, but it makes it work on older solaris boxes */ - va_copy(ap2, ap); - len = vsnprintf(&c, 1, fmt, ap2); - va_end(ap2); - if (unlikely(len < 0)) { - return NULL; - } - - ret = (char *)__talloc(t, len+1); - if (unlikely(!ret)) return NULL; - - va_copy(ap2, ap); - vsnprintf(ret, len+1, fmt, ap2); - va_end(ap2); - - _talloc_set_name_const(ret, ret); - return ret; -} - - -/* - Perform string formatting, and return a pointer to newly allocated - memory holding the result, inside a memory pool. - */ -char *talloc_asprintf(const void *t, const char *fmt, ...) -{ - va_list ap; - char *ret; - - va_start(ap, fmt); - ret = talloc_vasprintf(t, fmt, ap); - va_end(ap); - return ret; -} - -static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, - const char *fmt, va_list ap) - PRINTF_ATTRIBUTE(3,0); - -static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, - const char *fmt, va_list ap) -{ - ssize_t alen; - va_list ap2; - char c; - - va_copy(ap2, ap); - alen = vsnprintf(&c, 1, fmt, ap2); - va_end(ap2); - - if (alen <= 0) { - /* Either the vsnprintf failed or the format resulted in - * no characters being formatted. In the former case, we - * ought to return NULL, in the latter we ought to return - * the original string. Most current callers of this - * function expect it to never return NULL. - */ - return s; - } - - s = talloc_realloc(NULL, s, char, slen + alen + 1); - if (!s) return NULL; - - va_copy(ap2, ap); - vsnprintf(s + slen, alen + 1, fmt, ap2); - va_end(ap2); - - _talloc_set_name_const(s, s); - return s; -} - -/** - * Realloc @p s to append the formatted result of @p fmt and @p ap, - * and return @p s, which may have moved. Good for gradually - * accumulating output into a string buffer. Appends at the end - * of the string. - **/ -char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) -{ - if (unlikely(!s)) { - return talloc_vasprintf(NULL, fmt, ap); - } - - return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap); -} - -/** - * Realloc @p s to append the formatted result of @p fmt and @p ap, - * and return @p s, which may have moved. Always appends at the - * end of the talloc'ed buffer, not the end of the string. - **/ -char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) -{ - size_t slen; - - if (unlikely(!s)) { - return talloc_vasprintf(NULL, fmt, ap); - } - - slen = talloc_get_size(s); - if (likely(slen > 0)) { - slen--; - } - - return __talloc_vaslenprintf_append(s, slen, fmt, ap); -} - -/* - Realloc @p s to append the formatted result of @p fmt and return @p - s, which may have moved. Good for gradually accumulating output - into a string buffer. - */ -char *talloc_asprintf_append(char *s, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - s = talloc_vasprintf_append(s, fmt, ap); - va_end(ap); - return s; -} - -/* - Realloc @p s to append the formatted result of @p fmt and return @p - s, which may have moved. Good for gradually accumulating output - into a buffer. - */ -char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - s = talloc_vasprintf_append_buffer(s, fmt, ap); - va_end(ap); - return s; -} - -/* - alloc an array, checking for integer overflow in the array size -*/ -void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) -{ - if (count >= MAX_TALLOC_SIZE/el_size) { - return NULL; - } - return _talloc_named_const(ctx, el_size * count, name); -} - -/* - alloc an zero array, checking for integer overflow in the array size -*/ -void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) -{ - if (count >= MAX_TALLOC_SIZE/el_size) { - return NULL; - } - return _talloc_zero(ctx, el_size * count, name); -} - -/* - realloc an array, checking for integer overflow in the array size -*/ -void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) -{ - if (count >= MAX_TALLOC_SIZE/el_size) { - return NULL; - } - return _talloc_realloc(ctx, ptr, el_size * count, name); -} - -/* - a function version of talloc_realloc(), so it can be passed as a function pointer - to libraries that want a realloc function (a realloc function encapsulates - all the basic capabilities of an allocation library, which is why this is useful) -*/ -void *talloc_realloc_fn(const void *context, void *ptr, size_t size) -{ - return _talloc_realloc(context, ptr, size, NULL); -} - - -static int talloc_autofree_destructor(void *ptr) -{ - autofree_context = NULL; - return 0; -} - -static void talloc_autofree(void) -{ - _talloc_free(autofree_context); -} - -/* - return a context which will be auto-freed on exit - this is useful for reducing the noise in leak reports -*/ -void *talloc_autofree_context(void) -{ - if (autofree_context == NULL) { - autofree_context = _talloc_named_const(NULL, 0, "autofree_context"); - talloc_set_destructor(autofree_context, talloc_autofree_destructor); - atexit(talloc_autofree); - } - return autofree_context; -} - -size_t talloc_get_size(const void *context) -{ - struct talloc_chunk *tc; - - if (context == NULL) - return 0; - - tc = talloc_chunk_from_ptr(context); - - return tc->size; -} - -/* - find a parent of this context that has the given name, if any -*/ -void *talloc_find_parent_byname(const void *context, const char *name) -{ - struct talloc_chunk *tc; - - if (context == NULL) { - return NULL; - } - - tc = talloc_chunk_from_ptr(context); - while (tc) { - if (tc->name && strcmp(tc->name, name) == 0) { - return TC_PTR_FROM_CHUNK(tc); - } - while (tc && tc->prev) tc = tc->prev; - if (tc) { - tc = tc->parent; - } - } - return NULL; -} - -/* - show the parentage of a context -*/ -void talloc_show_parents(const void *context, FILE *file) -{ - struct talloc_chunk *tc; - - if (context == NULL) { - fprintf(file, "talloc no parents for NULL\n"); - return; - } - - tc = talloc_chunk_from_ptr(context); - fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context)); - while (tc) { - fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc))); - while (tc && tc->prev) tc = tc->prev; - if (tc) { - tc = tc->parent; - } - } - fflush(file); -} - -/* - return 1 if ptr is a parent of context -*/ -int talloc_is_parent(const void *context, const void *ptr) -{ - struct talloc_chunk *tc; - - if (context == NULL) { - return 0; - } - - tc = talloc_chunk_from_ptr(context); - while (tc) { - if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; - while (tc && tc->prev) tc = tc->prev; - if (tc) { - tc = tc->parent; - } - } - return 0; -} diff --git a/openbsc/src/talloc_ctx.c b/openbsc/src/talloc_ctx.c index aa9f0b4d7..6379e13db 100644 --- a/openbsc/src/talloc_ctx.c +++ b/openbsc/src/talloc_ctx.c @@ -1,6 +1,7 @@ -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/gsm_data.h> +extern void *tall_msgb_ctx; extern void *tall_fle_ctx; extern void *tall_locop_ctx; extern void *tall_gsms_ctx; @@ -13,9 +14,11 @@ extern void *tall_tqe_ctx; extern void *tall_trans_ctx; extern void *tall_map_ctx; extern void *tall_upq_ctx; +extern void *tall_ctr_ctx; void talloc_ctx_init(void) { + tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0, "bs11_file_list_entry"); tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 0, "loc_updating_oper"); @@ -29,4 +32,5 @@ void talloc_ctx_init(void) tall_trans_ctx = talloc_named_const(tall_bsc_ctx, 0, "transaction"); tall_map_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_map_entry"); tall_upq_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_upq_entry"); + tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); } diff --git a/openbsc/src/telnet_interface.c b/openbsc/src/telnet_interface.c index bc91ca333..805dd127d 100644 --- a/openbsc/src/telnet_interface.c +++ b/openbsc/src/telnet_interface.c @@ -30,11 +30,11 @@ #include <openbsc/chan_alloc.h> #include <openbsc/gsm_04_08.h> #include <openbsc/gsm_04_11.h> -#include <openbsc/msgb.h> +#include <osmocore/msgb.h> #include <openbsc/abis_rsl.h> #include <openbsc/paging.h> #include <openbsc/signal.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/debug.h> #include <vty/buffer.h> @@ -102,9 +102,9 @@ static void print_welcome(int fd) { int ret; static char *msg = "Welcome to the OpenBSC Control interface\n" - "Copyright (C) 2008, 2009 Harald Welte\n" + "Copyright (C) 2008-2010 Harald Welte\n" "Contributions by Daniel Willmann, Jan Lübbe, " - "Stefan Schmidt, Holger Freyther\n\n" + "Stefan Schmidt, Holger Freyther, Andreas Eversberg\n\n" "License GPLv2+: GNU GPL version 2 or later " "<http://gnu.org/licenses/gpl.html>\n" "This is free software: you are free to change " @@ -120,6 +120,12 @@ int telnet_close_client(struct bsc_fd *fd) { close(fd->fd); bsc_unregister_fd(fd); + + if (conn->dbg) { + debug_del_target(conn->dbg); + talloc_free(conn->dbg); + } + llist_del(&conn->entry); talloc_free(conn); return 0; diff --git a/openbsc/src/timer.c b/openbsc/src/timer.c deleted file mode 100644 index ffeb4aba3..000000000 --- a/openbsc/src/timer.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * (C) 2008,2009 by Holger Hans Peter Freyther <zecke@selfish.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <assert.h> -#include <string.h> -#include <openbsc/timer.h> - -static LLIST_HEAD(timer_list); -static struct timeval s_nearest_time; -static struct timeval s_select_time; - -#define MICRO_SECONDS 1000000LL - -#define TIME_SMALLER(left, right) \ - (left.tv_sec*MICRO_SECONDS+left.tv_usec) <= (right.tv_sec*MICRO_SECONDS+right.tv_usec) - -void bsc_add_timer(struct timer_list *timer) -{ - struct timer_list *list_timer; - - /* TODO: Optimize and remember the closest item... */ - timer->active = 1; - - /* this might be called from within update_timers */ - llist_for_each_entry(list_timer, &timer_list, entry) - if (timer == list_timer) - return; - - timer->in_list = 1; - llist_add(&timer->entry, &timer_list); -} - -void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds) -{ - struct timeval current_time; - - gettimeofday(¤t_time, NULL); - unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; - currentTime += seconds * MICRO_SECONDS + microseconds; - timer->timeout.tv_sec = currentTime / MICRO_SECONDS; - timer->timeout.tv_usec = currentTime % MICRO_SECONDS; - bsc_add_timer(timer); -} - -void bsc_del_timer(struct timer_list *timer) -{ - if (timer->in_list) { - timer->active = 0; - timer->in_list = 0; - llist_del(&timer->entry); - } -} - -int bsc_timer_pending(struct timer_list *timer) -{ - return timer->active; -} - -/* - * if we have a nearest time return the delta between the current - * time and the time of the nearest timer. - * If the nearest timer timed out return NULL and then we will - * dispatch everything after the select - */ -struct timeval *bsc_nearest_timer() -{ - struct timeval current_time; - - if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0) - return NULL; - - if (gettimeofday(¤t_time, NULL) == -1) - return NULL; - - unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec; - unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; - - if (nearestTime < currentTime) { - s_select_time.tv_sec = 0; - s_select_time.tv_usec = 0; - } else { - s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS; - s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS; - } - - return &s_select_time; -} - -/* - * Find the nearest time and update s_nearest_time - */ -void bsc_prepare_timers() -{ - struct timer_list *timer, *nearest_timer = NULL; - llist_for_each_entry(timer, &timer_list, entry) { - if (!nearest_timer || TIME_SMALLER(timer->timeout, nearest_timer->timeout)) { - nearest_timer = timer; - } - } - - if (nearest_timer) { - s_nearest_time = nearest_timer->timeout; - } else { - memset(&s_nearest_time, 0, sizeof(struct timeval)); - } -} - -/* - * fire all timers... and remove them - */ -int bsc_update_timers() -{ - struct timeval current_time; - struct timer_list *timer, *tmp; - int work = 0; - - gettimeofday(¤t_time, NULL); - - /* - * The callbacks might mess with our list and in this case - * even llist_for_each_entry_safe is not safe to use. To allow - * del_timer, add_timer, schedule_timer to be called from within - * the callback we jump through some loops. - * - * First we set the handled flag of each active timer to zero, - * then we iterate over the list and execute the callbacks. As the - * list might have been changed (specially the next) from within - * the callback we have to start over again. Once every callback - * is dispatched we will remove the non-active from the list. - * - * TODO: If this is a performance issue we can poison a global - * variable in add_timer and del_timer and only then restart. - */ - llist_for_each_entry(timer, &timer_list, entry) { - timer->handled = 0; - } - -restart: - llist_for_each_entry(timer, &timer_list, entry) { - if (!timer->handled && TIME_SMALLER(timer->timeout, current_time)) { - timer->handled = 1; - timer->active = 0; - (*timer->cb)(timer->data); - work = 1; - goto restart; - } - } - - llist_for_each_entry_safe(timer, tmp, &timer_list, entry) { - timer->handled = 0; - if (!timer->active) { - bsc_del_timer(timer); - } - } - - return work; -} - -int bsc_timer_check(void) -{ - struct timer_list *timer; - int i = 0; - - llist_for_each_entry(timer, &timer_list, entry) { - i++; - } - return i; -} diff --git a/openbsc/src/tlv_parser.c b/openbsc/src/tlv_parser.c deleted file mode 100644 index fd0045f97..000000000 --- a/openbsc/src/tlv_parser.c +++ /dev/null @@ -1,157 +0,0 @@ -#include <stdio.h> -#include <openbsc/tlv.h> -#include <openbsc/gsm_data.h> - -struct tlv_definition tvlv_att_def; - -int tlv_dump(struct tlv_parsed *dec) -{ - int i; - - for (i = 0; i <= 0xff; i++) { - if (!dec->lv[i].val) - continue; - printf("T=%02x L=%d\n", i, dec->lv[i].len); - } - return 0; -} - -/* o_tag: output: tag found - * o_len: output: length of the data - * o_val: output: pointer to the data - * def: input: a structure defining the valid TLV tags / configurations - * buf: input: the input data buffer to be parsed - * buf_len: input: the length of the input data buffer - * - * Also, returns the number of bytes consumed by the TLV entry - */ -int tlv_parse_one(u_int8_t *o_tag, u_int16_t *o_len, const u_int8_t **o_val, - const struct tlv_definition *def, - const u_int8_t *buf, int buf_len) -{ - u_int8_t tag; - int len; - - tag = *buf; - *o_tag = tag; - - /* FIXME: use tables for knwon IEI */ - switch (def->def[tag].type) { - case TLV_TYPE_T: - /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */ - *o_val = buf; - *o_len = 0; - len = 1; - break; - case TLV_TYPE_TV: - *o_val = buf+1; - *o_len = 1; - len = 2; - break; - case TLV_TYPE_FIXED: - *o_val = buf+1; - *o_len = def->def[tag].fixed_len; - len = def->def[tag].fixed_len + 1; - break; - case TLV_TYPE_TLV: - /* GSM TS 04.07 11.2.4: Type 4 TLV */ - if (buf + 1 > buf + buf_len) - return -1; - *o_val = buf+2; - *o_len = *(buf+1); - len = *o_len + 2; - if (len > buf_len) - return -2; - break; - case TLV_TYPE_TvLV: - if (*(buf+1) & 0x80) { - /* like TLV, but without highest bit of len */ - if (buf + 1 > buf + buf_len) - return -1; - *o_val = buf+2; - *o_len = *(buf+1) & 0x7f; - len = *o_len + 2; - if (len > buf_len) - return -2; - break; - } - /* like TL16V, fallthrough */ - case TLV_TYPE_TL16V: - if (2 > buf_len) - return -1; - *o_val = buf+3; - *o_len = *(buf+1) << 8 | *(buf+2); - len = *o_len + 3; - if (len > buf_len) - return -2; - break; - default: - return -3; - } - - return len; -} - -/* dec: output: a caller-allocated pointer to a struct tlv_parsed, - * def: input: a structure defining the valid TLV tags / configurations - * buf: input: the input data buffer to be parsed - * buf_len: input: the length of the input data buffer - * lv_tag: input: an initial LV tag at the start of the buffer - * lv_tag2: input: a second initial LV tag following lv_tag - */ -int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def, - const u_int8_t *buf, int buf_len, u_int8_t lv_tag, - u_int8_t lv_tag2) -{ - int ofs = 0, num_parsed = 0; - u_int16_t len; - - memset(dec, 0, sizeof(*dec)); - - if (lv_tag) { - if (ofs > buf_len) - return -1; - dec->lv[lv_tag].val = &buf[ofs+1]; - dec->lv[lv_tag].len = buf[ofs]; - len = dec->lv[lv_tag].len + 1; - if (ofs + len > buf_len) - return -2; - num_parsed++; - ofs += len; - } - if (lv_tag2) { - if (ofs > buf_len) - return -1; - dec->lv[lv_tag2].val = &buf[ofs+1]; - dec->lv[lv_tag2].len = buf[ofs]; - len = dec->lv[lv_tag2].len + 1; - if (ofs + len > buf_len) - return -2; - num_parsed++; - ofs += len; - } - - while (ofs < buf_len) { - int rv; - u_int8_t tag; - const u_int8_t *val; - - rv = tlv_parse_one(&tag, &len, &val, def, - &buf[ofs], buf_len-ofs); - if (rv < 0) - return rv; - dec->lv[tag].val = val; - dec->lv[tag].len = len; - ofs += rv; - num_parsed++; - } - //tlv_dump(dec); - return num_parsed; -} - -static __attribute__((constructor)) void on_dso_load_tlv(void) -{ - int i; - for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++) - tvlv_att_def.def[i].type = TLV_TYPE_TvLV; -} diff --git a/openbsc/src/token_auth.c b/openbsc/src/token_auth.c index f6be0bc98..7fefea5a4 100644 --- a/openbsc/src/token_auth.c +++ b/openbsc/src/token_auth.c @@ -21,7 +21,7 @@ */ #include <stdio.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/signal.h> #include <openbsc/gsm_data.h> #include <openbsc/gsm_04_11.h> @@ -103,7 +103,7 @@ unauth: if (lchan) { u_int8_t auth_rand[16]; /* kick the subscriber off the network */ - gsm48_tx_mm_auth_req(lchan, auth_rand); + gsm48_tx_mm_auth_req(lchan, auth_rand, 0); gsm48_tx_mm_auth_rej(lchan); /* FIXME: close the channel early ?*/ //gsm48_send_rr_Release(lchan); @@ -139,7 +139,7 @@ static int token_sms_cb(unsigned int subsys, unsigned int signal, lchan = lchan_for_subscr(sms->receiver); if (lchan) { /* kick the subscriber off the network */ - gsm48_tx_mm_auth_req(lchan, auth_rand); + gsm48_tx_mm_auth_req(lchan, auth_rand, 0); gsm48_tx_mm_auth_rej(lchan); /* FIXME: close the channel early ?*/ //gsm48_send_rr_Release(lchan); diff --git a/openbsc/src/transaction.c b/openbsc/src/transaction.c index 2f5681433..23dfe073d 100644 --- a/openbsc/src/transaction.c +++ b/openbsc/src/transaction.c @@ -23,7 +23,7 @@ #include <openbsc/gsm_data.h> #include <openbsc/mncc.h> #include <openbsc/debug.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/gsm_subscriber.h> #include <openbsc/gsm_04_08.h> #include <openbsc/mncc.h> @@ -120,7 +120,7 @@ int trans_assign_trans_id(struct gsm_subscriber *subscr, struct gsm_network *net = subscr->net; struct gsm_trans *trans; unsigned int used_tid_bitmask = 0; - int i; + int i, j, h; if (ti_flag) ti_flag = 0x8; @@ -134,9 +134,14 @@ int trans_assign_trans_id(struct gsm_subscriber *subscr, used_tid_bitmask |= (1 << trans->transaction_id); } + /* find a new one, trying to go in a 'circular' pattern */ + for (h = 6; h > 0; h--) + if (used_tid_bitmask & (1 << (h | ti_flag))) + break; for (i = 0; i < 7; i++) { - if ((used_tid_bitmask & (1 << (i | ti_flag))) == 0) - return i | ti_flag; + j = ((h + i) % 7) | ti_flag; + if ((used_tid_bitmask & (1 << j)) == 0) + return j; } return -1; diff --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c index 9930751a5..f2fa5c023 100644 --- a/openbsc/src/trau_mux.c +++ b/openbsc/src/trau_mux.c @@ -30,7 +30,7 @@ #include <openbsc/subchan_demux.h> #include <openbsc/e1_input.h> #include <openbsc/debug.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> u_int8_t gsm_fr_map[] = { 6, 6, 5, 5, 4, 4, 3, 3, diff --git a/openbsc/src/vty/buffer.c b/openbsc/src/vty/buffer.c index 8ab73114b..195d06209 100644 --- a/openbsc/src/vty/buffer.c +++ b/openbsc/src/vty/buffer.c @@ -28,7 +28,7 @@ #include <stddef.h> #include <sys/uio.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <vty/buffer.h> #include <vty/vty.h> diff --git a/openbsc/src/vty/command.c b/openbsc/src/vty/command.c index a0d36a913..30c26ac9b 100644 --- a/openbsc/src/vty/command.c +++ b/openbsc/src/vty/command.c @@ -47,8 +47,10 @@ Boston, MA 02111-1307, USA. */ #include <openbsc/gsm_data.h> #include <openbsc/gsm_subscriber.h> -#include <openbsc/talloc.h> #include <openbsc/bsc_nat.h> +#include <osmocore/talloc.h> + +void *tall_vty_cmd_ctx; /* Command vector which includes some level of command lists. Normally each daemon maintains each own cmdvec. */ @@ -174,7 +176,7 @@ char *argv_concat(const char **argv, int argc, int shift) len += strlen(argv[i]) + 1; if (!len) return NULL; - p = str = _talloc_zero(tall_vty_ctx, len, "arvg_concat"); + p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat"); for (i = shift; i < argc; i++) { size_t arglen; memcpy(p, argv[i], (arglen = strlen(argv[i]))); @@ -276,7 +278,7 @@ vector cmd_make_strvec(const char *string) *cp != '\0') cp++; strlen = cp - start; - token = _talloc_zero(tall_vty_ctx, strlen + 1, "make_strvec"); + token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec"); memcpy(token, start, strlen); *(token + strlen) = '\0'; vector_set(strvec, token); @@ -332,7 +334,7 @@ static char *cmd_desc_str(const char **string) cp++; strlen = cp - start; - token = _talloc_zero(tall_vty_ctx, strlen + 1, "cmd_desc_str"); + token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str"); memcpy(token, start, strlen); *(token + strlen) = '\0'; @@ -403,11 +405,11 @@ static vector cmd_make_descvec(const char *string, const char *descstr) len = cp - sp; - token = _talloc_zero(tall_vty_ctx, len + 1, "cmd_make_descvec"); + token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec"); memcpy(token, sp, len); *(token + len) = '\0'; - desc = talloc_zero(tall_vty_ctx, struct desc); + desc = talloc_zero(tall_vty_cmd_ctx, struct desc); desc->cmd = token; desc->str = cmd_desc_str(&dp); @@ -1805,7 +1807,7 @@ static char **cmd_complete_command_real(vector vline, struct vty *vty, if ((desc = vector_slot(descvec, j))) { if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd))) if (cmd_unique_string (matchvec, string)) - vector_set (matchvec, talloc_strdup(tall_vty_ctx, string)); + vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string)); } } } @@ -1846,7 +1848,7 @@ static char **cmd_complete_command_real(vector vline, struct vty *vty, if (len < lcd) { char *lcdstr; - lcdstr = _talloc_zero(tall_vty_ctx, lcd + 1, + lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1, "complete-lcdstr"); memcpy(lcdstr, matchvec->index[0], lcd); lcdstr[lcd] = '\0'; @@ -2471,13 +2473,13 @@ DEFUN(config_write_file, config_file = host.config; config_file_sav = - _talloc_zero(tall_vty_ctx, + _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1, "config_file_sav"); strcpy(config_file_sav, config_file); strcat(config_file_sav, CONF_BACKUP_EXT); - config_file_tmp = _talloc_zero(tall_vty_ctx, strlen(config_file) + 8, + config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8, "config_file_tmp"); sprintf(config_file_tmp, "%s.XXXXXX", config_file); @@ -2658,7 +2660,7 @@ DEFUN(config_hostname, if (host.name) talloc_free(host.name); - host.name = talloc_strdup(tall_vty_ctx, argv[0]); + host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]); return CMD_SUCCESS; } @@ -2693,7 +2695,7 @@ DEFUN(config_password, password_cmd, host.password = NULL; if (host.password_encrypt) talloc_free(host.password_encrypt); - host.password_encrypt = talloc_strdup(tall_vty_ctx, argv[1]); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]); return CMD_SUCCESS; } else { vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE); @@ -2716,10 +2718,10 @@ DEFUN(config_password, password_cmd, if (host.encrypt) { if (host.password_encrypt) talloc_free(host.password_encrypt); - host.password_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(argv[0])); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0])); } else #endif - host.password = talloc_strdup(tall_vty_ctx, argv[0]); + host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]); return CMD_SUCCESS; } @@ -2752,7 +2754,7 @@ ALIAS(config_password, password_text_cmd, if (host.enable_encrypt) talloc_free(host.enable_encrypt); - host.enable_encrypt = talloc_strdup(tall_vty_ctx, argv[1]); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]); return CMD_SUCCESS; } else { @@ -2777,10 +2779,10 @@ ALIAS(config_password, password_text_cmd, if (host.encrypt) { if (host.enable_encrypt) talloc_free(host.enable_encrypt); - host.enable_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(argv[0])); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0])); } else #endif - host.enable = talloc_strdup(tall_vty_ctx, argv[0]); + host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]); return CMD_SUCCESS; } @@ -2824,12 +2826,12 @@ DEFUN(service_password_encrypt, if (host.password) { if (host.password_encrypt) talloc_free(host.password_encrypt); - host.password_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(host.password)); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password)); } if (host.enable) { if (host.enable_encrypt) talloc_free(host.enable_encrypt); - host.enable_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(host.enable)); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable)); } return CMD_SUCCESS; @@ -3103,7 +3105,7 @@ static int set_log_file(struct vty *vty, const char *fname, int loglevel) if (host.logfile) talloc_free(host.logfile); - host.logfile = talloc_strdup(tall_vty_ctx, fname); + host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname); return CMD_SUCCESS; } @@ -3293,7 +3295,7 @@ DEFUN(banner_motd_file, { if (host.motdfile) talloc_free(host.motdfile); - host.motdfile = talloc_strdup(tall_vty_ctx, argv[0]); + host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]); return CMD_SUCCESS; } @@ -3321,7 +3323,7 @@ DEFUN(no_banner_motd, /* Set config filename. Called from vty.c */ void host_config_set(const char *filename) { - host.config = talloc_strdup(tall_vty_ctx, filename); + host.config = talloc_strdup(tall_vty_cmd_ctx, filename); } void install_default(enum node_type node) diff --git a/openbsc/src/vty/vector.c b/openbsc/src/vty/vector.c index 371f71d95..db47ae59a 100644 --- a/openbsc/src/vty/vector.c +++ b/openbsc/src/vty/vector.c @@ -24,13 +24,15 @@ #include <vty/vector.h> #include <vty/vty.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <memory.h> +void *tall_vty_vec_ctx; + /* Initialize vector : allocate memory and return vector. */ vector vector_init(unsigned int size) { - vector v = talloc_zero(tall_vty_ctx, struct _vector); + vector v = talloc_zero(tall_vty_vec_ctx, struct _vector); if (!v) return NULL; @@ -40,7 +42,7 @@ vector vector_init(unsigned int size) v->alloced = size; v->active = 0; - v->index = _talloc_zero(tall_vty_ctx, sizeof(void *) * size, + v->index = _talloc_zero(tall_vty_vec_ctx, sizeof(void *) * size, "vector_init:index"); if (!v->index) { talloc_free(v); @@ -68,7 +70,7 @@ void vector_free(vector v) vector vector_copy(vector v) { unsigned int size; - vector new = talloc_zero(tall_vty_ctx, struct _vector); + vector new = talloc_zero(tall_vty_vec_ctx, struct _vector); if (!new) return NULL; @@ -76,7 +78,7 @@ vector vector_copy(vector v) new->alloced = v->alloced; size = sizeof(void *) * (v->alloced); - new->index = _talloc_zero(tall_vty_ctx, size, "vector_copy:index"); + new->index = _talloc_zero(tall_vty_vec_ctx, size, "vector_copy:index"); if (!new->index) { talloc_free(new); return NULL; @@ -92,7 +94,7 @@ void vector_ensure(vector v, unsigned int num) if (v->alloced > num) return; - v->index = talloc_realloc_size(tall_vty_ctx, v->index, + v->index = talloc_realloc_size(tall_vty_vec_ctx, v->index, sizeof(void *) * (v->alloced * 2)); memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced); v->alloced *= 2; diff --git a/openbsc/src/vty/vty.c b/openbsc/src/vty/vty.c index 788c7fd6f..1260f38c5 100644 --- a/openbsc/src/vty/vty.c +++ b/openbsc/src/vty/vty.c @@ -17,7 +17,7 @@ #include <vty/vty.h> #include <vty/command.h> #include <vty/buffer.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> /* our callback, located in telnet_interface.c */ void vty_event(enum event event, int sock, struct vty *vty); @@ -1633,6 +1633,8 @@ extern void *tall_bsc_ctx; void vty_init() { tall_vty_ctx = talloc_named_const(NULL, 0, "vty"); + tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector"); + tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command"); /* For further configuration read, preserve current directory. */ vty_save_cwd(); diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c index 5197ec36b..c2488c3c4 100644 --- a/openbsc/src/vty_interface.c +++ b/openbsc/src/vty_interface.c @@ -28,13 +28,16 @@ #include <arpa/inet.h> -#include <openbsc/linuxlist.h> +#include <osmocore/linuxlist.h> #include <openbsc/gsm_data.h> #include <openbsc/e1_input.h> #include <openbsc/abis_nm.h> -#include <openbsc/gsm_utils.h> +#include <osmocore/gsm_utils.h> +#include <openbsc/chan_alloc.h> +#include <openbsc/meas_rep.h> #include <openbsc/db.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> +#include <openbsc/telnet_interface.h> static struct gsm_network *gsmnet; @@ -74,9 +77,30 @@ static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) nm_avail_name(nms->availability), VTY_NEWLINE); } +static void dump_pchan_load_vty(struct vty *vty, char *prefix, + const struct pchan_load *pl) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) { + const struct load_counter *lc = &pl->pchan[i]; + unsigned int percent; + + if (lc->total == 0) + continue; + + percent = (lc->used * 100) / lc->total; + + vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix, + gsm_pchan_name(i), percent, lc->used, lc->total, + VTY_NEWLINE); + } +} + static void net_dump_vty(struct vty *vty, struct gsm_network *net) { int i; + struct pchan_load pl; vty_out(vty, "BSC is on Country Code %u, Network Code %u " "and has %u BTS%s", net->country_code, net->network_code, @@ -99,6 +123,10 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net) VTY_NEWLINE); vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off", VTY_NEWLINE); + network_chan_load(&pl, net); + vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); + dump_pchan_load_vty(vty, " ", &pl); + vty_out(vty, " Allowed Audio Codecs: "); for (i = 0; i < net->audio_length; ++i) vty_out(vty, "hr: %d ver: %d, ", @@ -135,6 +163,8 @@ static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l) static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) { + struct pchan_load pl; + vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, " "BSIC %u, TSC %u and %u TRX%s", bts->nr, btstype2str(bts->type), gsm_band_name(bts->band), @@ -147,6 +177,11 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) VTY_NEWLINE); vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s", bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); + vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer, + VTY_NEWLINE); + vty_out(vty, "RACH Max transmissions: %u%s", + rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), + VTY_NEWLINE); if (bts->si_common.rach_control.cell_bar) vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE); if (is_ipaccess_bts(bts)) @@ -164,6 +199,10 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) e1isl_dump_vty(vty, bts->oml_link); } /* FIXME: oml_link, chan_desc */ + memset(&pl, 0, sizeof(pl)); + bts_chan_load(&pl, bts); + vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); + dump_pchan_load_vty(vty, " ", &pl); } DEFUN(show_bts, show_bts_cmd, "show bts [number]", @@ -234,6 +273,7 @@ static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx) vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE); vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE); + vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE); vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE); config_write_e1_link(vty, &trx->rsl_e1_link, " rsl "); vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE); @@ -265,6 +305,11 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) vty_out(vty, " channel allocator %s%s", bts->chan_alloc_reverse ? "descending" : "ascending", VTY_NEWLINE); + vty_out(vty, " rach tx integer %u%s", + bts->si_common.rach_control.tx_integer, VTY_NEWLINE); + vty_out(vty, " rach max transmission %u%s", + rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), + VTY_NEWLINE); if (bts->si_common.rach_control.cell_bar) vty_out(vty, " cell barred 1%s", VTY_NEWLINE); if (is_ipaccess_bts(bts)) { @@ -500,8 +545,12 @@ DEFUN(show_ts, return CMD_SUCCESS; } -void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr) +static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr) { + int rc; + struct gsm_auth_info ainfo; + struct gsm_auth_tuple atuple; + vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, subscr->authorized, VTY_NEWLINE); if (subscr->name) @@ -514,16 +563,54 @@ void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr) if (subscr->tmsi != GSM_RESERVED_TMSI) vty_out(vty, " TMSI: %08X%s", subscr->tmsi, VTY_NEWLINE); + vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); } +static void meas_rep_dump_uni_vty(struct vty *vty, + struct gsm_meas_rep_unidir *mru, + const char *prefix, + const char *dir) +{ + vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ", + prefix, dir, rxlev2dbm(mru->full.rx_lev), + dir, rxlev2dbm(mru->sub.rx_lev)); + vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s", + dir, mru->full.rx_qual, dir, mru->sub.rx_qual, + VTY_NEWLINE); +} + +static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr, + const char *prefix) +{ + vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE); + vty_out(vty, "%s Flags: %s%s%s%s%s", prefix, + mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "", + mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "", + mr->flags & MEAS_REP_F_FPC ? "FPC " : "", + mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ", + VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_MS_TO) + vty_out(vty, "%s MS Timing Offset: %u%s", prefix, + mr->ms_timing_offset, VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_MS_L1) + vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s", + prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_DL_VALID) + meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl"); + meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul"); +} + static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan) { + int idx; + vty_out(vty, "Lchan %u in Timeslot %u of TRX %u in BTS %u, Type %s%s", lchan->nr, lchan->ts->nr, lchan->ts->trx->nr, - lchan->ts->trx->bts->nr, gsm_lchan_name(lchan->type), + lchan->ts->trx->bts->nr, gsm_lchant_name(lchan->type), VTY_NEWLINE); - vty_out(vty, " Use Count: %u%s", lchan->use_count, VTY_NEWLINE); + vty_out(vty, " Use Count: %u, State: %s%s", lchan->use_count, + gsm_lchans_name(lchan->state), VTY_NEWLINE); vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s", lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red - lchan->bs_power*2, @@ -542,6 +629,11 @@ static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan) lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id, VTY_NEWLINE); } + + /* we want to report the last measurement report */ + idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, 1); + meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " "); } #if 0 @@ -794,6 +886,204 @@ DEFUN(show_paging, return CMD_SUCCESS; } +static void _vty_output(struct debug_target *tgt, const char *line) +{ + struct vty *vty = tgt->tgt_vty.vty; + vty_out(vty, "%s", line); + /* This is an ugly hack, but there is no easy way... */ + if (strchr(line, '\n')) + vty_out(vty, "\r"); +} + +struct debug_target *debug_target_create_vty(struct vty *vty) +{ + struct debug_target *target; + + target = debug_target_create(); + if (!target) + return NULL; + + target->tgt_vty.vty = vty; + target->output = _vty_output; + return target; +} + +DEFUN(enable_logging, + enable_logging_cmd, + "logging enable", + "Enables logging to this vty\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (conn->dbg) { + vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + conn->dbg = debug_target_create_vty(vty); + if (!conn->dbg) + return CMD_WARNING; + + debug_add_target(conn->dbg); + return CMD_SUCCESS; +} + +DEFUN(logging_fltr_imsi, + logging_fltr_imsi_cmd, + "logging filter imsi IMSI", + "Print all messages related to a IMSI\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_set_imsi_filter(conn->dbg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(logging_fltr_all, + logging_fltr_all_cmd, + "logging filter all <0-1>", + "Print all messages to the console\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_set_all_filter(conn->dbg, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(logging_use_clr, + logging_use_clr_cmd, + "logging color <0-1>", + "Use color for printing messages\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_set_use_color(conn->dbg, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(logging_prnt_timestamp, + logging_prnt_timestamp_cmd, + "logging timestamp <0-1>", + "Print the timestamp of each message\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_set_print_timestamp(conn->dbg, atoi(argv[0])); + return CMD_SUCCESS; +} + +/* FIXME: those have to be kept in sync with the log levels and categories */ +#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref)" +#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)" +DEFUN(logging_level, + logging_level_cmd, + "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS, + "Set the log level for a specified category\n") +{ + struct telnet_connection *conn; + int category = debug_parse_category(argv[0]); + int level = debug_parse_level(argv[1]); + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (category < 0) { + vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (level < 0) { + vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + conn->dbg->categories[category].enabled = 1; + conn->dbg->categories[category].loglevel = level; + + return CMD_SUCCESS; +} + +DEFUN(logging_set_category_mask, + logging_set_category_mask_cmd, + "logging set debug mask MASK", + "Decide which categories to output.\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_parse_category_mask(conn->dbg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(logging_set_log_level, + logging_set_log_level_cmd, + "logging set log level <0-8>", + "Set the global log level. The value 0 implies no filtering.\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_set_log_level(conn->dbg, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(diable_logging, + disable_logging_cmd, + "logging disable", + "Disables logging to this vty\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + debug_del_target(conn->dbg); + talloc_free(conn->dbg); + conn->dbg = NULL; + return CMD_SUCCESS; +} + DEFUN(show_stats, show_stats_cmd, "show statistics", @@ -801,31 +1091,36 @@ DEFUN(show_stats, { struct gsm_network *net = gsmnet; - vty_out(vty, "Channel Requests: %lu total, %lu no channel%s", - net->stats.chreq.total, net->stats.chreq.no_channel, - VTY_NEWLINE); - vty_out(vty, "Location Update: %lu attach, %lu normal, %lu periodic%s", - net->stats.loc_upd_type.attach, net->stats.loc_upd_type.normal, - net->stats.loc_upd_type.periodic, VTY_NEWLINE); - vty_out(vty, "IMSI Detach Indications: %lu%s\n", - net->stats.loc_upd_type.detach, VTY_NEWLINE); + vty_out(vty, "Channel Requests : %lu total, %lu no channel%s", + counter_get(net->stats.chreq.total), + counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); + vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s", + counter_get(net->stats.loc_upd_type.attach), + counter_get(net->stats.loc_upd_type.normal), + counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE); + vty_out(vty, "IMSI Detach Indications : %lu%s", + counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE); vty_out(vty, "Location Update Response: %lu accept, %lu reject%s", - net->stats.loc_upd_resp.accept, - net->stats.loc_upd_resp.reject, VTY_NEWLINE); - vty_out(vty, "Paging: %lu attempted, %lu complete, %lu expired%s", - net->stats.paging.attempted, net->stats.paging.completed, - net->stats.paging.expired, VTY_NEWLINE); - vty_out(vty, "Handover: %lu attempted, %lu no_channel, %lu timeout, " - "%lu completed, %lu failed%s", net->stats.handover.attempted, - net->stats.handover.no_channel, net->stats.handover.timeout, - net->stats.handover.completed, net->stats.handover.failed, - VTY_NEWLINE); - vty_out(vty, "SMS MO: %lu submitted, %lu no receiver%s", - net->stats.sms.submitted, net->stats.sms.no_receiver, - VTY_NEWLINE); - vty_out(vty, "SMS MT: %lu delivered, %lu no memory, %lu other error%s", - net->stats.sms.delivered, net->stats.sms.rp_err_mem, - net->stats.sms.rp_err_other, VTY_NEWLINE); + counter_get(net->stats.loc_upd_resp.accept), + counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE); + vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s", + counter_get(net->stats.paging.attempted), + counter_get(net->stats.paging.completed), + counter_get(net->stats.paging.expired), VTY_NEWLINE); + vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, " + "%lu completed, %lu failed%s", + counter_get(net->stats.handover.attempted), + counter_get(net->stats.handover.no_channel), + counter_get(net->stats.handover.timeout), + counter_get(net->stats.handover.completed), + counter_get(net->stats.handover.failed), VTY_NEWLINE); + vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s", + counter_get(net->stats.sms.submitted), + counter_get(net->stats.sms.no_receiver), VTY_NEWLINE); + vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s", + counter_get(net->stats.sms.delivered), + counter_get(net->stats.sms.rp_err_mem), + counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE); return CMD_SUCCESS; } @@ -970,13 +1265,15 @@ DEFUN(cfg_net_handover, cfg_net_handover_cmd, "handover (0|1)", "Whether or not to use in-call handover") { - if (ipacc_rtp_direct) { + int enable = atoi(argv[0]); + + if (enable && ipacc_rtp_direct) { vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode " "is enabled by using the -P command line option%s", VTY_NEWLINE); return CMD_WARNING; } - gsmnet->handover.active = atoi(argv[0]); + gsmnet->handover.active = enable; return CMD_SUCCESS; } @@ -1125,11 +1422,11 @@ DEFUN(cfg_net_bsc_token, return CMD_SUCCESS; } -#define DECLARE_TIMER(number) \ +#define DECLARE_TIMER(number, doc) \ DEFUN(cfg_net_T##number, \ cfg_net_T##number##_cmd, \ "timer t" #number " <0-65535>", \ - "Set the T" #number " value.") \ + doc) \ { \ int value = atoi(argv[0]); \ \ @@ -1143,17 +1440,17 @@ DEFUN(cfg_net_bsc_token, return CMD_SUCCESS; \ } -DECLARE_TIMER(3101) -DECLARE_TIMER(3103) -DECLARE_TIMER(3105) -DECLARE_TIMER(3107) -DECLARE_TIMER(3109) -DECLARE_TIMER(3111) -DECLARE_TIMER(3113) -DECLARE_TIMER(3115) -DECLARE_TIMER(3117) -DECLARE_TIMER(3119) -DECLARE_TIMER(3141) +DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT.") +DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.") +DECLARE_TIMER(3105, "Currently not used.") +DECLARE_TIMER(3107, "Currently not used.") +DECLARE_TIMER(3109, "Currently not used.") +DECLARE_TIMER(3111, "Currently not used.") +DECLARE_TIMER(3113, "Set the time to try paging a subscriber.") +DECLARE_TIMER(3115, "Currently not used.") +DECLARE_TIMER(3117, "Currently not used.") +DECLARE_TIMER(3119, "Currently not used.") +DECLARE_TIMER(3141, "Currently not used.") /* per-BTS configuration */ DEFUN(cfg_bts, @@ -1175,8 +1472,11 @@ DEFUN(cfg_bts, } else bts = gsm_bts_num(gsmnet, bts_nr); - if (!bts) + if (!bts) { + vty_out(vty, "%% Unable to allocate BTS %u%s", + gsmnet->num_bts, VTY_NEWLINE); return CMD_WARNING; + } vty->index = bts; vty->node = BTS_NODE; @@ -1190,13 +1490,11 @@ DEFUN(cfg_bts_type, "Set the BTS type\n") { struct gsm_bts *bts = vty->index; + int rc; - bts->type = parse_btstype(argv[0]); - - if (is_ipaccess_bts(bts)) { - /* Set the default OML Stream ID to 0xff */ - bts->oml_tei = 0xff; - } + rc = gsm_set_bts_type(bts, parse_btstype(argv[0])); + if (rc < 0) + return CMD_WARNING; return CMD_SUCCESS; } @@ -1379,6 +1677,26 @@ DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd, return CMD_SUCCESS; } +DEFUN(cfg_bts_rach_tx_integer, + cfg_bts_rach_tx_integer_cmd, + "rach tx integer <0-15>", + "Set the raw tx integer value in RACH Control parameters IE") +{ + struct gsm_bts *bts = vty->index; + bts->si_common.rach_control.tx_integer = atoi(argv[0]) & 0xf; + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_rach_max_trans, + cfg_bts_rach_max_trans_cmd, + "rach max transmission (1|2|4|7)", + "Set the maximum number of RACH burst transmissions") +{ + struct gsm_bts *bts = vty->index; + bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0])); + return CMD_SUCCESS; +} + DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd, "cell barred (0|1)", "Should this cell be barred from access?") @@ -1483,6 +1801,18 @@ DEFUN(cfg_trx_arfcn, return CMD_SUCCESS; } +DEFUN(cfg_trx_nominal_power, + cfg_trx_nominal_power_cmd, + "nominal power <0-100>", + "Nominal TRX RF Power in dB\n") +{ + struct gsm_bts_trx *trx = vty->index; + + trx->nominal_power = atoi(argv[0]); + + return CMD_SUCCESS; +} + DEFUN(cfg_trx_max_power_red, cfg_trx_max_power_red_cmd, "max_power_red <0-100>", @@ -1620,6 +1950,16 @@ int bsc_vty_init(struct gsm_network *net) install_element(VIEW_NODE, &show_paging_cmd); install_element(VIEW_NODE, &show_stats_cmd); + install_element(VIEW_NODE, &enable_logging_cmd); + install_element(VIEW_NODE, &disable_logging_cmd); + install_element(VIEW_NODE, &logging_fltr_imsi_cmd); + install_element(VIEW_NODE, &logging_fltr_all_cmd); + install_element(VIEW_NODE, &logging_use_clr_cmd); + install_element(VIEW_NODE, &logging_prnt_timestamp_cmd); + install_element(VIEW_NODE, &logging_set_category_mask_cmd); + install_element(VIEW_NODE, &logging_level_cmd); + install_element(VIEW_NODE, &logging_set_log_level_cmd); + install_element(CONFIG_NODE, &cfg_net_cmd); install_node(&net_node, config_write_net); install_default(GSMNET_NODE); @@ -1672,6 +2012,8 @@ int bsc_vty_init(struct gsm_network *net) install_element(BTS_NODE, &cfg_bts_oml_e1_cmd); install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd); install_element(BTS_NODE, &cfg_bts_challoc_cmd); + install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd); + install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd); install_element(BTS_NODE, &cfg_bts_cell_barred_cmd); install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd); install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd); @@ -1683,6 +2025,7 @@ int bsc_vty_init(struct gsm_network *net) install_node(&trx_node, dummy_config_write); install_default(TRX_NODE); install_element(TRX_NODE, &cfg_trx_arfcn_cmd); + install_element(TRX_NODE, &cfg_trx_nominal_power_cmd); install_element(TRX_NODE, &cfg_trx_max_power_red_cmd); install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd); install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd); diff --git a/openbsc/src/vty_interface_layer3.c b/openbsc/src/vty_interface_layer3.c index 4cc08c2da..b824c3db6 100644 --- a/openbsc/src/vty_interface_layer3.c +++ b/openbsc/src/vty_interface_layer3.c @@ -29,20 +29,18 @@ #include <arpa/inet.h> -#include <openbsc/linuxlist.h> +#include <osmocore/linuxlist.h> #include <openbsc/gsm_data.h> #include <openbsc/gsm_subscriber.h> #include <openbsc/silent_call.h> #include <openbsc/gsm_04_11.h> #include <openbsc/e1_input.h> #include <openbsc/abis_nm.h> -#include <openbsc/gsm_utils.h> +#include <osmocore/gsm_utils.h> #include <openbsc/db.h> -#include <openbsc/talloc.h> +#include <osmocore/talloc.h> #include <openbsc/signal.h> - -/* forward declarations */ -void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr); +#include <openbsc/debug.h> static struct gsm_network *gsmnet; @@ -74,6 +72,33 @@ static struct buffer *argv_to_buffer(int argc, const char *argv[], int base) return b; } +static int hexparse(const char *str, u_int8_t *b, int max_len) + +{ + int i, l, v; + + l = strlen(str); + if ((l&1) || ((l>>1) > max_len)) + return -1; + + memset(b, 0x00, max_len); + + for (i=0; i<l; i++) { + char c = str[i]; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'a' && c <= 'f') + v = 10 + (c - 'a'); + else if (c >= 'A' && c <= 'F') + v = 10 + (c - 'a'); + else + return -1; + b[i>>1] |= v << (i&1 ? 0 : 4); + } + + return i>>1; +} + /* per-subscriber configuration */ DEFUN(cfg_subscr, cfg_subscr_cmd, @@ -97,6 +122,55 @@ DEFUN(cfg_subscr, return CMD_SUCCESS; } +static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr) +{ + int rc; + struct gsm_auth_info ainfo; + struct gsm_auth_tuple atuple; + + vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, + subscr->authorized, VTY_NEWLINE); + if (subscr->name) + vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); + if (subscr->extension) + vty_out(vty, " Extension: %s%s", subscr->extension, + VTY_NEWLINE); + if (subscr->imsi) + vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); + if (subscr->tmsi != GSM_RESERVED_TMSI) + vty_out(vty, " TMSI: %08X%s", subscr->tmsi, + VTY_NEWLINE); + + rc = get_authinfo_by_subscr(&ainfo, subscr); + if (!rc) { + vty_out(vty, " A3A8 algorithm id: %d%s", + ainfo.auth_algo, VTY_NEWLINE); + vty_out(vty, " A3A8 Ki: %s%s", + hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len), + VTY_NEWLINE); + } + + rc = get_authtuple_by_subscr(&atuple, subscr); + if (!rc) { + vty_out(vty, " A3A8 last tuple (used %d times):%s", + atuple.use_count, VTY_NEWLINE); + vty_out(vty, " seq # : %d%s", + atuple.key_seq, VTY_NEWLINE); + vty_out(vty, " RAND : %s%s", + hexdump(atuple.rand, sizeof(atuple.rand)), + VTY_NEWLINE); + vty_out(vty, " SRES : %s%s", + hexdump(atuple.sres, sizeof(atuple.sres)), + VTY_NEWLINE); + vty_out(vty, " Kc : %s%s", + hexdump(atuple.kc, sizeof(atuple.kc)), + VTY_NEWLINE); + } + + vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); +} + + /* Subscriber */ DEFUN(show_subscr, show_subscr_cmd, @@ -114,7 +188,7 @@ DEFUN(show_subscr, VTY_NEWLINE); return CMD_WARNING; } - subscr_dump_vty(vty, subscr); + subscr_dump_full_vty(vty, subscr); subscr_put(subscr); return CMD_SUCCESS; @@ -135,7 +209,7 @@ DEFUN(show_subscr_cache, llist_for_each_entry(subscr, &active_subscribers, entry) { vty_out(vty, " Subscriber:%s", VTY_NEWLINE); - subscr_dump_vty(vty, subscr); + subscr_dump_full_vty(vty, subscr); } return CMD_SUCCESS; @@ -143,23 +217,20 @@ DEFUN(show_subscr_cache, DEFUN(sms_send_pend, sms_send_pend_cmd, - "sms send pending MIN_ID", - "Send all pending SMS starting from MIN_ID") + "sms send pending", + "Send all pending SMS") { struct gsm_sms *sms; - int id = atoi(argv[0]); + int id = 0; while (1) { - sms = db_sms_get_unsent(gsmnet, id++); + sms = db_sms_get_unsent_by_subscr(gsmnet, id); if (!sms) - return CMD_WARNING; - - if (!sms->receiver) { - sms_free(sms); - continue; - } + break; gsm411_send_sms_subscr(sms->receiver, sms); + + id = sms->receiver->id + 1; } return CMD_SUCCESS; @@ -269,10 +340,46 @@ DEFUN(subscriber_silent_sms, return rc; } -DEFUN(subscriber_silent_call, - subscriber_silent_call_cmd, - "subscriber " SUBSCR_TYPES " EXTEN silent call (start|stop)", - "Send a silent call to a subscriber") +DEFUN(subscriber_silent_call_start, + subscriber_silent_call_start_cmd, + "subscriber " SUBSCR_TYPES " EXTEN silent call start (any|tch/f|tch/any|sdcch)", + "Start a silent call to a subscriber") +{ + struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]); + int rc, type; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[2], "tch/f")) + type = RSL_CHANNEED_TCH_F; + else if (!strcmp(argv[2], "tch/any")) + type = RSL_CHANNEED_TCH_ForH; + else if (!strcmp(argv[2], "sdcch")) + type = RSL_CHANNEED_SDCCH; + else + type = RSL_CHANNEED_ANY; /* Defaults to ANY */ + + rc = gsm_silent_call_start(subscr, vty, type); + if (rc <= 0) { + vty_out(vty, "%% Subscriber not attached%s", + VTY_NEWLINE); + subscr_put(subscr); + return CMD_WARNING; + } + + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(subscriber_silent_call_stop, + subscriber_silent_call_stop_cmd, + "subscriber " SUBSCR_TYPES " EXTEN silent call stop", + "Stop a silent call to a subscriber") { struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]); int rc; @@ -283,17 +390,10 @@ DEFUN(subscriber_silent_call, return CMD_WARNING; } - if (!strcmp(argv[2], "start")) { - rc = gsm_silent_call_start(subscr, vty); - if (rc <= 0) { - vty_out(vty, "%% Subscriber not attached%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } else { - rc = gsm_silent_call_stop(subscr); - if (rc < 0) - return CMD_WARNING; + rc = gsm_silent_call_stop(subscr); + if (rc < 0) { + subscr_put(subscr); + return CMD_WARNING; } subscr_put(subscr); @@ -349,6 +449,40 @@ DEFUN(cfg_subscr_authorized, return CMD_SUCCESS; } +#define A3A8_ALG_TYPES "(none|comp128v1)" + +DEFUN(cfg_subscr_a3a8, + cfg_subscr_a3a8_cmd, + "a3a8 " A3A8_ALG_TYPES " [KI]", + "Set a3a8 parameters for the subscriber") +{ + struct gsm_subscriber *subscr = vty->index; + const char *alg_str = argv[0]; + const char *ki_str = argv[1]; + struct gsm_auth_info ainfo; + int rc; + + if (!strcasecmp(alg_str, "none")) { + /* Just erase */ + rc = set_authinfo_for_subscr(NULL, subscr); + } else if (!strcasecmp(alg_str, "comp128v1")) { + /* Parse hex string Ki */ + rc = hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki)); + if (rc != 16) + return CMD_WARNING; + + /* Set the infos */ + ainfo.auth_algo = AUTH_ALGO_COMP128v1; + ainfo.a3a8_ki_len = rc; + rc = set_authinfo_for_subscr(&ainfo, subscr); + } else { + /* Unknown method */ + return CMD_WARNING; + } + + return rc ? CMD_WARNING : CMD_SUCCESS; +} + static int scall_cbfn(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { @@ -381,7 +515,8 @@ int bsc_vty_init_extra(struct gsm_network *net) install_element(VIEW_NODE, &subscriber_send_sms_cmd); install_element(VIEW_NODE, &subscriber_silent_sms_cmd); - install_element(VIEW_NODE, &subscriber_silent_call_cmd); + install_element(VIEW_NODE, &subscriber_silent_call_start_cmd); + install_element(VIEW_NODE, &subscriber_silent_call_stop_cmd); install_element(CONFIG_NODE, &cfg_subscr_cmd); install_node(&subscr_node, dummy_config_write); @@ -390,6 +525,7 @@ int bsc_vty_init_extra(struct gsm_network *net) install_element(SUBSCR_NODE, &cfg_subscr_name_cmd); install_element(SUBSCR_NODE, &cfg_subscr_extension_cmd); install_element(SUBSCR_NODE, &cfg_subscr_authorized_cmd); + install_element(SUBSCR_NODE, &cfg_subscr_a3a8_cmd); return 0; } |