nat: Implement a post-routing for the NAT software

* The post-routing is applied after the first re-writing. To do this
  the new number is copied back into the called data structure.

* Add a testcase that goes from 0172 to 0049 and then back to 0049
  using the post rule with a table lookup.
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h
index f93b596..6ebe045 100644
--- a/openbsc/include/openbsc/bsc_nat.h
+++ b/openbsc/include/openbsc/bsc_nat.h
@@ -299,6 +299,8 @@
 	/* number rewriting */
 	char *num_rewr_name;
 	struct llist_head num_rewr;
+	char *num_rewr_post_name;
+	struct llist_head num_rewr_post;
 
 	char *smsc_rewr_name;
 	struct llist_head smsc_rewr;
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c b/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c
index 5984d96..3bc602e 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c
@@ -93,8 +93,9 @@
 	return new_number;
 }
 
-static char *rewrite_isdn_number(struct bsc_nat *nat, void *ctx, const char *imsi,
-				       struct gsm_mncc_number *called)
+static char *rewrite_isdn_number(struct bsc_nat *nat, struct llist_head *rewr_list,
+				void *ctx, const char *imsi,
+				struct gsm_mncc_number *called)
 {
 	char int_number[sizeof(called->number) + 2];
 	char *number = called->number;
@@ -114,9 +115,22 @@
 	}
 
 	return match_and_rewrite_number(ctx, number,
-					imsi, &nat->num_rewr, nat->num_rewr_trie);
+					imsi, rewr_list, nat->num_rewr_trie);
 }
 
+static void update_called_number(struct gsm_mncc_number *called,
+				const char *chosen_number)
+{
+	if (strncmp(chosen_number, "00", 2) == 0) {
+		called->type = 1;
+		strncpy(called->number, chosen_number + 2, sizeof(called->number));
+	} else {
+		/* rewrite international to unknown */
+		if (called->type == 1)
+			called->type = 0;
+		strncpy(called->number, chosen_number, sizeof(called->number));
+	}
+}
 
 /**
  * Rewrite non global numbers... according to rules based on the IMSI
@@ -129,7 +143,7 @@
 	unsigned int payload_len;
 	struct gsm_mncc_number called;
 	struct msgb *out;
-	char *new_number = NULL;
+	char *new_number_pre = NULL, *new_number_post = NULL, *chosen_number;
 	uint8_t *outptr;
 	const uint8_t *msgptr;
 	int sec_len;
@@ -147,16 +161,30 @@
 			    TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1);
 
 	/* check if it looks international and stop */
-	new_number = rewrite_isdn_number(nat, msg, imsi, &called);
+	new_number_pre = rewrite_isdn_number(nat, &nat->num_rewr, msg, imsi, &called);
 
-	if (!new_number) {
+	if (!new_number_pre) {
 		LOGP(DNAT, LOGL_DEBUG, "No IMSI match found, returning message.\n");
 		return NULL;
 	}
 
-	if (strlen(new_number) > sizeof(called.number)) {
+	if (strlen(new_number_pre) > sizeof(called.number)) {
 		LOGP(DNAT, LOGL_ERROR, "Number is too long for structure.\n");
-		talloc_free(new_number);
+		talloc_free(new_number_pre);
+		return NULL;
+	}
+	update_called_number(&called, new_number_pre);
+
+	/* another run through the re-write engine with other rules */
+	new_number_post = rewrite_isdn_number(nat, &nat->num_rewr_post, msg,
+					imsi, &called);
+	chosen_number = new_number_post ? new_number_post : new_number_pre;
+
+
+	if (strlen(chosen_number) > sizeof(called.number)) {
+		LOGP(DNAT, LOGL_ERROR, "Number is too long for structure.\n");
+		talloc_free(new_number_pre);
+		talloc_free(new_number_post);
 		return NULL;
 	}
 
@@ -169,7 +197,8 @@
 	out = msgb_alloc_headroom(4096, 128, "changed-setup");
 	if (!out) {
 		LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n");
-		talloc_free(new_number);
+		talloc_free(new_number_pre);
+		talloc_free(new_number_post);
 		return NULL;
 	}
 
@@ -183,15 +212,7 @@
 	memcpy(outptr, &hdr48->data[0], sec_len);
 
 	/* create the new number */
-	if (strncmp(new_number, "00", 2) == 0) {
-		called.type = 1;
-		strncpy(called.number, new_number + 2, sizeof(called.number));
-	} else {
-		/* rewrite international to unknown */
-		if (called.type == 1)
-			called.type = 0;
-		strncpy(called.number, new_number, sizeof(called.number));
-	}
+	update_called_number(&called, chosen_number);
 	gsm48_encode_called(out, &called);
 
 	/* copy thre rest */
@@ -201,7 +222,8 @@
 	outptr = msgb_put(out, sec_len);
 	memcpy(outptr, msgptr, sec_len);
 
-	talloc_free(new_number);
+	talloc_free(new_number_pre);
+	talloc_free(new_number_post);
 	return out;
 }
 
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
index 45c224c..bc8c4c1 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
@@ -96,6 +96,7 @@
 	INIT_LLIST_HEAD(&nat->access_lists);
 	INIT_LLIST_HEAD(&nat->dests);
 	INIT_LLIST_HEAD(&nat->num_rewr);
+	INIT_LLIST_HEAD(&nat->num_rewr_post);
 	INIT_LLIST_HEAD(&nat->smsc_rewr);
 	INIT_LLIST_HEAD(&nat->tpdest_match);
 	INIT_LLIST_HEAD(&nat->sms_clear_tp_srr);
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
index 5404bfb..0cac794 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
@@ -127,6 +127,10 @@
 
 	if (_nat->num_rewr_name)
 		vty_out(vty, " number-rewrite %s%s", _nat->num_rewr_name, VTY_NEWLINE);
+	if (_nat->num_rewr_post_name)
+		vty_out(vty, " number-rewrite-post %s%s",
+			_nat->num_rewr_post_name, VTY_NEWLINE);
+
 	if (_nat->smsc_rewr_name)
 		vty_out(vty, " rewrite-smsc addr %s%s",
 			_nat->smsc_rewr_name, VTY_NEWLINE);
@@ -570,6 +574,27 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_nat_number_rewrite_post,
+      cfg_nat_number_rewrite_post_cmd,
+      "number-rewrite-post FILENAME",
+      "Set the file with post-routing rewriting rules.\n" "Filename")
+{
+	return replace_rules(_nat, &_nat->num_rewr_post_name,
+			     &_nat->num_rewr_post, argv[0]);
+}
+
+DEFUN(cfg_nat_no_number_rewrite_post,
+      cfg_nat_no_number_rewrite_post_cmd,
+      "no number-rewrite-post",
+      NO_STR "Set the file with post-routing rewriting rules.\n")
+{
+	talloc_free(_nat->num_rewr_post_name);
+	_nat->num_rewr_post_name = NULL;
+
+	bsc_nat_num_rewr_entry_adapt(NULL, &_nat->num_rewr_post, NULL);
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_nat_smsc_addr,
       cfg_nat_smsc_addr_cmd,
       "rewrite-smsc addr FILENAME",
@@ -1183,6 +1208,8 @@
 	/* number rewriting */
 	install_element(NAT_NODE, &cfg_nat_number_rewrite_cmd);
 	install_element(NAT_NODE, &cfg_nat_no_number_rewrite_cmd);
+	install_element(NAT_NODE, &cfg_nat_number_rewrite_post_cmd);
+	install_element(NAT_NODE, &cfg_nat_no_number_rewrite_post_cmd);
 	install_element(NAT_NODE, &cfg_nat_smsc_addr_cmd);
 	install_element(NAT_NODE, &cfg_nat_smsc_tpdest_cmd);
 	install_element(NAT_NODE, &cfg_nat_sms_clear_tpsrr_cmd);
diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.c b/openbsc/tests/bsc-nat/bsc_nat_test.c
index 2b30413..5158f46 100644
--- a/openbsc/tests/bsc-nat/bsc_nat_test.c
+++ b/openbsc/tests/bsc-nat/bsc_nat_test.c
@@ -1096,6 +1096,66 @@
 	talloc_free(nat);
 }
 
+static void test_setup_rewrite_post(void)
+{
+	struct msgb *msg = msgb_alloc(4096, "test_dt_filter");
+	struct msgb *out;
+	struct bsc_nat_parsed *parsed;
+	const char *imsi = "27408000001234";
+
+	struct bsc_nat *nat = bsc_nat_alloc();
+
+	/* a fake list */
+	struct osmo_config_list entries;
+	struct osmo_config_entry entry;
+	struct osmo_config_list entries_post;
+	struct osmo_config_entry entry_post;
+
+	INIT_LLIST_HEAD(&entries.entry);
+	entry.mcc = "274";
+	entry.mnc = "08";
+	entry.option = "^0([1-9])";
+	entry.text = "0049";
+	llist_add_tail(&entry.list, &entries.entry);
+	bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries);
+
+	/* attempt to undo the previous one */
+	INIT_LLIST_HEAD(&entries_post.entry);
+	entry_post.mcc = "274";
+	entry_post.mnc = "08";
+	entry_post.option = "^\\+49([1-9])";
+	entry_post.text = "prefix_lookup";
+	llist_add_tail(&entry_post.list, &entries_post.entry);
+	bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr_post, &entries_post);
+
+        nat->num_rewr_trie = nat_rewrite_parse(nat, "prefixes.csv");
+
+	msgb_reset(msg);
+	copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national));
+	parsed = bsc_nat_parse(msg);
+	if (!parsed) {
+		printf("FAIL: Could not parse ID resp\n");
+		abort();
+	}
+
+	out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi);
+	if (!out) {
+		printf("FAIL: A new message should be created.\n");
+		abort();
+	}
+
+	if (msg == out) {
+		printf("FAIL: The message should have changed\n");
+		abort();
+	}
+
+	verify_msg(out, cc_setup_national, ARRAY_SIZE(cc_setup_national));
+	msgb_free(out);
+
+	bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, NULL);
+	talloc_free(nat);
+}
+
 static void test_sms_smsc_rewrite()
 {
 	struct msgb *msg = msgb_alloc(4096, "SMSC rewrite"), *out;
@@ -1378,6 +1438,7 @@
 	test_dt_filter();
 	test_setup_rewrite();
 	test_setup_rewrite_prefix();
+	test_setup_rewrite_post();
 	test_sms_smsc_rewrite();
 	test_sms_number_rewrite();
 	test_mgcp_allocations();
diff --git a/openbsc/tests/bsc-nat/prefixes.csv b/openbsc/tests/bsc-nat/prefixes.csv
index 0b66ffe..0c7660f 100644
--- a/openbsc/tests/bsc-nat/prefixes.csv
+++ b/openbsc/tests/bsc-nat/prefixes.csv
@@ -1 +1,2 @@
 0172,0049
++49,0
diff --git a/openbsc/tests/vty_test_runner.py b/openbsc/tests/vty_test_runner.py
index 91b4684..730b8ba 100644
--- a/openbsc/tests/vty_test_runner.py
+++ b/openbsc/tests/vty_test_runner.py
@@ -103,6 +103,14 @@
         res = self.vty.command("number-rewrite rewrite.cfg")
         res = self.vty.command("no number-rewrite")
 
+    def testRewritePostNoRewrite(self):
+        self.vty.enable()
+        self.vty.command("configure terminal")
+        self.vty.command("nat")
+        self.vty.verify("number-rewrite-post rewrite.cfg", [''])
+        self.vty.verify("no number-rewrite-post", [''])
+
+
     def testPrefixTreeLoading(self):
         cfg = os.path.join(confpath, "tests/bsc-nat-trie/prefixes.csv")