nat: Allow a BSC to have multiple LACs

Make it possible that one BSC is serving multiple
cells. Introduce a list of lacs, add functions to
manipulate the lists. The current test cases for
paging by lac continue to work.
diff --git a/openbsc/src/nat/bsc_nat.c b/openbsc/src/nat/bsc_nat.c
index 48407b9..f5eca80 100644
--- a/openbsc/src/nat/bsc_nat.c
+++ b/openbsc/src/nat/bsc_nat.c
@@ -753,8 +753,8 @@
 			bsc->authenticated = 1;
 			bsc->cfg = conf;
 			bsc_del_timer(&bsc->id_timeout);
-			LOGP(DNAT, LOGL_NOTICE, "Authenticated bsc nr: %d lac: %d on fd %d\n",
-			     conf->nr, conf->lac, bsc->write_queue.bfd.fd);
+			LOGP(DNAT, LOGL_NOTICE, "Authenticated bsc nr: %d on fd %d\n",
+			     conf->nr, bsc->write_queue.bfd.fd);
 			start_ping_pong(bsc);
 			return;
 		}
diff --git a/openbsc/src/nat/bsc_nat_utils.c b/openbsc/src/nat/bsc_nat_utils.c
index 44443d2..e11b48c 100644
--- a/openbsc/src/nat/bsc_nat_utils.c
+++ b/openbsc/src/nat/bsc_nat_utils.c
@@ -116,21 +116,22 @@
 	return con;
 }
 
-struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token, unsigned int lac)
+struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token)
 {
 	struct bsc_config *conf = talloc_zero(nat, struct bsc_config);
 	if (!conf)
 		return NULL;
 
 	conf->token = talloc_strdup(conf, token);
-	conf->lac = lac;
 	conf->nr = nat->num_bsc;
 	conf->nat = nat;
 
+	INIT_LLIST_HEAD(&conf->lac_list);
+
 	llist_add_tail(&conf->entry, &nat->bsc_configs);
 	++nat->num_bsc;
 
-	conf->stats.ctrg = rate_ctr_group_alloc(conf, &bsc_cfg_ctrg_desc, conf->lac);
+	conf->stats.ctrg = rate_ctr_group_alloc(conf, &bsc_cfg_ctrg_desc, conf->nr);
 	if (!conf->stats.ctrg) {
 		talloc_free(conf);
 		return NULL;
@@ -139,6 +140,47 @@
 	return conf;
 }
 
+void bsc_config_add_lac(struct bsc_config *cfg, int _lac)
+{
+	struct bsc_lac_entry *lac;
+
+	llist_for_each_entry(lac, &cfg->lac_list, entry)
+		if (lac->lac == _lac)
+			return;
+
+	lac = talloc_zero(cfg, struct bsc_lac_entry);
+	if (!lac) {
+		LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n");
+		return;
+	}
+
+	lac->lac = _lac;
+	llist_add_tail(&lac->entry, &cfg->lac_list);
+}
+
+void bsc_config_del_lac(struct bsc_config *cfg, int _lac)
+{
+	struct bsc_lac_entry *lac;
+
+	llist_for_each_entry(lac, &cfg->lac_list, entry)
+		if (lac->lac == _lac) {
+			llist_del(&lac->entry);
+			talloc_free(lac);
+			return;
+		}
+}
+
+int bsc_config_handles_lac(struct bsc_config *cfg, int lac_nr)
+{
+	struct bsc_lac_entry *entry;
+
+	llist_for_each_entry(entry, &cfg->lac_list, entry)
+		if (entry->lac == lac_nr)
+			return 1;
+
+	return 0;
+}
+
 void sccp_connection_destroy(struct sccp_connections *conn)
 {
 	LOGP(DNAT, LOGL_DEBUG, "Destroy 0x%x <-> 0x%x mapping for con %p\n",
@@ -188,7 +230,9 @@
 		llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) {
 			if (!bsc->cfg)
 				continue;
-			if (!bsc->authenticated || _lac != bsc->cfg->lac)
+			if (!bsc->authenticated)
+				continue;
+			if (!bsc_config_handles_lac(bsc->cfg, _lac))
 				continue;
 
 			return bsc;
diff --git a/openbsc/src/nat/bsc_nat_vty.c b/openbsc/src/nat/bsc_nat_vty.c
index 0da686d..1dbc755 100644
--- a/openbsc/src/nat/bsc_nat_vty.c
+++ b/openbsc/src/nat/bsc_nat_vty.c
@@ -86,11 +86,18 @@
 	return CMD_SUCCESS;
 }
 
+static void dump_lac(struct vty *vty, struct bsc_config *cfg)
+{
+	struct bsc_lac_entry *lac;
+	llist_for_each_entry(lac, &cfg->lac_list, entry)
+		vty_out(vty, "  location_area_code %u%s", lac->lac, VTY_NEWLINE);
+}
+
 static void config_write_bsc_single(struct vty *vty, struct bsc_config *bsc)
 {
 	vty_out(vty, " bsc %u%s", bsc->nr, VTY_NEWLINE);
 	vty_out(vty, "  token %s%s", bsc->token, VTY_NEWLINE);
-	vty_out(vty, "  location_area_code %u%s", bsc->lac, VTY_NEWLINE);
+	dump_lac(vty, bsc);
 	vty_out(vty, "  paging forbidden %d%s", bsc->forbid_paging, VTY_NEWLINE);
 	if (bsc->description)
 		vty_out(vty, "  description %s%s", bsc->description, VTY_NEWLINE);
@@ -115,9 +122,8 @@
 	vty_out(vty, "Listing all open SCCP connections%s", VTY_NEWLINE);
 
 	llist_for_each_entry(con, &_nat->sccp_connections, list_entry) {
-		vty_out(vty, "For BSC Nr: %d lac: %d; BSC ref: 0x%x; MUX ref: 0x%x; Network has ref: %d ref: 0x%x MSC/BSC mux: 0x%x/0x%x type: %s%s",
+		vty_out(vty, "For BSC Nr: %d BSC ref: 0x%x; MUX ref: 0x%x; Network has ref: %d ref: 0x%x MSC/BSC mux: 0x%x/0x%x type: %s%s",
 			con->bsc->cfg ? con->bsc->cfg->nr : -1,
-			con->bsc->cfg ? con->bsc->cfg->lac : -1,
 			sccp_src_ref_to_int(&con->real_ref),
 			sccp_src_ref_to_int(&con->patched_ref),
 			con->has_remote_ref,
@@ -139,9 +145,8 @@
 
 	llist_for_each_entry(con, &_nat->bsc_connections, list_entry) {
 		getpeername(con->write_queue.bfd.fd, (struct sockaddr *) &sock, &len);
-		vty_out(vty, "BSC nr: %d lac: %d auth: %d fd: %d peername: %s%s",
+		vty_out(vty, "BSC nr: %d auth: %d fd: %d peername: %s%s",
 			con->cfg ? con->cfg->nr : -1,
-			con->cfg ? con->cfg->lac : -1,
 			con->authenticated, con->write_queue.bfd.fd,
 			inet_ntoa(sock.sin_addr), VTY_NEWLINE);
 	}
@@ -178,8 +183,8 @@
 {
 	struct bsc_config *conf;
 	llist_for_each_entry(conf, &_nat->bsc_configs, entry) {
-		vty_out(vty, "BSC token: '%s' lac: %u nr: %u%s",
-			conf->token, conf->lac, conf->nr, VTY_NEWLINE);
+		vty_out(vty, "BSC token: '%s' nr: %u%s",
+			conf->token, conf->nr, VTY_NEWLINE);
 		if (conf->acc_lst_name)
 			vty_out(vty, " access-list: %s%s",
 				conf->acc_lst_name, VTY_NEWLINE);
@@ -215,8 +220,8 @@
 	int connected = 0;
 	struct bsc_connection *con;
 
-	vty_out(vty, " BSC lac: %d nr: %d%s",
-		conf->lac, conf->nr, VTY_NEWLINE);
+	vty_out(vty, " BSC nr: %d%s",
+		conf->nr, VTY_NEWLINE);
 	vty_out_rate_ctr_group(vty, " ", conf->stats.ctrg);
 
 	llist_for_each_entry(con, &conf->nat->bsc_connections, list_entry) {
@@ -264,7 +269,7 @@
 
 	dump_stat_total(vty, _nat);
 	llist_for_each_entry(conf, &_nat->bsc_configs, entry) {
-		if (conf->lac != lac)
+		if (!bsc_config_handles_lac(conf, lac))
 			continue;
 		dump_stat_bsc(vty, conf);
 	}
@@ -406,7 +411,7 @@
 		return CMD_WARNING;
 	} else if (bsc_nr == _nat->num_bsc) {
 		/* allocate a new one */
-		bsc = bsc_config_alloc(_nat, "unknown", 0);
+		bsc = bsc_config_alloc(_nat, "unknown");
 	} else
 		bsc = bsc_config_num(_nat, bsc_nr);
 
@@ -445,17 +450,30 @@
 
 	/* verify that the LACs are unique */
 	llist_for_each_entry(tmp, &_nat->bsc_configs, entry) {
-		if (tmp->lac == lac) {
+		if (bsc_config_handles_lac(tmp, lac)) {
 			vty_out(vty, "%% LAC %d is already used.%s", lac, VTY_NEWLINE);
 			return CMD_ERR_INCOMPLETE;
 		}
 	}
 
-	conf->lac = lac;
+	bsc_config_add_lac(conf, lac);
 
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_bsc_no_lac, cfg_bsc_no_lac_cmd,
+      "no location_area_code <0-65535>",
+      NO_STR "Set the Location Area Code (LAC) of this BSC")
+{
+	int lac = atoi(argv[0]);
+	struct bsc_config *conf = vty->index;
+
+	bsc_config_del_lac(conf, lac);
+	return CMD_SUCCESS;
+}
+
+
+
 DEFUN(cfg_lst_imsi_allow,
       cfg_lst_imsi_allow_cmd,
       "access-list NAME imsi-allow [REGEXP]",
@@ -638,6 +656,7 @@
 	install_element(NAT_BSC_NODE, &ournode_end_cmd);
 	install_element(NAT_BSC_NODE, &cfg_bsc_token_cmd);
 	install_element(NAT_BSC_NODE, &cfg_bsc_lac_cmd);
+	install_element(NAT_BSC_NODE, &cfg_bsc_no_lac_cmd);
 	install_element(NAT_BSC_NODE, &cfg_bsc_paging_cmd);
 	install_element(NAT_BSC_NODE, &cfg_bsc_desc_cmd);
 	install_element(NAT_BSC_NODE, &cfg_bsc_acc_lst_name_cmd);