nat: Create a Paging Group that BSCs can refer to

Introduce a paging group that a BSC can refer to and is used
during the LAC lookup. This way paging can be flooded through
the network and just filtered at the last element in the core.
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h
index 85c9eaa..19f8efa 100644
--- a/openbsc/include/openbsc/bsc_nat.h
+++ b/openbsc/include/openbsc/bsc_nat.h
@@ -387,4 +387,11 @@
 
 struct msgb *bsc_nat_rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *, const char *imsi);
 
+/** paging group handling */
+struct bsc_nat_paging_group *bsc_nat_paging_group_num(struct bsc_nat *nat, int group);
+struct bsc_nat_paging_group *bsc_nat_paging_group_create(struct bsc_nat *nat, int group);
+void bsc_nat_paging_group_delete(struct bsc_nat_paging_group *);
+void bsc_nat_paging_group_add_lac(struct bsc_nat_paging_group *grp, int lac);
+void bsc_nat_paging_group_del_lac(struct bsc_nat_paging_group *grp, int lac);
+
 #endif
diff --git a/openbsc/include/openbsc/vty.h b/openbsc/include/openbsc/vty.h
index ded2e15..6635896 100644
--- a/openbsc/include/openbsc/vty.h
+++ b/openbsc/include/openbsc/vty.h
@@ -35,6 +35,7 @@
 	MSC_NODE,
 	OM2K_NODE,
 	TRUNK_NODE,
+	PGROUP_NODE,
 };
 
 extern int bsc_vty_is_config_node(struct vty *vty, int node);
diff --git a/openbsc/src/libcommon/common_vty.c b/openbsc/src/libcommon/common_vty.c
index f14dbca..5b4b296 100644
--- a/openbsc/src/libcommon/common_vty.c
+++ b/openbsc/src/libcommon/common_vty.c
@@ -83,6 +83,9 @@
 			vty->index = bsc_config->nat;
 		}
 		break;
+	case PGROUP_NODE:
+		vty->node = NAT_NODE;
+		break;
 	case MSC_NODE:
 		vty->node = CONFIG_NODE;
 		break;
@@ -139,6 +142,9 @@
 			vty->index = bsc_config->nat;
 		}
 		break;
+	case PGROUP_NODE:
+		vty->node = NAT_NODE;
+		break;
 	case MGCP_NODE:
 	case GBPROXY_NODE:
 	case SGSN_NODE:
@@ -189,6 +195,7 @@
 	case VTY_NODE:
 	case NAT_NODE:
 	case NAT_BSC_NODE:
+	case PGROUP_NODE:
 	case MSC_NODE:
 		vty_config_unlock(vty);
 		vty->node = ENABLE_NODE;
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
index 512656a..9ddc948 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
@@ -159,29 +159,29 @@
 	rate_ctr_group_free(cfg->stats.ctrg);
 }
 
-void bsc_config_add_lac(struct bsc_config *cfg, int _lac)
+static void _add_lac(void *ctx, struct llist_head *list, int _lac)
 {
 	struct bsc_lac_entry *lac;
 
-	llist_for_each_entry(lac, &cfg->lac_list, entry)
+	llist_for_each_entry(lac, list, entry)
 		if (lac->lac == _lac)
 			return;
 
-	lac = talloc_zero(cfg, struct bsc_lac_entry);
+	lac = talloc_zero(ctx, 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);
+	llist_add_tail(&lac->entry, list);
 }
 
-void bsc_config_del_lac(struct bsc_config *cfg, int _lac)
+static void _del_lac(struct llist_head *list, int _lac)
 {
 	struct bsc_lac_entry *lac;
 
-	llist_for_each_entry(lac, &cfg->lac_list, entry)
+	llist_for_each_entry(lac, list, entry)
 		if (lac->lac == _lac) {
 			llist_del(&lac->entry);
 			talloc_free(lac);
@@ -189,8 +189,39 @@
 		}
 }
 
-static struct bsc_nat_paging_group *bsc_nat_paging_group_num(
-					struct bsc_nat *nat, int group)
+void bsc_config_add_lac(struct bsc_config *cfg, int _lac)
+{
+	_add_lac(cfg, &cfg->lac_list, _lac);
+}
+
+void bsc_config_del_lac(struct bsc_config *cfg, int _lac)
+{
+	_del_lac(&cfg->lac_list, _lac);
+}
+
+struct bsc_nat_paging_group *bsc_nat_paging_group_create(struct bsc_nat *nat, int group)
+{
+	struct bsc_nat_paging_group *pgroup;
+
+	pgroup = talloc_zero(nat, struct bsc_nat_paging_group);
+	if (!pgroup) {
+		LOGP(DNAT, LOGL_ERROR, "Failed to allocate a paging group.\n");
+		return NULL;
+	}
+
+	pgroup->nr = group;
+	INIT_LLIST_HEAD(&pgroup->lists);
+	llist_add_tail(&pgroup->entry, &nat->paging_groups);
+	return pgroup;
+}
+
+void bsc_nat_paging_group_delete(struct bsc_nat_paging_group *pgroup)
+{
+	llist_del(&pgroup->entry);
+	talloc_free(pgroup);
+}
+
+struct bsc_nat_paging_group *bsc_nat_paging_group_num(struct bsc_nat *nat, int group)
 {
 	struct bsc_nat_paging_group *pgroup;
 
@@ -201,6 +232,16 @@
 	return NULL;
 }
 
+void bsc_nat_paging_group_add_lac(struct bsc_nat_paging_group *pgroup, int lac)
+{
+	_add_lac(pgroup, &pgroup->lists, lac);
+}
+
+void bsc_nat_paging_group_del_lac(struct bsc_nat_paging_group *pgroup, int lac)
+{
+	_del_lac(&pgroup->lists, lac);
+}
+
 int bsc_config_handles_lac(struct bsc_config *cfg, int lac_nr)
 {
 	struct bsc_nat_paging_group *pgroup;
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
index ffbfe9b..b892d7a 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
@@ -50,6 +50,17 @@
 	1,
 };
 
+static struct cmd_node pgroup_node = {
+	PGROUP_NODE,
+	"%s(paging-group)#",
+	1,
+};
+
+static int config_write_pgroup(struct vty *vty)
+{
+	return CMD_SUCCESS;
+}
+
 static void write_acc_lst(struct vty *vty, struct bsc_nat_acc_lst *lst)
 {
 	struct bsc_nat_acc_lst_entry *entry;
@@ -64,9 +75,24 @@
 	}
 }
 
+static void dump_lac(struct vty *vty, struct llist_head *head)
+{
+	struct bsc_lac_entry *lac;
+	llist_for_each_entry(lac, head, entry)
+		vty_out(vty, "  location_area_code %u%s", lac->lac, VTY_NEWLINE);
+}
+
+
+static void write_pgroup_lst(struct vty *vty, struct bsc_nat_paging_group *pgroup)
+{
+	vty_out(vty, " paging-group %d%s", pgroup->nr, VTY_NEWLINE);
+	dump_lac(vty, &pgroup->lists);
+}
+
 static int config_write_nat(struct vty *vty)
 {
 	struct bsc_nat_acc_lst *lst;
+	struct bsc_nat_paging_group *pgroup;
 
 	vty_out(vty, "nat%s", VTY_NEWLINE);
 	vty_out(vty, " msc ip %s%s", _nat->main_dest->ip, VTY_NEWLINE);
@@ -91,31 +117,27 @@
 	if (_nat->num_rewr_name)
 		vty_out(vty, " number-rewrite %s%s", _nat->num_rewr_name, VTY_NEWLINE);
 
-	llist_for_each_entry(lst, &_nat->access_lists, list) {
+	llist_for_each_entry(lst, &_nat->access_lists, list)
 		write_acc_lst(vty, lst);
-	}
+	llist_for_each_entry(pgroup, &_nat->paging_groups, entry)
+		write_pgroup_lst(vty, pgroup);
 
 	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);
-	dump_lac(vty, bsc);
+	dump_lac(vty, &bsc->lac_list);
 	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);
 	if (bsc->acc_lst_name)
 		vty_out(vty, "  access-list-name %s%s", bsc->acc_lst_name, VTY_NEWLINE);
 	vty_out(vty, "  max-endpoints %d%s", bsc->max_endpoints, VTY_NEWLINE);
+	if (bsc->paging_group != -1)
+		vty_out(vty, "  paging-group %d%s", bsc->paging_group, VTY_NEWLINE);
 }
 
 static int config_write_bsc(struct vty *vty)
@@ -523,7 +545,7 @@
 }
 
 DEFUN(cfg_bsc_lac, cfg_bsc_lac_cmd, "location_area_code <0-65535>",
-      "Set the Location Area Code (LAC) of this BSC")
+      "Add the Location Area Code (LAC) of this BSC\n" "LAC\n")
 {
 	struct bsc_config *tmp;
 	struct bsc_config *conf = vty->index;
@@ -551,7 +573,7 @@
 
 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")
+      NO_STR "Remove the Location Area Code (LAC) of this BSC\n" "LAC\n")
 {
 	int lac = atoi(argv[0]);
 	struct bsc_config *conf = vty->index;
@@ -704,6 +726,26 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_bsc_paging_grp,
+      cfg_bsc_paging_grp_cmd,
+      "paging-group <0-1000>",
+      "Use a paging group\n" "Paging Group to use\n")
+{
+	struct bsc_config *conf = vty->index;
+	conf->paging_group = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bsc_no_paging_grp,
+      cfg_bsc_no_paging_grp_cmd,
+      "no paging-group",
+      NO_STR "Disable the usage of a paging group.\n")
+{
+	struct bsc_config *conf = vty->index;
+	conf->paging_group = PAGIN_GROUP_UNASSIGNED;
+	return CMD_SUCCESS;
+}
+
 DEFUN(test_regex, test_regex_cmd,
       "test regex PATTERN STRING",
       "Check if the string is matching the current pattern.")
@@ -759,6 +801,70 @@
 	return CMD_SUCCESS;
 }
 
+/* paging group */
+DEFUN(cfg_nat_pgroup, cfg_nat_pgroup_cmd,
+      "paging-group <0-1000>",
+      "Create a Paging Group\n" "Number of the Group\n")
+{
+	int group = atoi(argv[0]);
+	struct bsc_nat_paging_group *pgroup;
+	pgroup = bsc_nat_paging_group_num(_nat, group);
+	if (!pgroup)
+		pgroup = bsc_nat_paging_group_create(_nat, group);
+	if (!pgroup) {
+		vty_out(vty, "Failed to create the group.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	vty->index = pgroup;
+	vty->node = PGROUP_NODE;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nat_no_pgroup, cfg_nat_no_pgroup_cmd,
+      "no paging-group <0-1000>",
+      NO_STR "Delete paging-group\n")
+{
+	int group = atoi(argv[0]);
+	struct bsc_nat_paging_group *pgroup;
+	pgroup = bsc_nat_paging_group_num(_nat, group);
+	if (!pgroup) {
+		vty_out(vty, "No such paging group %d.%s", group, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	bsc_nat_paging_group_delete(pgroup);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_pgroup_lac, cfg_pgroup_lac_cmd,
+      "location_area_code <0-65535>",
+       "Add the Location Area Code (LAC)\n" "LAC\n")
+{
+	struct bsc_nat_paging_group *pgroup = vty->index;
+
+	int lac = atoi(argv[0]);
+	if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) {
+		vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s",
+			lac, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	bsc_nat_paging_group_add_lac(pgroup, lac);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_pgroup_no_lac, cfg_pgroup_no_lac_cmd,
+      "no location_area_code <0-65535>",
+      NO_STR "Remove the Location Area Code (LAC)\n" "LAC\n")
+{
+	int lac = atoi(argv[0]);
+	struct bsc_nat_paging_group *pgroup = vty->index;
+
+	bsc_nat_paging_group_del_lac(pgroup, lac);
+	return CMD_SUCCESS;
+}
+
 int bsc_nat_vty_init(struct bsc_nat *nat)
 {
 	_nat = nat;
@@ -807,6 +913,13 @@
 	/* number rewriting */
 	install_element(NAT_NODE, &cfg_nat_number_rewrite_cmd);
 
+	install_element(NAT_NODE, &cfg_nat_pgroup_cmd);
+	install_element(NAT_NODE, &cfg_nat_no_pgroup_cmd);
+	install_node(&pgroup_node, config_write_pgroup);
+	install_default(PGROUP_NODE);
+	install_element(PGROUP_NODE, &cfg_pgroup_lac_cmd);
+	install_element(PGROUP_NODE, &cfg_pgroup_no_lac_cmd);
+
 	/* BSC subgroups */
 	install_element(NAT_NODE, &cfg_bsc_cmd);
 	install_node(&bsc_node, config_write_bsc);
@@ -821,6 +934,8 @@
 	install_element(NAT_BSC_NODE, &cfg_bsc_acc_lst_name_cmd);
 	install_element(NAT_BSC_NODE, &cfg_bsc_no_acc_lst_name_cmd);
 	install_element(NAT_BSC_NODE, &cfg_bsc_max_endps_cmd);
+	install_element(NAT_BSC_NODE, &cfg_bsc_paging_grp_cmd);
+	install_element(NAT_BSC_NODE, &cfg_bsc_no_paging_grp_cmd);
 
 	mgcp_vty_init();