keep some internal statistics inside OpenBSC

the statistics will give us some idea about the network load and
performance.
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 9d2549c..74e1938 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -457,6 +457,49 @@
 	struct llist_head trx_list;
 };
 
+/* Some statistics of our network */
+struct gsmnet_stats {
+	struct {
+		unsigned long total;
+		unsigned long no_channel;
+	} chreq;
+	struct {
+		unsigned long attempted;
+		unsigned long no_channel;	/* no channel available */
+		unsigned long timeout;		/* T3103 timeout */
+		unsigned long completed;	/* HO COMPL received */
+		unsigned long failed;		/* HO FAIL received */
+	} handover;
+	struct {
+		unsigned long attach;
+		unsigned long normal;
+		unsigned long periodic;
+		unsigned long detach;
+	} loc_upd_type;
+	struct {
+		unsigned long reject;
+		unsigned long accept;
+	} loc_upd_resp;
+	struct {
+		unsigned long attempted;
+		unsigned long detached;
+		unsigned long completed;
+		unsigned long expired;
+	} paging;
+	struct {
+		unsigned long submitted; /* MO SMS submissions */
+		unsigned long no_receiver;
+		unsigned long delivered; /* MT SMS deliveries */
+		unsigned long rp_err_mem;
+		unsigned long rp_err_other;
+	} sms;
+	struct {
+		unsigned long dialled;	/* total number of dialled calls */
+		unsigned long alerted;	/* we alerted the other end */
+		unsigned long connected;/* how many calls were accepted */
+	} call;
+};
+
 enum gsm_auth_policy {
 	GSM_AUTH_POLICY_CLOSED, /* only subscribers authorized in DB */
 	GSM_AUTH_POLICY_ACCEPT_ALL, /* accept everyone, even if not authorized in DB */
@@ -494,6 +537,8 @@
 		unsigned int max_distance;	/* TA values */
 	} handover;
 
+	struct gsmnet_stats stats;
+
 	/* layer 4 */
 	int (*mncc_recv) (struct gsm_network *net, int msg_type, void *arg);
 	struct llist_head upqueue;
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 72ae9db..ae1d6af 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -1261,11 +1261,14 @@
 	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++;
+
 	/* 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++;
 		/* FIXME: send some kind of reject ?!? */
 		return -ENOMEM;
 	}
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 156927f..2b9dcc2 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -886,6 +886,7 @@
 /* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
 int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
 {
+	struct gsm_bts *bts = lchan->ts->trx->bts;
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh;
 	
@@ -897,6 +898,8 @@
 	gh->data[0] = cause;
 
 	DEBUGP(DMM, "-> LOCATION UPDATING REJECT on channel: %d\n", lchan->nr);
+
+	bts->network->stats.loc_upd_resp.reject++;
 	
 	return gsm48_sendmsg(msg, NULL);
 }
@@ -925,6 +928,8 @@
 
 	DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
 
+	bts->network->stats.loc_upd_resp.accept++;
+
 	return gsm48_sendmsg(msg, NULL);
 }
 
@@ -1043,6 +1048,18 @@
 
 	dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len);
 
+	switch (lu->type) {
+	case GSM48_LUPD_NORMAL:
+		bts->network->stats.loc_upd_type.normal++;
+		break;
+	case GSM48_LUPD_IMSI_ATT:
+		bts->network->stats.loc_upd_type.attach++;
+		break;
+	case GSM48_LUPD_PERIODIC:
+		bts->network->stats.loc_upd_type.periodic++;
+		break;
+	}
+
 	/*
 	 * Pseudo Spoof detection: Just drop a second/concurrent
 	 * location updating request.
@@ -1369,6 +1386,8 @@
 	DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ",
 		mi_type, mi_string);
 
+	bts->network->stats.loc_upd_type.detach++;
+
 	switch (mi_type) {
 	case GSM_MI_TYPE_TMSI:
 		subscr = subscr_get_by_tmsi(bts->network,
diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c
index e96a1ca..2cd571e 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -488,6 +488,8 @@
 	sig_data.bts	= msg->lchan->ts->trx->bts;
 	sig_data.lchan	= msg->lchan;
 
+	bts->network->stats.paging.completed++;
+
 	dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
 
 	/* Stop paging on the bts we received the paging response */
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 17a5831..579bb55 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -517,6 +517,8 @@
 	u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
 	int rc = 0;
 
+	bts->network->stats.sms.submitted++;
+
 	gsms = sms_alloc();
 	if (!gsms)
 		return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
@@ -605,6 +607,7 @@
 	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++;
 		goto out;
 	}
 
@@ -791,7 +794,9 @@
 		 * 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++;
+	} else
+		trans->lchan->ts->trx->bts->network->stats.sms.rp_err_other++;
 
 	sms_free(sms);
 	trans->sms.sms = NULL;
@@ -1064,6 +1069,8 @@
 
 	DEBUGP(DSMS, "TX: SMS DELIVER\n");
 
+	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/handover_logic.c b/openbsc/src/handover_logic.c
index 94d3d0d..393b0f2 100644
--- a/openbsc/src/handover_logic.c
+++ b/openbsc/src/handover_logic.c
@@ -97,9 +97,12 @@
 	DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n",
 		old_lchan->ts->trx->bts->nr, bts->nr);
 
+	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++;
 		return -ENOSPC;
 	}
 
@@ -143,6 +146,7 @@
 	struct bsc_handover *ho = _ho;
 
 	DEBUGP(DHO, "HO T3103 expired\n");
+	ho->new_lchan->ts->trx->bts->network->stats.handover.timeout++;
 
 	lchan_free(ho->new_lchan);
 	llist_del(&ho->list);
@@ -211,6 +215,8 @@
 		return -ENODEV;
 	}
 
+	new_lchan->ts->trx->bts->network->stats.handover.completed++;
+
 	bsc_del_timer(&ho->T3103);
 
 	/* update lchan pointer of transaction */
@@ -238,6 +244,8 @@
 		return -ENODEV;
 	}
 
+	old_lchan->ts->trx->bts->network->stats.handover.failed++;
+
 	bsc_del_timer(&ho->T3103);
 	llist_del(&ho->list);
 	put_lchan(ho->new_lchan);
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index b273419..538e0a8 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -212,6 +212,8 @@
 	cbfn = req->cbfn;
 	paging_remove_request(&req->bts->paging, req);
 
+	req->bts->network->stats.paging.expired++;
+
 	dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
 	if (cbfn)
 		cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
@@ -254,6 +256,8 @@
 	struct gsm_bts *bts = NULL;
 	int num_pages = 0;
 
+	network->stats.paging.attempted++;
+
 	/* start paging subscriber on all BTS within Location Area */
 	do {
 		int rc;
@@ -269,6 +273,9 @@
 			return rc;
 	} while (1);
 
+	if (num_pages == 0)
+		network->stats.paging.detached++;
+
 	return num_pages;
 }
 
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index b13dc5f..fa9b45d 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -761,6 +761,41 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(show_stats,
+      show_stats_cmd,
+      "show statistics",
+	SHOW_STR "Display network statistics\n")
+{
+	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, "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);
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_net,
       cfg_net_cmd,
       "network",
@@ -1435,6 +1470,7 @@
 	install_element(VIEW_NODE, &show_e1ts_cmd);
 
 	install_element(VIEW_NODE, &show_paging_cmd);
+	install_element(VIEW_NODE, &show_stats_cmd);
 
 	install_element(CONFIG_NODE, &cfg_net_cmd);
 	install_node(&net_node, config_write_net);