| /* Bloated main routine, refactor */ |
| /* |
| * (C) 2010-2011 by Holger Hans Peter Freyther <zecke@selfish.org> |
| * (C) 2010-2011 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 Affero General Public License as published by |
| * the Free Software Foundation, either version 3 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 Affero General Public License for more details. |
| * |
| * You should have received a copy of the GNU Affero General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| * |
| */ |
| |
| #include <mtp_data.h> |
| #include <mtp_level3.h> |
| #include <mtp_pcap.h> |
| #include <thread.h> |
| #include <bss_patch.h> |
| #include <bssap_sccp.h> |
| #include <bsc_data.h> |
| #include <cellmgr_debug.h> |
| #include <bsc_sccp.h> |
| |
| #include <osmocore/talloc.h> |
| |
| #include <osmocom/vty/vty.h> |
| #include <osmocom/vty/telnet_interface.h> |
| |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <unistd.h> |
| |
| #ifndef _GNU_SOURCE |
| #define _GNU_SOURCE |
| #endif |
| #include <getopt.h> |
| |
| #undef PACKAGE_NAME |
| #undef PACKAGE_VERSION |
| #undef PACKAGE_BUGREPORT |
| #undef PACKAGE_TARNAME |
| #undef PACKAGE_STRING |
| #include <cellmgr_config.h> |
| |
| static struct log_target *stderr_target; |
| |
| static char *config = "cellmgr_ng.cfg"; |
| |
| struct bsc_data bsc; |
| extern void cell_vty_init(void); |
| |
| static void send_reset_ack(struct mtp_link_set *link, int sls); |
| static void bsc_resources_released(struct bsc_data *bsc); |
| static void handle_local_sccp(struct mtp_link_set *link, struct msgb *inp, struct sccp_parse_result *res, int sls); |
| static void clear_connections(struct bsc_data *bsc); |
| static void send_local_rlsd(struct mtp_link_set *link, struct sccp_parse_result *res); |
| |
| /* send a RSIP to the MGCP GW */ |
| static void mgcp_reset(struct bsc_data *bsc) |
| { |
| static const char mgcp_reset[] = { |
| "RSIP 1 13@mgw MGCP 1.0\r\n" |
| }; |
| |
| mgcp_forward(bsc, (const uint8_t *) mgcp_reset, strlen(mgcp_reset)); |
| } |
| |
| /* |
| * methods called from the MTP Level3 part |
| */ |
| void mtp_link_set_forward_sccp(struct mtp_link_set *link, struct msgb *_msg, int sls) |
| { |
| int rc; |
| struct sccp_parse_result result; |
| |
| rc = bss_patch_filter_msg(_msg, &result); |
| if (rc == BSS_FILTER_RESET) { |
| LOGP(DMSC, LOGL_NOTICE, "Filtering BSS Reset from the BSC\n"); |
| mgcp_reset(&bsc); |
| send_reset_ack(link, sls); |
| return; |
| } |
| |
| /* special responder */ |
| if (bsc.msc_link_down) { |
| if (rc == BSS_FILTER_RESET_ACK && bsc.reset_count > 0) { |
| LOGP(DMSC, LOGL_ERROR, "Received reset ack for closing.\n"); |
| clear_connections(&bsc); |
| bsc_resources_released(&bsc); |
| return; |
| } |
| |
| if (rc != 0 && rc != BSS_FILTER_RLSD && rc != BSS_FILTER_RLC) { |
| LOGP(DMSC, LOGL_ERROR, "Ignoring unparsable msg during closedown.\n"); |
| return; |
| } |
| |
| return handle_local_sccp(link, _msg, &result, sls); |
| } |
| |
| /* update the connection state */ |
| update_con_state(link, rc, &result, _msg, 0, sls); |
| |
| if (rc == BSS_FILTER_CLEAR_COMPL) { |
| send_local_rlsd(link, &result); |
| } else if (rc == BSS_FILTER_RLC || rc == BSS_FILTER_RLSD) { |
| LOGP(DMSC, LOGL_DEBUG, "Not forwarding RLC/RLSD to the MSC.\n"); |
| return; |
| } |
| |
| |
| msc_send_msg(&bsc, rc, &result, _msg); |
| } |
| |
| void mtp_link_set_forward_isup(struct mtp_link_set *set, struct msgb *msg, int sls) |
| { |
| LOGP(DINP, LOGL_ERROR, "ISUP is not handled.\n"); |
| } |
| |
| /* |
| * handle local message in close down mode |
| */ |
| static void handle_local_sccp(struct mtp_link_set *link, struct msgb *inpt, struct sccp_parse_result *result, int sls) |
| { |
| /* Handle msg with a reject */ |
| if (inpt->l2h[0] == SCCP_MSG_TYPE_CR) { |
| struct sccp_connection_request *cr; |
| struct msgb *msg; |
| |
| LOGP(DINP, LOGL_NOTICE, "Handling CR localy.\n"); |
| cr = (struct sccp_connection_request *) inpt->l2h; |
| msg = create_sccp_refuse(&cr->source_local_reference); |
| if (msg) { |
| mtp_link_set_submit_sccp_data(link, sls, msg->l2h, msgb_l2len(msg)); |
| msgb_free(msg); |
| } |
| return; |
| } else if (inpt->l2h[0] == SCCP_MSG_TYPE_DT1 && result->data_len >= 3) { |
| struct active_sccp_con *con; |
| struct sccp_data_form1 *form1; |
| struct msgb *msg; |
| |
| if (inpt->l3h[0] == 0 && inpt->l3h[2] == BSS_MAP_MSG_CLEAR_COMPLETE) { |
| LOGP(DINP, LOGL_DEBUG, "Received Clear Complete. Sending Release.\n"); |
| |
| form1 = (struct sccp_data_form1 *) inpt->l2h; |
| |
| llist_for_each_entry(con, &bsc.sccp_connections, entry) { |
| if (memcmp(&form1->destination_local_reference, |
| &con->dst_ref, sizeof(con->dst_ref)) == 0) { |
| LOGP(DINP, LOGL_DEBUG, "Sending a release request now.\n"); |
| msg = create_sccp_rlsd(&con->dst_ref, &con->src_ref); |
| if (msg) { |
| mtp_link_set_submit_sccp_data(link, con->sls, msg->l2h, msgb_l2len(msg)); |
| msgb_free(msg); |
| } |
| return; |
| } |
| } |
| |
| LOGP(DINP, LOGL_ERROR, "Could not find connection for the Clear Command.\n"); |
| } |
| } else if (inpt->l2h[0] == SCCP_MSG_TYPE_UDT && result->data_len >= 3) { |
| if (inpt->l3h[0] == 0 && inpt->l3h[2] == BSS_MAP_MSG_RESET_ACKNOWLEDGE) { |
| LOGP(DINP, LOGL_NOTICE, "Reset ACK. Connecting to the MSC again.\n"); |
| bsc_resources_released(&bsc); |
| return; |
| } |
| } |
| |
| |
| /* Update the state, maybe the connection was released? */ |
| update_con_state(link, 0, result, inpt, 0, sls); |
| if (llist_empty(&bsc.sccp_connections)) |
| bsc_resources_released(&bsc); |
| return; |
| } |
| |
| static void clear_connections(struct bsc_data *bsc) |
| { |
| struct active_sccp_con *tmp, *con; |
| |
| llist_for_each_entry_safe(con, tmp, &bsc->sccp_connections, entry) { |
| free_con(con); |
| } |
| |
| link_clear_all(bsc->link_set); |
| } |
| |
| void bsc_resources_released(struct bsc_data *bsc) |
| { |
| bsc_del_timer(&bsc->reset_timeout); |
| } |
| |
| static void bsc_reset_timeout(void *_data) |
| { |
| struct msgb *msg; |
| struct bsc_data *bsc = (struct bsc_data *) _data; |
| |
| /* no reset */ |
| if (bsc->reset_count > 0) { |
| LOGP(DINP, LOGL_ERROR, "The BSC did not answer the GSM08.08 reset. Restart MTP\n"); |
| mtp_link_set_stop(bsc->link_set); |
| clear_connections(bsc); |
| link_reset_all(bsc->link_set); |
| bsc_resources_released(bsc); |
| return; |
| } |
| |
| msg = create_reset(); |
| if (!msg) { |
| bsc_schedule_timer(&bsc->reset_timeout, 10, 0); |
| return; |
| } |
| |
| ++bsc->reset_count; |
| mtp_link_set_submit_sccp_data(bsc->link_set, -1, msg->l2h, msgb_l2len(msg)); |
| msgb_free(msg); |
| bsc_schedule_timer(&bsc->reset_timeout, 20, 0); |
| } |
| |
| /* |
| * We have lost the connection to the MSC. This is tough. We |
| * can not just bring down the MTP link as this will disable |
| * the BTS radio. We will have to do the following: |
| * |
| * 1.) Bring down all open SCCP connections. As this will close |
| * all radio resources |
| * 2.) Bring down all MGCP endpoints |
| * 3.) Clear the connection data. |
| * |
| * To make things worse we need to buffer the BSC messages... atfer |
| * everything has been sent we will try to connect to the MSC again. |
| * |
| * We will have to veriy that all connections are closed properly.. |
| * this means we need to parse response message. In the case the |
| * MTP link is going down while we are sending. We will simply |
| * reconnect to the MSC. |
| */ |
| void release_bsc_resources(struct bsc_data *bsc) |
| { |
| struct active_sccp_con *tmp; |
| struct active_sccp_con *con; |
| |
| bsc_del_timer(&bsc->reset_timeout); |
| |
| /* 2. clear the MGCP endpoints */ |
| mgcp_reset(bsc); |
| |
| /* 1. send BSSMAP Cleanup.. if we have any connection */ |
| llist_for_each_entry_safe(con, tmp, &bsc->sccp_connections, entry) { |
| if (!con->has_dst_ref) { |
| free_con(con); |
| continue; |
| } |
| |
| struct msgb *msg = create_clear_command(&con->src_ref); |
| if (!msg) |
| continue; |
| |
| /* wait for the clear commands */ |
| mtp_link_set_submit_sccp_data(bsc->link_set, con->sls, msg->l2h, msgb_l2len(msg)); |
| msgb_free(msg); |
| } |
| |
| if (llist_empty(&bsc->sccp_connections)) { |
| bsc_resources_released(bsc); |
| } else { |
| /* Send a reset in 20 seconds if we fail to bring everything down */ |
| bsc->reset_timeout.cb = bsc_reset_timeout; |
| bsc->reset_timeout.data = bsc; |
| bsc->reset_count = 0; |
| bsc_schedule_timer(&bsc->reset_timeout, 10, 0); |
| } |
| } |
| |
| void mtp_linkset_down(struct mtp_link_set *set) |
| { |
| set->available = 0; |
| mtp_link_set_stop(set); |
| clear_connections(set->bsc); |
| mgcp_reset(set->bsc); |
| |
| /* If we have an A link send a reset to the MSC */ |
| msc_send_reset(set->bsc); |
| } |
| |
| void mtp_linkset_up(struct mtp_link_set *set) |
| { |
| set->available = 1; |
| |
| /* we have not gone through link down */ |
| if (set->bsc->msc_link_down) { |
| clear_connections(set->bsc); |
| bsc_resources_released(set->bsc); |
| } |
| |
| mtp_link_set_reset(set); |
| } |
| |
| /** |
| * update the connection state and helpers below |
| */ |
| static void send_rlc_to_bsc(unsigned int sls, struct sccp_source_reference *src, struct sccp_source_reference *dst) |
| { |
| struct msgb *msg; |
| |
| msg = create_sccp_rlc(src, dst); |
| if (!msg) |
| return; |
| |
| mtp_link_set_submit_sccp_data(bsc.link_set, sls, msg->l2h, msgb_l2len(msg)); |
| msgb_free(msg); |
| } |
| |
| static void handle_rlsd(struct sccp_connection_released *rlsd, int from_msc) |
| { |
| struct active_sccp_con *con; |
| |
| if (from_msc) { |
| /* search for a connection, reverse src/dest for MSC */ |
| con = find_con_by_src_dest_ref(&rlsd->destination_local_reference, |
| &rlsd->source_local_reference); |
| if (con) { |
| LOGP(DINP, LOGL_DEBUG, "RLSD conn still alive: local: 0x%x remote: 0x%x\n", |
| sccp_src_ref_to_int(&con->src_ref), |
| sccp_src_ref_to_int(&con->dst_ref)); |
| con->released_from_msc = 1; |
| } else { |
| /* send RLC */ |
| LOGP(DINP, LOGL_DEBUG, "Sending RLC for MSC: src: 0x%x dst: 0x%x\n", |
| sccp_src_ref_to_int(&rlsd->destination_local_reference), |
| sccp_src_ref_to_int(&rlsd->source_local_reference)); |
| msc_send_rlc(&bsc, &rlsd->destination_local_reference, |
| &rlsd->source_local_reference); |
| } |
| } else { |
| unsigned int sls = -1; |
| con = find_con_by_src_dest_ref(&rlsd->source_local_reference, |
| &rlsd->destination_local_reference); |
| if (con) { |
| LOGP(DINP, LOGL_DEBUG, "Timeout on BSC. Sending RLC. src: 0x%x\n", |
| sccp_src_ref_to_int(&rlsd->source_local_reference)); |
| |
| if (con->released_from_msc) |
| msc_send_rlc(&bsc, &con->src_ref, &con->dst_ref); |
| sls = con->sls; |
| free_con(con); |
| } else { |
| LOGP(DINP, LOGL_ERROR, "Timeout on BSC for unknown connection. src: 0x%x\n", |
| sccp_src_ref_to_int(&rlsd->source_local_reference)); |
| } |
| |
| /* now send a rlc back to the BSC */ |
| send_rlc_to_bsc(sls, &rlsd->destination_local_reference, &rlsd->source_local_reference); |
| } |
| } |
| |
| /** |
| * Update connection state and also send message..... |
| * |
| * RLSD from MSC: |
| * 1.) We don't find the entry in this case we will send a |
| * forged RLC to the MSC and we are done. |
| * 2.) We find an entry in this we will need to register that |
| * we need to send a RLC and we are done for now. |
| * RLSD from BSC: |
| * 1.) This is an error we are ignoring for now. |
| * RLC from BSC: |
| * 1.) We are destroying the connection, we might send a RLC to |
| * the MSC if we are waiting for one. |
| */ |
| void update_con_state(struct mtp_link_set *link, int rc, struct sccp_parse_result *res, struct msgb *msg, int from_msc, int sls) |
| { |
| struct active_sccp_con *con; |
| struct sccp_connection_request *cr; |
| struct sccp_connection_confirm *cc; |
| struct sccp_connection_release_complete *rlc; |
| struct sccp_connection_refused *cref; |
| |
| /* was the header okay? */ |
| if (rc < 0) |
| return; |
| |
| /* the header was size checked */ |
| switch (msg->l2h[0]) { |
| case SCCP_MSG_TYPE_CR: |
| if (from_msc) { |
| LOGP(DMSC, LOGL_ERROR, "CR from MSC is not handled.\n"); |
| return; |
| } |
| |
| cr = (struct sccp_connection_request *) msg->l2h; |
| con = find_con_by_src_ref(&cr->source_local_reference); |
| if (con) { |
| LOGP(DINP, LOGL_ERROR, "Duplicate SRC reference for: 0x%x. Reusing\n", |
| sccp_src_ref_to_int(&con->src_ref)); |
| free_con(con); |
| } |
| |
| con = talloc_zero(NULL, struct active_sccp_con); |
| if (!con) { |
| LOGP(DINP, LOGL_ERROR, "Failed to allocate\n"); |
| return; |
| } |
| |
| con->src_ref = cr->source_local_reference; |
| con->sls = sls; |
| con->link = link; |
| llist_add_tail(&con->entry, &bsc.sccp_connections); |
| LOGP(DINP, LOGL_DEBUG, "Adding CR: local ref: 0x%x\n", sccp_src_ref_to_int(&con->src_ref)); |
| break; |
| case SCCP_MSG_TYPE_CC: |
| if (!from_msc) { |
| LOGP(DINP, LOGL_ERROR, "CC from BSC is not handled.\n"); |
| return; |
| } |
| |
| cc = (struct sccp_connection_confirm *) msg->l2h; |
| con = find_con_by_src_ref(&cc->destination_local_reference); |
| if (con) { |
| con->dst_ref = cc->source_local_reference; |
| con->has_dst_ref = 1; |
| LOGP(DINP, LOGL_DEBUG, "Updating CC: local: 0x%x remote: 0x%x\n", |
| sccp_src_ref_to_int(&con->src_ref), sccp_src_ref_to_int(&con->dst_ref)); |
| return; |
| } |
| |
| LOGP(DINP, LOGL_ERROR, "CCed connection can not be found: 0x%x\n", |
| sccp_src_ref_to_int(&cc->destination_local_reference)); |
| break; |
| case SCCP_MSG_TYPE_CREF: |
| if (!from_msc) { |
| LOGP(DINP, LOGL_ERROR, "CREF from BSC is not handled.\n"); |
| return; |
| } |
| |
| cref = (struct sccp_connection_refused *) msg->l2h; |
| con = find_con_by_src_ref(&cref->destination_local_reference); |
| if (con) { |
| LOGP(DINP, LOGL_DEBUG, "Releasing local: 0x%x\n", sccp_src_ref_to_int(&con->src_ref)); |
| free_con(con); |
| return; |
| } |
| |
| LOGP(DINP, LOGL_ERROR, "CREF from BSC is not handled.\n"); |
| break; |
| case SCCP_MSG_TYPE_RLSD: |
| handle_rlsd((struct sccp_connection_released *) msg->l2h, from_msc); |
| break; |
| case SCCP_MSG_TYPE_RLC: |
| if (from_msc) { |
| LOGP(DINP, LOGL_ERROR, "RLC from MSC is wrong.\n"); |
| return; |
| } |
| |
| rlc = (struct sccp_connection_release_complete *) msg->l2h; |
| con = find_con_by_src_dest_ref(&rlc->source_local_reference, |
| &rlc->destination_local_reference); |
| if (con) { |
| LOGP(DINP, LOGL_DEBUG, "Releasing local: 0x%x\n", sccp_src_ref_to_int(&con->src_ref)); |
| if (con->released_from_msc) |
| msc_send_rlc(&bsc, &con->src_ref, &con->dst_ref); |
| free_con(con); |
| return; |
| } |
| |
| LOGP(DINP, LOGL_ERROR, "RLC can not be found. 0x%x 0x%x\n", |
| sccp_src_ref_to_int(&rlc->source_local_reference), |
| sccp_src_ref_to_int(&rlc->destination_local_reference)); |
| break; |
| } |
| } |
| |
| static void send_local_rlsd_for_con(void *data) |
| { |
| struct msgb *rlsd; |
| struct active_sccp_con *con = (struct active_sccp_con *) data; |
| |
| /* try again in three seconds */ |
| con->rlc_timeout.data = con; |
| con->rlc_timeout.cb = send_local_rlsd_for_con; |
| bsc_schedule_timer(&con->rlc_timeout, 3, 0); |
| |
| /* we send this to the BSC so we need to switch src and dest */ |
| rlsd = create_sccp_rlsd(&con->dst_ref, &con->src_ref); |
| if (!rlsd) |
| return; |
| |
| ++con->rls_tries; |
| LOGP(DINP, LOGL_DEBUG, "Sending RLSD for 0x%x the %d time.\n", |
| sccp_src_ref_to_int(&con->src_ref), con->rls_tries); |
| mtp_link_set_submit_sccp_data(bsc.link_set, con->sls, rlsd->l2h, msgb_l2len(rlsd)); |
| msgb_free(rlsd); |
| } |
| |
| static void send_local_rlsd(struct mtp_link_set *link, struct sccp_parse_result *res) |
| { |
| struct active_sccp_con *con; |
| |
| LOGP(DINP, LOGL_DEBUG, "Received GSM Clear Complete. Sending RLSD locally.\n"); |
| |
| con = find_con_by_dest_ref(res->destination_local_reference); |
| if (!con) |
| return; |
| con->rls_tries = 0; |
| send_local_rlsd_for_con(con); |
| } |
| |
| static void send_reset_ack(struct mtp_link_set *link, int sls) |
| { |
| static const uint8_t reset_ack[] = { |
| 0x09, 0x00, 0x03, 0x05, 0x7, 0x02, 0x42, 0xfe, |
| 0x02, 0x42, 0xfe, 0x03, |
| 0x00, 0x01, 0x31 |
| }; |
| |
| mtp_link_set_submit_sccp_data(link, sls, reset_ack, sizeof(reset_ack)); |
| } |
| |
| static void print_usage() |
| { |
| printf("Usage: cellmgr_ng\n"); |
| } |
| |
| static void sigint() |
| { |
| static pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER; |
| static int handled = 0; |
| |
| /* failed to lock */ |
| if (pthread_mutex_trylock(&exit_mutex) != 0) |
| return; |
| if (handled) |
| goto out; |
| |
| printf("Terminating.\n"); |
| handled = 1; |
| if (bsc.setup) |
| link_shutdown_all(bsc.link_set); |
| exit(0); |
| |
| out: |
| pthread_mutex_unlock(&exit_mutex); |
| } |
| |
| static void sigusr2() |
| { |
| printf("Closing the MSC connection on demand.\n"); |
| msc_close_connection(&bsc); |
| } |
| |
| static void print_help() |
| { |
| printf(" Some useful help...\n"); |
| printf(" -h --help this text\n"); |
| printf(" -c --config=CFG The config file to use.\n"); |
| printf(" -p --pcap=FILE. Write MSUs to the PCAP file.\n"); |
| printf(" -c --once. Send the SLTM msg only once.\n"); |
| printf(" -v --version. Print the version number\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", 1, 0, 'c'}, |
| {"pcap", 1, 0, 'p'}, |
| {"version", 0, 0, 0}, |
| {0, 0, 0, 0}, |
| }; |
| |
| c = getopt_long(argc, argv, "hc:p:v", |
| long_options, &option_index); |
| if (c == -1) |
| break; |
| |
| switch (c) { |
| case 'h': |
| print_usage(); |
| print_help(); |
| exit(0); |
| case 'p': |
| if (bsc.pcap_fd >= 0) |
| close(bsc.pcap_fd); |
| bsc.pcap_fd = open(optarg, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP| S_IROTH); |
| if (bsc.pcap_fd < 0) { |
| fprintf(stderr, "Failed to open PCAP file.\n"); |
| exit(0); |
| } |
| mtp_pcap_write_header(bsc.pcap_fd); |
| break; |
| case 'c': |
| config = optarg; |
| break; |
| case 'v': |
| printf("This is %s version %s.\n", PACKAGE, VERSION); |
| exit(0); |
| break; |
| default: |
| fprintf(stderr, "Unknown option.\n"); |
| break; |
| } |
| } |
| } |
| |
| int main(int argc, char **argv) |
| { |
| int rc; |
| INIT_LLIST_HEAD(&bsc.sccp_connections); |
| |
| bsc.app = APP_CELLMGR; |
| bsc.dpc = 1; |
| bsc.opc = 0; |
| bsc.sccp_opc = -1; |
| bsc.isup_opc = -1; |
| bsc.udp_port = 3456; |
| bsc.udp_ip = NULL; |
| bsc.src_port = 1313; |
| bsc.ni_ni = MTP_NI_NATION_NET; |
| bsc.ni_spare = 0; |
| |
| mtp_link_set_init(); |
| thread_init(); |
| |
| log_init(&log_info); |
| stderr_target = log_target_create_stderr(); |
| log_add_target(stderr_target); |
| |
| /* enable filters */ |
| log_set_all_filter(stderr_target, 1); |
| log_set_category_filter(stderr_target, DINP, 1, LOGL_INFO); |
| log_set_category_filter(stderr_target, DSCCP, 1, LOGL_INFO); |
| log_set_category_filter(stderr_target, DMSC, 1, LOGL_INFO); |
| log_set_category_filter(stderr_target, DMGCP, 1, LOGL_INFO); |
| log_set_print_timestamp(stderr_target, 1); |
| log_set_use_color(stderr_target, 0); |
| |
| sccp_set_log_area(DSCCP); |
| |
| bsc.setup = 0; |
| bsc.msc_address = "127.0.0.1"; |
| bsc.pcap_fd = -1; |
| bsc.udp_reset_timeout = 180; |
| bsc.ping_time = 20; |
| bsc.pong_time = 5; |
| bsc.msc_time = 20; |
| |
| handle_options(argc, argv); |
| |
| signal(SIGPIPE, SIG_IGN); |
| signal(SIGINT, sigint); |
| signal(SIGUSR2, sigusr2); |
| srand(time(NULL)); |
| |
| cell_vty_init(); |
| if (vty_read_config_file(config, NULL) < 0) { |
| fprintf(stderr, "Failed to read the VTY config.\n"); |
| return -1; |
| } |
| |
| rc = telnet_init(NULL, NULL, 4242); |
| if (rc < 0) |
| return rc; |
| |
| if (link_init(&bsc) != 0) |
| return -1; |
| |
| while (1) { |
| bsc_select_main(0); |
| } |
| |
| return 0; |
| } |
| |