aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/manuals/chapters/anr.adoc159
-rw-r--r--doc/manuals/osmobsc-usermanual.adoc2
-rw-r--r--include/osmocom/bsc/abis_osmo.h2
-rw-r--r--include/osmocom/bsc/bts.h5
-rw-r--r--include/osmocom/bsc/gsm_04_08_rr.h3
-rw-r--r--include/osmocom/bsc/gsm_data.h12
-rw-r--r--include/osmocom/bsc/neighbor_ident.h3
-rw-r--r--include/osmocom/bsc/pcuif_proto.h17
-rw-r--r--include/osmocom/bsc/vty.h1
-rw-r--r--src/ipaccess/ipaccess-config.c2
-rw-r--r--src/ipaccess/ipaccess-proxy.c2
-rw-r--r--src/osmo-bsc/abis_osmo.c104
-rw-r--r--src/osmo-bsc/bsc_init.c49
-rw-r--r--src/osmo-bsc/bsc_vty.c111
-rw-r--r--src/osmo-bsc/bts.c39
-rw-r--r--src/osmo-bsc/gsm_04_08_rr.c2
-rw-r--r--src/osmo-bsc/neighbor_ident_vty.c8
-rw-r--r--src/osmo-bsc/osmo_bsc_main.c3
-rw-r--r--src/utils/bs11_config.c2
-rw-r--r--src/utils/meas_json.c2
-rw-r--r--tests/abis/abis_test.c2
-rw-r--r--tests/acc/acc_test.c2
-rw-r--r--tests/nanobts_omlattr/nanobts_omlattr_test.c2
23 files changed, 529 insertions, 5 deletions
diff --git a/doc/manuals/chapters/anr.adoc b/doc/manuals/chapters/anr.adoc
new file mode 100644
index 000000000..ec86b95e8
--- /dev/null
+++ b/doc/manuals/chapters/anr.adoc
@@ -0,0 +1,159 @@
+[[anr]]
+== Automatic Neighbor Registration (ANR)
+
+Automatic Neighbor Registration (ANR from now on) is the process by which the
+BSC is able to dynamically resolve the topology of the network, that is, cells neighboring
+one another, by means of requesting and processing measurement reports from MS
+camping on the network.
+
+This feature is not described in any 3GPP TS specification, but it can be
+implemented by using standard specification features. The communication between
+BSC and PCU, though, is not standardized, and hence Osmocom specific protocol is
+used.
+
+ANR can be implemented using either (or both) CS and PS domains. In CS, MS are
+requested to provide Measurement Reports through SACCH, while in PS MS are
+requested to provide Packet measurement Reports through PDCH.
+
+=== ANR on CS domain (BTS)
+
+OsmOBSC doesn't implement this method, since the former (CS) relies on
+SACCH which is slow (throughput constrainer) and usually already constringed
+with usual usage, so using it for ANR would probably affect the operation of the
+network.
+
+=== ANR on PS domain (PCU)
+
+Under the PS domain, the BSC provides a list of target desired frequencies to
+the PCU and requests it to orchestrate the reporting of measures from MS on
+those frequencies. The provided list of desired target frequencies is basically
+the entire list of BCCH frequencies (TRX0) of all BTS under the management of
+the BSC.
+
+In order to communicate with OsmoPCU, OsmoBSC uses the PCUIF protocol. Since in
+the Osmocom architecture usually the PCU is directly attached to the BTS and hence
+there's no direct connection between the BSC and the PCU, a new transport link
+needs to be added to allow them to communicate. The implemented solution so far
+is to reuse the underlying IPA multiplex of the IPA/OML link between OsmoBSC and
+OsmoBTS to support forwarding PCUIF messages between the BSC and the PCU.
+
+OsmoBTS supporting the above mentioned protocol extension, must signal so during
+BTS Get Attr during OML link set up (see feature `BTS_FEAT_ABIS_OSMO_PCU`).
+
+When OsmoBSC wants to start the ANR procedure, and hence ask the PCU to provide
+measurement reports for the target BCCH frequencies, it does so by submitting a
+`PCU_IF_MSG_ANR_REQ` message on the above described protocol.
+
+Upon receiving that messages (forwarded from the BTS), the PCU is then
+responsible for the following tasks:
+
+Splitting the entire BCCH frequency list into smaller chunks::
+This is done for several reasons. Mainly, because an MS can only measure and
+hence provide measurement reports for up to 32 frequencies at a time.
+Furthermore, splitting the list into several MS is more fair since load is
+spread among several subscribers. Finally, spreading the load also means quicker
+results, with less noticeable operational affectation on the MS registered with
+the cell.
+
+Selecting candidate MS to issue and provide the measurement reports for a chunk of frequencies::
+The process is different whether the MS is in Idle mode or in Active
+(MAC-shared) mode. So far OsmoPCU only supports selecting and managing this
+procedure on MS in Active mode (holding an active TBF). The exact procedure is
+described bee, see <<anr_neigh_list>>.
+
+Report the measurements for each chunk of frequencies back to the BSC::
+This is done by means of sending back the recorded measurement reports obtained
+from MS in a message of type `PCU_IF_MSG_ANR_CNF`, which will then be forwarded
+by the BTS to the BSC.
+
+[[anr_neigh_list]]
+==== Gathering Measurements (TBF active mode)
+
+The purpose of this section is to describe the techinque used by the PCU to be
+able to retrieve measurements from an MS on the desired chunk of frequency list.
+
+In general, the MS can be instructed to send Packet Measurement Reports to the
+network when in PDCH by means of one of these:
+
+* Broadcasting Network Control Order NC1 or NC2 in CCCH System Information.
+* Setting Network Control Order to NC1 or NC2 when sending a _Packet Measurement
+ Order_ message to it.
+
+In general, the MS does the measurements on up to 32 BCCH frequencies from a
+list called the _Neighbour List_, which is built up locally based on the
+frequencies received in System Information 2, also called _BA(GPRS)_ or
+_BA(list)_ (they both are the same since no PBCCH is used).
+
+However, when in ANR, we want to instruct each MS to issue measurements on a
+different list of desired target frequencies, which is different from that of
+the currently announced neighbors in the cell (SI2, see above).
+The MS can be instructed to build up a different local _Neighbour List_ by using
+the `NC_FREQUENCY_LIST` bits present in _Packet Measurement Order_ sent to it.
+
+The `NC_FREQUENCY_LIST` contains an array of frequencies to remove, and an array
+of frequencies to add to the local _Neighbour List_ of the MS. Hence OsmoPCU
+builds up the `NC_FREQUENCY_LIST` with additions or removals taking into
+consideration the `BA(GPRS)` from SI2 and the target desired chunk of
+frequencies, so that when applying the bits, the MS ends up having a local
+_Neighbour List_ matching that of the chunk of frequencies we want to measure.
+
+This `NC_FREQUENCY_LIST` is sent to the MS using one or more instances of
+_Packet Measurement Order_ messages (increasing `PMO_IND`), and also setting
+`NETWORK_CONTROL_ORDER` to `NC1` in order to ask that MS to start sending
+_Packet Measurement Reports_.
+
+Once the desired _Packet Measurement Reports_ are received, OsmoPCU then sends a
+new _Packet Measurement Order_ message with an empty `NC_FREQUENCY_LIST` (which
+resets the local _Neighbour List_ to `BA(GPRS)` according to 3GPP TS 44.040 sec
+5.6.3.2), and sets `NETWORK_CONTROL_ORDER` to `RESET`.
+
+
+=== Configuring ANR
+
+ANR is mostly configured globally for the entire BSC, and it is configured to be disabled by default:
+
+.Example: Default ANR configuration
+----
+network
+ anr
+ scan-frequency 0 <1>
+ expiration-frequency 0 <2>
+ expiration-time 0
+ rxlev-threshold 0
+----
+<1> Disable (value 0) sending of ANR Requests towards PCU of all cells
+<2> Disable (value 0) expiration of dynamically found neighbors.
+
+Hence, one can see than using `scan frequency 0` alone effectively disables ANR
+for all BTS, and in that case expiration related values doesn't matter because
+no dynamic neighor is ever created. One can also quickly (1 minute delay) all
+dynamic neighbors by diabling ANR (`scan-frequency 0`), then setting
+`expiration-frequency 1` and `expiration-time 0`.
+
+On the other hand, a more typical configuration with ANR enabled could be:
+
+.Example: Default ANR configuration
+----
+network
+ anr
+ scan-frequency 30 <1>
+ expiration-frequency 15 <2>
+ expiration-time 600 <3>
+ rxlev-threshold 35 <4>
+----
+<1> An ANR request is scheduled for each cell every 30 minutes
+<2> Run the expiration timer to drop dynamic neighbors every 10 minutes
+<3> When the expiration timer runs, drop dynamic neighbors for which we didn't receive any measurement over last 10 hours (600 min).
+<4> Don't register, as neighbors, cells with Measurement Reports containing RXLEV lesser than 35 (-85dBm).
+
+Finally, ANR can also be enabled or disabled specifically for each BTS. The
+default configuration is to have it enabled by default, since anyway the default
+global config doesn't activate ANR. Hence, once the user enables ANR globally,
+the default is to enable it for all BTS under the BSC.
+
+.Example: Forbid use of ANR in a specific BTS
+----
+network
+ bts 0
+ anr disable
+----
diff --git a/doc/manuals/osmobsc-usermanual.adoc b/doc/manuals/osmobsc-usermanual.adoc
index c7589cc35..70113b7e6 100644
--- a/doc/manuals/osmobsc-usermanual.adoc
+++ b/doc/manuals/osmobsc-usermanual.adoc
@@ -34,6 +34,8 @@ include::{srcdir}/chapters/mscpool.adoc[]
include::{srcdir}/chapters/smlc.adoc[]
+include::{srcdir}/chapters/anr.adoc[]
+
include::./common/chapters/qos-dscp-pcp.adoc[]
include::./common/chapters/counters-overview.adoc[]
diff --git a/include/osmocom/bsc/abis_osmo.h b/include/osmocom/bsc/abis_osmo.h
index 97871ace0..96800859d 100644
--- a/include/osmocom/bsc/abis_osmo.h
+++ b/include/osmocom/bsc/abis_osmo.h
@@ -31,3 +31,5 @@ struct gsm_bts;
int abis_osmo_rcvmsg(struct msgb *msg);
int abis_osmo_sendmsg(struct gsm_bts *bts, struct msgb *msg);
+
+int abis_osmo_pcu_tx_anr_req(struct gsm_bts *bts, const struct gsm48_cell_desc *cell_desc_li, unsigned int num_cells);
diff --git a/include/osmocom/bsc/bts.h b/include/osmocom/bsc/bts.h
index 2e88129dd..314719797 100644
--- a/include/osmocom/bsc/bts.h
+++ b/include/osmocom/bsc/bts.h
@@ -556,6 +556,8 @@ struct gsm_bts {
/* Is Fast return to LTE allowed during Chan Release in this BTS? */
bool srvcc_fast_return_allowed;
+
+ bool anr_enabled; /* Is ANR enabled for this BTS (VTY)? */
};
#define GSM_BTS_SI2Q(bts, i) (struct gsm48_system_information_type_2quater *)((bts)->si_buf[SYSINFO_TYPE_2quater][i])
@@ -670,6 +672,9 @@ int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode);
#define BTS_STORE_UPTIME_INTERVAL 10 /* in seconds */
void bts_store_uptime(struct gsm_bts *bts);
+int bts_anr_request(struct gsm_bts *bts, const struct gsm48_cell_desc *cell_desc_li, unsigned int num_cells);
+void bts_anr_expiration(struct gsm_bts *bts, const struct timespec *expire_ts);
+
unsigned long long bts_uptime(const struct gsm_bts *bts);
char *get_model_oml_status(const struct gsm_bts *bts);
diff --git a/include/osmocom/bsc/gsm_04_08_rr.h b/include/osmocom/bsc/gsm_04_08_rr.h
index 5ddee7fc3..ebba0f862 100644
--- a/include/osmocom/bsc/gsm_04_08_rr.h
+++ b/include/osmocom/bsc/gsm_04_08_rr.h
@@ -12,6 +12,7 @@ struct gsm_lchan;
struct gsm_meas_rep;
struct gsm_network;
struct gsm_subscriber_connection;
+struct gsm_bts;
void gsm_net_update_ctype(struct gsm_network *network);
enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra);
@@ -43,6 +44,8 @@ struct msgb *gsm48_create_loc_upd_rej(uint8_t cause);
struct msgb *gsm48_create_rr_status(uint8_t cause);
int gsm48_tx_rr_status(struct gsm_subscriber_connection *conn, uint8_t cause);
+void gsm48_cell_desc(struct gsm48_cell_desc *cd, const struct gsm_bts *bts);
+
#define GSM48_ALLOC_SIZE 2048
#define GSM48_ALLOC_HEADROOM 256
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index b07345de0..fd9196770 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -1349,6 +1349,18 @@ struct gsm_network {
struct osmo_nri_ranges *null_nri_ranges;
struct smlc_config *smlc;
+
+ struct {
+ unsigned int scan_frequency; /* frequency of request_timer, in seconds. 0 = disabled */
+ unsigned int expiration_frequency; /* frequency of expiration_timer, in seconds. 0 = disabled */
+ unsigned int expiration_time; /* maximum allowed time for a dynamic neigh without detection, in seconds. 0 = disabled */
+ unsigned int rxlev_threshold; /* Minimum RXLEV an MS must provide for a neighboru to be considered valid */
+ /* Timer to submit Measure Neighbours Request from time to time. */
+ struct osmo_timer_list request_timer;
+ /* Timer to delete dynamic neighbours generated from ANR which are not recently detected. */
+ struct osmo_timer_list expiration_timer;
+
+ } anr;
};
struct gsm_audio_support {
diff --git a/include/osmocom/bsc/neighbor_ident.h b/include/osmocom/bsc/neighbor_ident.h
index 0565d528c..6f097c305 100644
--- a/include/osmocom/bsc/neighbor_ident.h
+++ b/include/osmocom/bsc/neighbor_ident.h
@@ -42,7 +42,6 @@ enum neighbor_type {
*/
struct neighbor {
struct llist_head entry;
-
enum neighbor_type type;
union {
uint8_t bts_nr;
@@ -52,6 +51,8 @@ struct neighbor {
struct cell_ab ab;
} cell_id;
};
+ bool dynamic; /* Generated dynamically by means like ANR, not from config */
+ struct timespec last_meas_detected; /* Last time it was detected by (pkt) meas report, triggered by ANR */
};
int resolve_local_neighbor(struct gsm_bts **local_neighbor_p, const struct gsm_bts *from_bts,
diff --git a/include/osmocom/bsc/pcuif_proto.h b/include/osmocom/bsc/pcuif_proto.h
index 3e6f6510e..2b62deebc 100644
--- a/include/osmocom/bsc/pcuif_proto.h
+++ b/include/osmocom/bsc/pcuif_proto.h
@@ -26,6 +26,10 @@
#define PCU_IF_MSG_TXT_IND 0x70 /* Text indication for BTS */
#define PCU_IF_MSG_CONTAINER 0x80 /* Transparent container message */
+/* msg_type coming from BSC (inside PCU_IF_MSG_CONTAINER) */
+#define PCU_IF_MSG_ANR_REQ 0x81 /* Automatic Neighbor Registration Request */
+#define PCU_IF_MSG_ANR_CNF 0x82 /* Automatic Neighbor Registration Confirmation (meas results) */
+
/* sapi */
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */
@@ -226,6 +230,19 @@ struct gsm_pcu_if_container {
uint8_t data[0];
} __attribute__ ((packed));
+/* Used inside container: */
+struct gsm_pcu_if_anr_req {
+ uint8_t num_cells;
+ uint16_t cell_list[96]; /* struct gsm48_cell_desc */
+} __attribute__ ((packed));
+
+/* PCU confirms back with measurements of target cells */
+struct gsm_pcu_if_anr_cnf {
+ uint8_t num_cells;
+ uint16_t cell_list[32]; /* struct gsm48_cell_desc */
+ uint8_t rxlev_list[32]; /* value 0xff: invalid */
+} __attribute__ ((packed));
+
struct gsm_pcu_if {
/* context based information */
uint8_t msg_type; /* message type */
diff --git a/include/osmocom/bsc/vty.h b/include/osmocom/bsc/vty.h
index 30094fa35..249b79b40 100644
--- a/include/osmocom/bsc/vty.h
+++ b/include/osmocom/bsc/vty.h
@@ -14,6 +14,7 @@ struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base);
enum bsc_vty_node {
GSMNET_NODE = _LAST_OSMOVTY_NODE + 1,
+ ANR_NODE,
BTS_NODE,
TRX_NODE,
TS_NODE,
diff --git a/src/ipaccess/ipaccess-config.c b/src/ipaccess/ipaccess-config.c
index 02501bdb5..5ffafa96d 100644
--- a/src/ipaccess/ipaccess-config.c
+++ b/src/ipaccess/ipaccess-config.c
@@ -1143,3 +1143,5 @@ int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data
int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
{ return 0; }
int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
+struct neighbor;
+char *neighbor_to_str_c(void *ctx, const struct neighbor *n) { return NULL; }
diff --git a/src/ipaccess/ipaccess-proxy.c b/src/ipaccess/ipaccess-proxy.c
index d67603833..903a94ffb 100644
--- a/src/ipaccess/ipaccess-proxy.c
+++ b/src/ipaccess/ipaccess-proxy.c
@@ -1261,3 +1261,5 @@ int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data
int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
{ return 0; }
int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
+struct neighbor;
+char *neighbor_to_str_c(void *ctx, const struct neighbor *n) { return NULL; }
diff --git a/src/osmo-bsc/abis_osmo.c b/src/osmo-bsc/abis_osmo.c
index 39caac6df..cb79c15a2 100644
--- a/src/osmo-bsc/abis_osmo.c
+++ b/src/osmo-bsc/abis_osmo.c
@@ -32,6 +32,7 @@
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/pcuif_proto.h>
+#include <osmocom/bsc/neighbor_ident.h>
#define OM_HEADROOM_SIZE 128
@@ -40,7 +41,6 @@
///////////////////////////////////////
#define PCUIF_HDR_SIZE ( sizeof(struct gsm_pcu_if) - sizeof(((struct gsm_pcu_if *)0)->u) )
-#if 0
static struct msgb *abis_osmo_pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr, size_t extra_size)
{
struct msgb *msg;
@@ -62,7 +62,100 @@ static int abis_osmo_pcu_sendmsg(struct gsm_bts *bts, struct msgb *msg)
ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_PCU);
return abis_osmo_sendmsg(bts, msg);
}
-#endif
+
+int abis_osmo_pcu_tx_anr_req(struct gsm_bts *bts, const struct gsm48_cell_desc *cell_desc_li, unsigned int num_cells)
+{
+ struct msgb *msg = abis_osmo_pcu_msgb_alloc(PCU_IF_MSG_CONTAINER, bts->bts_nr, sizeof(struct gsm_pcu_if_anr_req));
+ struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msgb_data(msg);
+ struct gsm_pcu_if_anr_req *anr_req = (struct gsm_pcu_if_anr_req *)&pcu_prim->u.container.data[0];
+
+ msgb_put(msg, sizeof(pcu_prim->u.container) + sizeof(struct gsm_pcu_if_anr_req));
+ pcu_prim->u.container.msg_type = PCU_IF_MSG_ANR_REQ;
+ osmo_store16be(sizeof(struct gsm_pcu_if_anr_req), &pcu_prim->u.container.length);
+
+ anr_req->num_cells = num_cells;
+ OSMO_ASSERT(num_cells <= ARRAY_SIZE(anr_req->cell_list));
+ if (num_cells)
+ memcpy(anr_req->cell_list, cell_desc_li, sizeof(*cell_desc_li) * num_cells);
+
+ return abis_osmo_pcu_sendmsg(bts, msg);
+}
+
+#define ANR_NEIGH_RXLEV_INVALID 0xff
+static int rcvmsg_pcu_anr_cnf(struct gsm_bts *bts, const struct gsm_pcu_if_anr_cnf* anr_cnf)
+{
+ unsigned int i;
+ struct timespec now;
+ struct gsm_bts *neigh_bts;
+ bool neigh_bts_found;
+ struct neighbor *n;
+
+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+ LOGP(DNM, LOGL_INFO, "(bts=%d) Rx ANR Confirmation (%u cells)\n",
+ bts->nr, anr_cnf->num_cells);
+
+ for (i = 0; i < anr_cnf->num_cells; i++) {
+ const struct gsm48_cell_desc *cell_desc = (const struct gsm48_cell_desc *)&anr_cnf->cell_list[i];
+ uint16_t arfcn = (cell_desc->arfcn_hi << 8) | cell_desc->arfcn_lo;
+ uint8_t bsic = (cell_desc->ncc << 3) | cell_desc->bcc;
+
+ if (anr_cnf->rxlev_list[i] == ANR_NEIGH_RXLEV_INVALID) {
+ LOGP(DNM, LOGL_INFO, "(bts=%d) ANR: ARFCN=%u BSIC=%u is NOT a neighbor (not found)\n",
+ bts->nr, arfcn, bsic);
+ continue;
+ }
+ if (anr_cnf->rxlev_list[i] < bts->network->anr.rxlev_threshold) {
+ LOGP(DNM, LOGL_INFO,
+ "(bts=%d) ANR: ARFCN=%u BSIC=%u RXLEV=%u (%d dBm) is NOT a neighbor (< rxlev %u)\n",
+ bts->nr, arfcn, bsic, anr_cnf->rxlev_list[i], anr_cnf->rxlev_list[i] - 110,
+ bts->network->anr.rxlev_threshold);
+ continue;
+ }
+ LOGP(DNM, LOGL_INFO, "(bts=%d) ANR: ARFCN=%u BSIC=%u RXLEV=%u (%d dBm) is a neighbor\n",
+ bts->nr, arfcn, bsic, anr_cnf->rxlev_list[i], anr_cnf->rxlev_list[i] - 110);
+
+ /* Find BTS owning ARFCN+BSIC: */
+ neigh_bts_found = false;
+ llist_for_each_entry(neigh_bts, &bts->network->bts_list, list) {
+ if (neigh_bts->c0->arfcn != arfcn || neigh_bts->bsic != bsic)
+ continue;
+ neigh_bts_found = true;
+ break;
+ }
+ if (!neigh_bts_found) {
+ LOGP(DNM, LOGL_NOTICE, "(bts=%d) ANR: ARFCN=%u BSIC=%u RXLEV=%u (%d dBm) matches no BTS configured in BSC!\n",
+ bts->nr, arfcn, bsic, anr_cnf->rxlev_list[i], anr_cnf->rxlev_list[i] - 110);
+ continue;
+ }
+
+ /* Try to find existing neighbour and update it. */
+ neigh_bts_found = false;
+ llist_for_each_entry(n, &bts->neighbors, entry) {
+ if (n->type != NEIGHBOR_TYPE_BTS_NR)
+ continue;
+ if (n->bts_nr != neigh_bts->nr)
+ continue;
+ neigh_bts_found = true;
+ n->last_meas_detected = now;
+ break;
+ }
+ /* If the neighbour didn't exist yet, create it and add it to the list */
+ if (!neigh_bts_found) {
+ LOGP(DNM, LOGL_NOTICE, "(bts=%d) ANR: Added new dynamic neighbor BTS%d\n",
+ bts->nr, neigh_bts->nr);
+ n = talloc(bts, struct neighbor);
+ *n = (struct neighbor){
+ .type = NEIGHBOR_TYPE_BTS_NR,
+ .bts_nr = neigh_bts->nr,
+ .dynamic = true,
+ .last_meas_detected = now,
+ };
+ llist_add_tail(&n->entry, &bts->neighbors);
+ /* TODO: force re-creation of SI? */
+ }
+ }
+ return 0;
+}
static int rcvmsg_pcu_container(struct gsm_bts *bts, struct gsm_pcu_if_container *container, size_t container_len)
{
@@ -79,6 +172,13 @@ static int rcvmsg_pcu_container(struct gsm_bts *bts, struct gsm_pcu_if_container
bts->nr, container->msg_type);
switch (container->msg_type) {
+ case PCU_IF_MSG_ANR_CNF:
+ if (data_length < sizeof(struct gsm_pcu_if_anr_cnf)) {
+ LOGP(DNM, LOGL_ERROR, "ABIS_OSMO_PCU CONTAINER ANR_CNF message too short\n");
+ return -EINVAL;
+ }
+ rc = rcvmsg_pcu_anr_cnf(bts, (struct gsm_pcu_if_anr_cnf*)&container->data);
+ break;
default:
LOGP(DNM, LOGL_NOTICE, "(bts=%d) Rx ABIS_OSMO_PCU unexpected msg type (%u) inside container!\n",
bts->nr, container->msg_type);
diff --git a/src/osmo-bsc/bsc_init.c b/src/osmo-bsc/bsc_init.c
index 24596f4eb..e4bfe0639 100644
--- a/src/osmo-bsc/bsc_init.c
+++ b/src/osmo-bsc/bsc_init.c
@@ -97,6 +97,51 @@ static void bsc_store_bts_uptime(void *data)
osmo_timer_schedule(&net->bts_store_uptime_timer, BTS_STORE_UPTIME_INTERVAL, 0);
}
+static void bsc_anr_request(void *data)
+{
+ struct gsm_network *net = data;
+ struct gsm_bts *bts;
+ struct gsm48_cell_desc cell_desc[256];
+ unsigned int num_cells = 0;
+
+ /* Build list of ARFCN+BSIC: */
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ if (num_cells == ARRAY_SIZE(cell_desc)) {
+ LOGP(DNM, LOGL_ERROR,
+ "ANR Req: Unable to build list larger than %zu elements",
+ ARRAY_SIZE(cell_desc));
+ break;
+ }
+ gsm48_cell_desc(&cell_desc[num_cells], bts);
+ num_cells++;
+ }
+
+ llist_for_each_entry(bts, &net->bts_list, list)
+ bts_anr_request(bts, cell_desc, num_cells);
+
+ /* Keep this timer ticking. */
+ osmo_timer_schedule(&net->anr.request_timer, net->anr.scan_frequency, 0);
+}
+
+static void bsc_anr_expiration(void *data)
+{
+ struct gsm_network *net = data;
+ struct gsm_bts *bts;
+ struct timespec now, res;
+ struct timespec max_age = {
+ .tv_sec = net->anr.expiration_time,
+ .tv_nsec = 0,
+ };
+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+ timespecsub(&now, &max_age, &res);
+
+ llist_for_each_entry(bts, &net->bts_list, list)
+ bts_anr_expiration(bts, &res);
+
+ /* Keep this timer ticking. */
+ osmo_timer_schedule(&net->anr.expiration_timer, net->anr.expiration_frequency, 0);
+}
+
static struct gsm_network *bsc_network_init(void *ctx)
{
struct gsm_network *net = gsm_network_init(ctx);
@@ -144,6 +189,10 @@ static struct gsm_network *bsc_network_init(void *ctx)
osmo_timer_setup(&net->bts_store_uptime_timer, bsc_store_bts_uptime, net);
osmo_timer_schedule(&net->bts_store_uptime_timer, BTS_STORE_UPTIME_INTERVAL, 0);
+ /* Init ANR tracking timer. */
+ osmo_timer_setup(&net->anr.request_timer, bsc_anr_request, net);
+ osmo_timer_setup(&net->anr.expiration_timer, bsc_anr_expiration, net);
+
net->cbc->net = net;
net->cbc->mode = BSC_CBC_LINK_MODE_DISABLED;
net->cbc->server.local_addr = bsc_cbc_default_server_local_addr;
diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c
index 070b66081..6ed31872a 100644
--- a/src/osmo-bsc/bsc_vty.c
+++ b/src/osmo-bsc/bsc_vty.c
@@ -179,6 +179,12 @@ static struct cmd_node ts_node = {
1,
};
+static struct cmd_node anr_node = {
+ ANR_NODE,
+ "%s(config-net-anr)# ",
+ 1,
+};
+
static struct gsm_network *vty_global_gsm_network = NULL;
struct gsm_network *gsmnet_from_vty(struct vty *v)
@@ -1294,6 +1300,9 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
if (!bts->srvcc_fast_return_allowed)
vty_out(vty, " srvcc fast-return forbid%s", VTY_NEWLINE);
+ vty_out(vty, " anr %s%s",
+ bts->anr_enabled ? "enable" : "disable", VTY_NEWLINE);
+
/* BS/MS Power Control parameters */
config_write_power_ctrl(vty, 2, &bts->bs_power_ctrl);
config_write_power_ctrl(vty, 2, &bts->ms_power_ctrl);
@@ -1312,6 +1321,18 @@ static int config_write_bts(struct vty *v)
return CMD_SUCCESS;
}
+static int config_write_anr(struct vty *vty)
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ vty_out(vty, " anr%s", VTY_NEWLINE);
+ vty_out(vty, " scan-frequency %u%s", gsmnet->anr.scan_frequency / 60, VTY_NEWLINE);
+ vty_out(vty, " expiration-frequency %u%s", gsmnet->anr.scan_frequency / 60, VTY_NEWLINE);
+ vty_out(vty, " expiration-time %u%s", gsmnet->anr.expiration_time / 60, VTY_NEWLINE);
+ vty_out(vty, " rxlev-threshold %u%s", gsmnet->anr.rxlev_threshold, VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
static int config_write_net(struct vty *vty)
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
@@ -2374,6 +2395,73 @@ DEFUN_ATTR(cfg_net_nri_null_del,
return CMD_SUCCESS;
}
+/* ANR configuration */
+DEFUN_ATTR(cfg_anr,
+ cfg_anr_cmd,
+ "anr",
+ "Configure ANR (Automatic Neighbor Registration) parameters\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ vty->node = ANR_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_anr_scan_frequency,
+ cfg_anr_scan_frequency_cmd,
+ "scan-frequency <0-100000>",
+ "Initiate ANR scan at given frequency\n"
+ "Time in minutes (0 = disabled, default)",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ gsmnet->anr.scan_frequency = atoi(argv[0]) * 60;
+ if (gsmnet->anr.scan_frequency)
+ osmo_timer_schedule(&gsmnet->anr.request_timer, gsmnet->anr.scan_frequency, 0);
+ else
+ osmo_timer_del(&gsmnet->anr.request_timer);
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_anr_expiration_frequency,
+ cfg_anr_expiration_frequency_cmd,
+ "expiration-frequency <0-100000>",
+ "Frequency at which to trigger expiration lookup timer\n"
+ "Frequency in minutes (0 = default)",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ gsmnet->anr.expiration_frequency = atoi(argv[0]) * 60;
+ if (gsmnet->anr.expiration_frequency)
+ osmo_timer_schedule(&gsmnet->anr.expiration_timer, gsmnet->anr.expiration_frequency, 0);
+ else
+ osmo_timer_del(&gsmnet->anr.expiration_timer);
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_anr_expiration_time,
+ cfg_anr_expiration_time_cmd,
+ "expiration-time <0-100000>",
+ "Expire dynamic neighbors not detected since a given amount of time\n"
+ "Time to expire a dynamic neighbor, in minutes (0 = disabled, default)",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ gsmnet->anr.expiration_time = atoi(argv[0]) * 60;
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_anr_rxlev_threshold,
+ cfg_anr_rxlev_threshold_cmd,
+ "rxlev-threshold <0-255>",
+ "Accept only dynamic neighbors with a minimum RXLEV provided by MS\n"
+ "RXLEV (0 = default)",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ gsmnet->anr.rxlev_threshold = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
/* per-BTS configuration */
DEFUN_ATTR(cfg_bts,
cfg_bts_cmd,
@@ -5020,6 +5108,21 @@ DEFUN_ATTR(cfg_bts_srvcc_fast_return, cfg_bts_srvcc_fast_return_cmd,
return CMD_SUCCESS;
}
+DEFUN_ATTR(cfg_bts_anr_enable_disable,
+ cfg_bts_anr_enable_disable_cmd,
+ "anr (enable|disable)",
+ "Allow or forbid ANR (Automatic Neighbor Registration) on this BTS\n"
+ "Allow ANR\n" "Forbid ANR\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ if (strcmp(argv[0], "enable") == 0)
+ bts->anr_enabled = true;
+ else
+ bts->anr_enabled = false;
+ return CMD_SUCCESS;
+}
+
#define BS_POWER_CONTROL_CMD \
"bs-power-control"
#define MS_POWER_CONTROL_CMD \
@@ -8033,6 +8136,13 @@ int bsc_vty_init(struct gsm_network *network)
install_element(GSMNET_NODE, &cfg_net_nri_null_add_cmd);
install_element(GSMNET_NODE, &cfg_net_nri_null_del_cmd);
+ install_element(GSMNET_NODE, &cfg_anr_cmd);
+ install_node(&anr_node, config_write_anr);
+ install_element(ANR_NODE, &cfg_anr_scan_frequency_cmd);
+ install_element(ANR_NODE, &cfg_anr_expiration_frequency_cmd);
+ install_element(ANR_NODE, &cfg_anr_expiration_time_cmd);
+ install_element(ANR_NODE, &cfg_anr_rxlev_threshold_cmd);
+
install_element(GSMNET_NODE, &cfg_bts_cmd);
install_node(&bts_node, config_write_bts);
install_element(BTS_NODE, &cfg_bts_type_cmd);
@@ -8169,6 +8279,7 @@ int bsc_vty_init(struct gsm_network *network)
install_element(BTS_NODE, &cfg_bts_interf_meas_avg_period_cmd);
install_element(BTS_NODE, &cfg_bts_interf_meas_level_bounds_cmd);
install_element(BTS_NODE, &cfg_bts_srvcc_fast_return_cmd);
+ install_element(BTS_NODE, &cfg_bts_anr_enable_disable_cmd);
neighbor_ident_vty_init();
/* See also handover commands added on bts level from handover_vty.c */
diff --git a/src/osmo-bsc/bts.c b/src/osmo-bsc/bts.c
index cf3a6b8cd..d320b85b5 100644
--- a/src/osmo-bsc/bts.c
+++ b/src/osmo-bsc/bts.c
@@ -24,6 +24,8 @@
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/abis_osmo.h>
+#include <osmocom/bsc/neighbor_ident.h>
const struct value_string bts_attribute_names[] = {
OSMO_VALUE_STRING(BTS_TYPE_VARIANT),
@@ -364,6 +366,8 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, struct gsm_bts_sm *bts_sm
/* SRVCC is enabled by default */
bts->srvcc_fast_return_allowed = true;
+ /* Enabled ANR by default for this BTS, since default BSC config disables it globally anyway */
+ bts->anr_enabled = true;
return bts;
}
@@ -583,6 +587,41 @@ unsigned long long bts_uptime(const struct gsm_bts *bts)
return difftime(tp.tv_sec, bts->uptime);
}
+int bts_anr_request(struct gsm_bts *bts, const struct gsm48_cell_desc *cell_desc_li, unsigned int num_cells)
+{
+ if (!bts->anr_enabled) {
+ LOGP(DNM, LOGL_DEBUG, "BTS%u: ANR Req: ANR disabled by config\n", bts->nr);
+ return 0;
+ }
+ if (!bts->osmo_link) {
+ LOGP(DNM, LOGL_INFO, "BTS%u: ANR Req: OSMO link is down\n", bts->nr);
+ return 0;
+ }
+ if (!osmo_bts_has_feature(&bts->features, BTS_FEAT_ABIS_OSMO_PCU)) {
+ LOGP(DNM, LOGL_INFO, "BTS%u: ANR Req: Abis OSMO_PCU proto not supported\n", bts->nr);
+ return 0;
+ }
+ LOGP(DNM, LOGL_INFO, "BTS%u: ANR Req (%u cells)\n", bts->nr, num_cells);
+ return abis_osmo_pcu_tx_anr_req(bts, cell_desc_li, num_cells);
+}
+
+void bts_anr_expiration(struct gsm_bts *bts, const struct timespec *expire_ts)
+{
+ struct neighbor *n, *n2;
+ llist_for_each_entry_safe(n, n2, &bts->neighbors, entry) {
+ if (!n->dynamic)
+ continue;
+ if (timespeccmp(&n->last_meas_detected, expire_ts, >=))
+ continue;
+ LOGP(DNM, LOGL_INFO, "BTS%u: ANR Expire: Remove dynamic neighbor %s\n",
+ bts->nr, neighbor_to_str_c(OTC_SELECT, n));
+ llist_del(&n->entry);
+ talloc_free(n);
+ /* TODO: force SI list update ?*/
+ break;
+ }
+}
+
char *get_model_oml_status(const struct gsm_bts *bts)
{
if (bts->model->oml_status)
diff --git a/src/osmo-bsc/gsm_04_08_rr.c b/src/osmo-bsc/gsm_04_08_rr.c
index 16fe99930..9f9f709dd 100644
--- a/src/osmo-bsc/gsm_04_08_rr.c
+++ b/src/osmo-bsc/gsm_04_08_rr.c
@@ -387,7 +387,7 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv)
return rsl_encryption_cmd(msg);
}
-static void gsm48_cell_desc(struct gsm48_cell_desc *cd,
+void gsm48_cell_desc(struct gsm48_cell_desc *cd,
const struct gsm_bts *bts)
{
cd->ncc = (bts->bsic >> 3 & 0x7);
diff --git a/src/osmo-bsc/neighbor_ident_vty.c b/src/osmo-bsc/neighbor_ident_vty.c
index b9160ec67..dc783a527 100644
--- a/src/osmo-bsc/neighbor_ident_vty.c
+++ b/src/osmo-bsc/neighbor_ident_vty.c
@@ -534,6 +534,11 @@ void neighbor_ident_vty_write_bts(struct vty *vty, const char *indent, struct gs
struct neighbor *n;
llist_for_each_entry(n, &bts->neighbors, entry) {
+
+ /* Don't store dynamically found neighbours */
+ if (n->dynamic)
+ continue;
+
switch (n->type) {
case NEIGHBOR_TYPE_BTS_NR:
vty_out(vty, "%sneighbor bts %u%s", indent, n->bts_nr, VTY_NEWLINE);
@@ -601,7 +606,8 @@ DEFUN(show_bts_neighbor, show_bts_neighbor_cmd,
vty_out(vty, " local BTS %u lac-ci %u %u%s",
local_neighbor->nr,
local_neighbor->location_area_code,
- local_neighbor->cell_identity, VTY_NEWLINE);
+ local_neighbor->cell_identity,
+ VTY_NEWLINE);
}
if (remote_neighbors.id_list_len) {
diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c
index 2a6c50183..5514229cf 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -633,6 +633,9 @@ static int bsc_vty_go_parent(struct vty *vty)
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
+ case ANR_NODE:
+ vty->node = GSMNET_NODE;
+ break;
default:
osmo_ss7_vty_go_parent(vty);
}
diff --git a/src/utils/bs11_config.c b/src/utils/bs11_config.c
index c279179d5..50e2551f7 100644
--- a/src/utils/bs11_config.c
+++ b/src/utils/bs11_config.c
@@ -995,3 +995,5 @@ int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data
int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
{ return 0; }
int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
+struct neighbor;
+char *neighbor_to_str_c(void *ctx, const struct neighbor *n) { return NULL; }
diff --git a/src/utils/meas_json.c b/src/utils/meas_json.c
index 82b1e570b..50a1928ef 100644
--- a/src/utils/meas_json.c
+++ b/src/utils/meas_json.c
@@ -210,3 +210,5 @@ int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data
int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
{ return 0; }
int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
+struct neighbor;
+char *neighbor_to_str_c(void *ctx, const struct neighbor *n) { return NULL; }
diff --git a/tests/abis/abis_test.c b/tests/abis/abis_test.c
index 9d26eddcd..a027551dc 100644
--- a/tests/abis/abis_test.c
+++ b/tests/abis/abis_test.c
@@ -196,3 +196,5 @@ int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data
int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
{ return 0; }
int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
+struct neighbor;
+char *neighbor_to_str_c(void *ctx, const struct neighbor *n) { return NULL; }
diff --git a/tests/acc/acc_test.c b/tests/acc/acc_test.c
index 759ff139f..5e8b5726d 100644
--- a/tests/acc/acc_test.c
+++ b/tests/acc/acc_test.c
@@ -557,3 +557,5 @@ int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data
int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
{ return 0; }
int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
+struct neighbor;
+char *neighbor_to_str_c(void *ctx, const struct neighbor *n) { return NULL; }
diff --git a/tests/nanobts_omlattr/nanobts_omlattr_test.c b/tests/nanobts_omlattr/nanobts_omlattr_test.c
index 73ba86950..069393a3e 100644
--- a/tests/nanobts_omlattr/nanobts_omlattr_test.c
+++ b/tests/nanobts_omlattr/nanobts_omlattr_test.c
@@ -345,3 +345,5 @@ int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data
int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
{ return 0; }
int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
+struct neighbor;
+char *neighbor_to_str_c(void *ctx, const struct neighbor *n) { return NULL; }