sgsn: Add functions to handle APN contexts

This commit adds the exported functions apn_ctx_find_alloc,
apn_ctx_free, apn_ctx_by_name, and apn_ctx_match to manage and
retrieve APN to GGSN mappings.

The following VTY commands are added to 'config-sgsn':

 - apn APN ggsn <0-255>
 - apn APN imsi-prefix PREFIX ggsn <0-255>

which maps an APN gateway string to an SGSN id. The SGSN must be
configured in advance. When matching an APN string, entries with a
leading '*' are used for suffix matching, otherwise an exact match is
done.  When a prefix is given, it is matched against the IMSI. If
several entries match, a longer matching IMSI prefix has precedence.
If there are several matching entries with the same PREFIX, the entry
with longest matching APN is returned.

Ticket: OW#1334
Sponsored-by: On-Waves ehf
diff --git a/openbsc/tests/vty_test_runner.py b/openbsc/tests/vty_test_runner.py
index d87ebde..cae1c14 100644
--- a/openbsc/tests/vty_test_runner.py
+++ b/openbsc/tests/vty_test_runner.py
@@ -794,6 +794,27 @@
         res = self.vty.command('show subscriber cache')
         self.assert_(res.find('1234567890') < 0)
 
+    def testVtyGgsn(self):
+        self.vty.enable()
+        self.assertTrue(self.vty.verify('configure terminal', ['']))
+        self.assertEquals(self.vty.node(), 'config')
+        self.assertTrue(self.vty.verify('sgsn', ['']))
+        self.assertEquals(self.vty.node(), 'config-sgsn')
+        self.assertTrue(self.vty.verify('ggsn 0 remote-ip 127.99.99.99', ['']))
+        self.assertTrue(self.vty.verify('ggsn 0 gtp-version 1', ['']))
+        self.assertTrue(self.vty.verify('apn * ggsn 0', ['']))
+        self.assertTrue(self.vty.verify('apn apn1.test ggsn 0', ['']))
+        self.assertTrue(self.vty.verify('apn apn1.test ggsn 1', ['% a GGSN with id 1 has not been defined']))
+        self.assertTrue(self.vty.verify('apn apn1.test imsi-prefix 123456 ggsn 0', ['']))
+        self.assertTrue(self.vty.verify('apn apn2.test imsi-prefix 123456 ggsn 0', ['']))
+        res = self.vty.command("show running-config")
+        self.assert_(res.find('ggsn 0 remote-ip 127.99.99.99') >= 0)
+        self.assert_(res.find('ggsn 0 gtp-version 1') >= 0)
+        self.assert_(res.find('apn * ggsn 0') >= 0)
+        self.assert_(res.find('apn apn1.test ggsn 0') >= 0)
+        self.assert_(res.find('apn apn1.test imsi-prefix 123456 ggsn 0') >= 0)
+        self.assert_(res.find('apn apn2.test imsi-prefix 123456 ggsn 0') >= 0)
+
 def add_nat_test(suite, workdir):
     if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc_nat/osmo-bsc_nat")):
         print("Skipping the NAT test")