split off osmo-sgsn: remove files, apply build

Change-Id: I5d27ff93e56cd13e0e70edd15e2080201e35e91f
diff --git a/src/gprs/Makefile.am b/src/gprs/Makefile.am
index 39a4c12..654604b 100644
--- a/src/gprs/Makefile.am
+++ b/src/gprs/Makefile.am
@@ -62,7 +62,6 @@
 	gprs_utils.c \
 	$(NULL)
 osmo_gbproxy_LDADD = \
-	$(top_builddir)/src/libcommon/libcommon.a \
 	$(OSMO_LIBS) \
 	$(LIBCRYPTO_LIBS) \
 	-lrt \
@@ -93,9 +92,10 @@
 	slhc.c \
 	gprs_llc_xid.c \
 	v42bis.c \
+	gsup_client.c \
+	oap_client.c \
 	$(NULL)
 osmo_sgsn_LDADD = \
-	$(top_builddir)/src/libcommon/libcommon.a \
 	$(OSMO_LIBS) \
 	$(LIBOSMOABIS_LIBS) \
 	$(LIBCARES_LIBS) \
@@ -122,7 +122,6 @@
 	gprs_utils.c \
 	$(NULL)
 osmo_gtphub_LDADD = \
-	$(top_builddir)/src/libcommon/libcommon.a \
 	$(LIBOSMOCORE_LIBS) \
 	$(LIBOSMOGSM_LIBS) \
 	$(LIBOSMOVTY_LIBS) \
diff --git a/src/gprs/gb_proxy.c b/src/gprs/gb_proxy.c
index cd38d23..d288cb3 100644
--- a/src/gprs/gb_proxy.c
+++ b/src/gprs/gb_proxy.c
@@ -47,12 +47,13 @@
 #include <openbsc/gb_proxy.h>
 
 #include <openbsc/gprs_llc.h>
-#include <openbsc/gsm_04_08.h>
 #include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
 #include <openbsc/gprs_utils.h>
 
 #include <openssl/rand.h>
 
+extern void *tall_bsc_ctx;
+
 static const struct rate_ctr_desc global_ctr_description[] = {
 	{ "inv-bvci",	    "Invalid BVC Identifier          " },
 	{ "inv-lai",	    "Invalid Location Area Identifier" },
diff --git a/src/gprs/gb_proxy_main.c b/src/gprs/gb_proxy_main.c
index caff27f..853a763 100644
--- a/src/gprs/gb_proxy_main.c
+++ b/src/gprs/gb_proxy_main.c
@@ -190,13 +190,39 @@
 	}
 }
 
-extern int bsc_vty_go_parent(struct vty *vty);
+int gbproxy_vty_is_config_node(struct vty *vty, int node)
+{
+        switch (node) {
+        /* add items that are not config */
+        case CONFIG_NODE:
+                return 0;
+
+        default:
+                return 1;
+        }
+}
+
+int gbproxy_vty_go_parent(struct vty *vty)
+{
+        switch (vty->node) {
+        case GBPROXY_NODE:
+        default:
+                if (gbproxy_vty_is_config_node(vty, vty->node))
+                        vty->node = CONFIG_NODE;
+                else
+                        vty->node = ENABLE_NODE;
+
+                vty->index = NULL;
+        }
+
+        return vty->node;
+}
 
 static struct vty_app_info vty_info = {
 	.name 		= "OsmoGbProxy",
 	.version	= PACKAGE_VERSION,
-	.go_parent_cb	= bsc_vty_go_parent,
-	.is_config_node	= bsc_vty_is_config_node,
+	.go_parent_cb	= gbproxy_vty_go_parent,
+	.is_config_node	= gbproxy_vty_is_config_node,
 };
 
 /* default categories */
@@ -226,7 +252,6 @@
 
 int main(int argc, char **argv)
 {
-	struct gsm_network dummy_network;
 	int rc;
 
 	tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy");
@@ -271,7 +296,7 @@
 	}
 
 	/* start telnet after reading config for vty_get_bind_addr() */
-	rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network,
+	rc = telnet_init_dynif(tall_bsc_ctx, NULL,
 			       vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY);
 	if (rc < 0)
 		exit(1);
diff --git a/src/gprs/gb_proxy_patch.c b/src/gprs/gb_proxy_patch.c
index 210fb2b..bee3150 100644
--- a/src/gprs/gb_proxy_patch.c
+++ b/src/gprs/gb_proxy_patch.c
@@ -23,13 +23,14 @@
 #include <openbsc/gprs_utils.h>
 #include <openbsc/gprs_gb_parse.h>
 
-#include <openbsc/gsm_data.h>
 #include <openbsc/debug.h>
 
 #include <osmocom/gprs/protocol/gsm_08_18.h>
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/gsm/apn.h>
 
+extern void *tall_bsc_ctx;
+
 /* patch RA identifier in place */
 static void gbproxy_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer,
 			       int to_bss, const char *log_text)
diff --git a/src/gprs/gb_proxy_peer.c b/src/gprs/gb_proxy_peer.c
index 8909687..a83630b 100644
--- a/src/gprs/gb_proxy_peer.c
+++ b/src/gprs/gb_proxy_peer.c
@@ -22,17 +22,18 @@
 
 #include <openbsc/gb_proxy.h>
 
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_data_shared.h>
 #include <openbsc/debug.h>
 
 #include <osmocom/gprs/protocol/gsm_08_18.h>
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/stats.h>
 #include <osmocom/core/talloc.h>
+#include <osmocom/gsm/tlv.h>
 
 #include <string.h>
 
+extern void *tall_bsc_ctx;
+
 static const struct rate_ctr_desc peer_ctr_description[] = {
 	{ "blocked",	   "BVC Block                       " },
 	{ "unblocked",	   "BVC Unblock                     " },
diff --git a/src/gprs/gb_proxy_vty.c b/src/gprs/gb_proxy_vty.c
index 86d65a8..bd5bb1b 100644
--- a/src/gprs/gb_proxy_vty.c
+++ b/src/gprs/gb_proxy_vty.c
@@ -26,8 +26,8 @@
 
 #include <osmocom/core/talloc.h>
 #include <osmocom/core/rate_ctr.h>
+#include <osmocom/gsm/gsm48.h>
 
-#include <openbsc/gsm_04_08.h>
 #include <osmocom/gprs/gprs_ns.h>
 #include <osmocom/gsm/apn.h>
 
diff --git a/src/gprs/gprs_gmm.c b/src/gprs/gprs_gmm.c
index 032137f..7301bf1 100644
--- a/src/gprs/gprs_gmm.c
+++ b/src/gprs/gprs_gmm.c
@@ -35,7 +35,6 @@
 
 #include "bscconfig.h"
 
-#include <openbsc/db.h>
 #include <osmocom/core/msgb.h>
 #include <osmocom/gsm/tlv.h>
 #include <osmocom/gsm/gsm_utils.h>
@@ -56,11 +55,6 @@
 #endif
 
 #include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/paging.h>
-#include <openbsc/transaction.h>
 #include <openbsc/gprs_llc.h>
 #include <openbsc/gprs_sgsn.h>
 #include <openbsc/gprs_gmm.h>
@@ -75,6 +69,7 @@
 #define PTMSI_ALLOC
 
 extern struct sgsn_instance *sgsn;
+extern void *tall_bsc_ctx;
 
 static const struct tlv_definition gsm48_gmm_att_tlvdef = {
 	.def = {
diff --git a/src/gprs/gprs_llc.c b/src/gprs/gprs_llc.c
index 2be663f..904ec7e 100644
--- a/src/gprs/gprs_llc.c
+++ b/src/gprs/gprs_llc.c
@@ -31,15 +31,14 @@
 #include <osmocom/core/talloc.h>
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/gprs/gprs_bssgp.h>
+#include <osmocom/gsm/gsm_utils.h>
 
-#include <openbsc/gsm_data.h>
 #include <openbsc/debug.h>
 #include <openbsc/gprs_sgsn.h>
 #include <openbsc/gprs_gmm.h>
 #include <openbsc/gprs_llc.h>
 #include <openbsc/crc24.h>
 #include <openbsc/sgsn.h>
-#include <openbsc/gsm_subscriber.h>
 #include <openbsc/gprs_llc_xid.h>
 #include <openbsc/gprs_sndcp_comp.h>
 #include <openbsc/gprs_sndcp.h>
diff --git a/src/gprs/gprs_llc_parse.c b/src/gprs/gprs_llc_parse.c
index a5a7a71..be63497 100644
--- a/src/gprs/gprs_llc_parse.c
+++ b/src/gprs/gprs_llc_parse.c
@@ -28,7 +28,6 @@
 #include <osmocom/core/talloc.h>
 #include <osmocom/gprs/gprs_bssgp.h>
 
-#include <openbsc/gsm_data.h>
 #include <openbsc/debug.h>
 #include <openbsc/gprs_sgsn.h>
 #include <openbsc/gprs_gmm.h>
diff --git a/src/gprs/gprs_llc_vty.c b/src/gprs/gprs_llc_vty.c
index bf34e97..626d6ef 100644
--- a/src/gprs/gprs_llc_vty.c
+++ b/src/gprs/gprs_llc_vty.c
@@ -27,7 +27,6 @@
 
 #include <arpa/inet.h>
 
-#include <openbsc/gsm_data.h>
 #include <osmocom/core/msgb.h>
 #include <osmocom/gsm/tlv.h>
 #include <osmocom/core/talloc.h>
diff --git a/src/gprs/gprs_sgsn.c b/src/gprs/gprs_sgsn.c
index de79afb..560485d 100644
--- a/src/gprs/gprs_sgsn.c
+++ b/src/gprs/gprs_sgsn.c
@@ -31,6 +31,7 @@
 #include <osmocom/gprs/gprs_bssgp.h>
 #include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
 #include <osmocom/gsm/apn.h>
+#include <osmocom/gsm/gsm_utils.h>
 
 #include <openbsc/gprs_subscriber.h>
 #include <openbsc/debug.h>
@@ -56,6 +57,7 @@
 #define GPRS_LLME_CHECK_TICK 30
 
 extern struct sgsn_instance *sgsn;
+extern void *tall_bsc_ctx;
 
 LLIST_HEAD(sgsn_mm_ctxts);
 LLIST_HEAD(sgsn_ggsn_ctxts);
diff --git a/src/gprs/gprs_sndcp.c b/src/gprs/gprs_sndcp.c
index a18998f..05dad66 100644
--- a/src/gprs/gprs_sndcp.c
+++ b/src/gprs/gprs_sndcp.c
@@ -30,7 +30,6 @@
 #include <osmocom/core/talloc.h>
 #include <osmocom/gprs/gprs_bssgp.h>
 
-#include <openbsc/gsm_data.h>
 #include <openbsc/debug.h>
 #include <openbsc/gprs_llc.h>
 #include <openbsc/sgsn.h>
diff --git a/src/gprs/gprs_sndcp_vty.c b/src/gprs/gprs_sndcp_vty.c
index 430881f..4bad8b0 100644
--- a/src/gprs/gprs_sndcp_vty.c
+++ b/src/gprs/gprs_sndcp_vty.c
@@ -26,7 +26,6 @@
 
 #include <arpa/inet.h>
 
-#include <openbsc/gsm_data.h>
 #include <osmocom/core/msgb.h>
 #include <osmocom/gsm/tlv.h>
 #include <osmocom/core/talloc.h>
diff --git a/src/gprs/gsup_client.c b/src/gprs/gsup_client.c
new file mode 100644
index 0000000..258f230
--- /dev/null
+++ b/src/gprs/gsup_client.c
@@ -0,0 +1,355 @@
+/* Generic Subscriber Update Protocol client */
+
+/* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Jacob Erlbeck
+ * Author: Neels Hofmeyr
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <openbsc/gsup_client.h>
+
+#include <osmocom/abis/ipa.h>
+#include <osmocom/gsm/protocol/ipaccess.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+
+#include <openbsc/debug.h>
+
+#include <errno.h>
+#include <string.h>
+
+extern void *tall_bsc_ctx;
+
+static void start_test_procedure(struct gsup_client *gsupc);
+
+static void gsup_client_send_ping(struct gsup_client *gsupc)
+{
+	struct msgb *msg = gsup_client_msgb_alloc();
+
+	msg->l2h = msgb_put(msg, 1);
+	msg->l2h[0] = IPAC_MSGT_PING;
+	ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS);
+	ipa_client_conn_send(gsupc->link, msg);
+}
+
+static int gsup_client_connect(struct gsup_client *gsupc)
+{
+	int rc;
+
+	if (gsupc->is_connected)
+		return 0;
+
+	if (osmo_timer_pending(&gsupc->connect_timer)) {
+		LOGP(DLGSUP, LOGL_DEBUG,
+		     "GSUP connect: connect timer already running\n");
+		osmo_timer_del(&gsupc->connect_timer);
+	}
+
+	if (osmo_timer_pending(&gsupc->ping_timer)) {
+		LOGP(DLGSUP, LOGL_DEBUG,
+		     "GSUP connect: ping timer already running\n");
+		osmo_timer_del(&gsupc->ping_timer);
+	}
+
+	if (ipa_client_conn_clear_queue(gsupc->link) > 0)
+		LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n");
+
+	rc = ipa_client_conn_open(gsupc->link);
+
+	if (rc >= 0) {
+		LOGP(DLGSUP, LOGL_NOTICE, "GSUP connecting to %s:%d\n",
+		     gsupc->link->addr, gsupc->link->port);
+		return 0;
+	}
+
+	LOGP(DLGSUP, LOGL_ERROR, "GSUP failed to connect to %s:%d: %s\n",
+	     gsupc->link->addr, gsupc->link->port, strerror(-rc));
+
+	if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT ||
+	    rc == -EINVAL)
+		return rc;
+
+	osmo_timer_schedule(&gsupc->connect_timer,
+			    GSUP_CLIENT_RECONNECT_INTERVAL, 0);
+
+	LOGP(DLGSUP, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n",
+	     gsupc->link->addr, gsupc->link->port);
+
+	return 0;
+}
+
+static void connect_timer_cb(void *gsupc_)
+{
+	struct gsup_client *gsupc = gsupc_;
+
+	if (gsupc->is_connected)
+		return;
+
+	gsup_client_connect(gsupc);
+}
+
+static void client_send(struct gsup_client *gsupc, int proto_ext,
+			struct msgb *msg_tx)
+{
+	ipa_prepend_header_ext(msg_tx, proto_ext);
+	ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO);
+	ipa_client_conn_send(gsupc->link, msg_tx);
+	/* msg_tx is now queued and will be freed. */
+}
+
+static void gsup_client_oap_register(struct gsup_client *gsupc)
+{
+	struct msgb *msg_tx;
+	int rc;
+	rc = oap_client_register(&gsupc->oap_state, &msg_tx);
+
+	if ((rc < 0) || (!msg_tx)) {
+		LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n");
+		return;
+	}
+
+	client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
+}
+
+static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
+{
+	struct gsup_client *gsupc = link->data;
+
+	LOGP(DLGSUP, LOGL_INFO, "GSUP link to %s:%d %s\n",
+		     link->addr, link->port, up ? "UP" : "DOWN");
+
+	gsupc->is_connected = up;
+
+	if (up) {
+		start_test_procedure(gsupc);
+
+		if (gsupc->oap_state.state == OAP_INITIALIZED)
+			gsup_client_oap_register(gsupc);
+
+		osmo_timer_del(&gsupc->connect_timer);
+	} else {
+		osmo_timer_del(&gsupc->ping_timer);
+
+		osmo_timer_schedule(&gsupc->connect_timer,
+				    GSUP_CLIENT_RECONNECT_INTERVAL, 0);
+	}
+}
+
+static int gsup_client_oap_handle(struct gsup_client *gsupc, struct msgb *msg_rx)
+{
+	int rc;
+	struct msgb *msg_tx;
+
+	/* If the oap_state is disabled, this will reject the messages. */
+	rc = oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx);
+	msgb_free(msg_rx);
+	if (rc < 0)
+		return rc;
+
+	if (msg_tx)
+		client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
+
+	return 0;
+}
+
+static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg)
+{
+	struct ipaccess_head *hh = (struct ipaccess_head *) msg->data;
+	struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg);
+	struct gsup_client *gsupc = (struct gsup_client *)link->data;
+	int rc;
+	struct ipaccess_unit ipa_dev = {
+		/* see gsup_client_create() on const vs non-const */
+		.unit_name = (char*)gsupc->unit_name,
+	};
+
+	OSMO_ASSERT(ipa_dev.unit_name);
+
+	msg->l2h = &hh->data[0];
+
+	rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg);
+
+	if (rc < 0) {
+		LOGP(DLGSUP, LOGL_NOTICE,
+		     "GSUP received an invalid IPA/CCM message from %s:%d\n",
+		     link->addr, link->port);
+		/* Link has been closed */
+		gsupc->is_connected = 0;
+		msgb_free(msg);
+		return -1;
+	}
+
+	if (rc == 1) {
+		uint8_t msg_type = *(msg->l2h);
+		/* CCM message */
+		if (msg_type == IPAC_MSGT_PONG) {
+			LOGP(DLGSUP, LOGL_DEBUG, "GSUP receiving PONG\n");
+			gsupc->got_ipa_pong = 1;
+		}
+
+		msgb_free(msg);
+		return 0;
+	}
+
+	if (hh->proto != IPAC_PROTO_OSMO)
+		goto invalid;
+
+	if (!he || msgb_l2len(msg) < sizeof(*he))
+		goto invalid;
+
+	msg->l2h = &he->data[0];
+
+	if (he->proto == IPAC_PROTO_EXT_GSUP) {
+		OSMO_ASSERT(gsupc->read_cb != NULL);
+		gsupc->read_cb(gsupc, msg);
+		/* expecting read_cb() to free msg */
+	} else if (he->proto == IPAC_PROTO_EXT_OAP) {
+		return gsup_client_oap_handle(gsupc, msg);
+		/* gsup_client_oap_handle frees msg */
+	} else
+		goto invalid;
+
+	return 0;
+
+invalid:
+	LOGP(DLGSUP, LOGL_NOTICE,
+	     "GSUP received an invalid IPA message from %s:%d, size = %d\n",
+	     link->addr, link->port, msgb_length(msg));
+
+	msgb_free(msg);
+	return -1;
+}
+
+static void ping_timer_cb(void *gsupc_)
+{
+	struct gsup_client *gsupc = gsupc_;
+
+	LOGP(DLGSUP, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n",
+	     gsupc->is_connected ? "connected" : "not connected",
+	     gsupc->got_ipa_pong ? "got" : "didn't get");
+
+	if (gsupc->got_ipa_pong) {
+		start_test_procedure(gsupc);
+		return;
+	}
+
+	LOGP(DLGSUP, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n");
+	ipa_client_conn_close(gsupc->link);
+	gsupc->is_connected = 0;
+
+	gsup_client_connect(gsupc);
+}
+
+static void start_test_procedure(struct gsup_client *gsupc)
+{
+	osmo_timer_setup(&gsupc->ping_timer, ping_timer_cb, gsupc);
+
+	gsupc->got_ipa_pong = 0;
+	osmo_timer_schedule(&gsupc->ping_timer, GSUP_CLIENT_PING_INTERVAL, 0);
+	LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n");
+	gsup_client_send_ping(gsupc);
+}
+
+struct gsup_client *gsup_client_create(const char *unit_name,
+				       const char *ip_addr,
+				       unsigned int tcp_port,
+				       gsup_client_read_cb_t read_cb,
+				       struct oap_client_config *oapc_config)
+{
+	struct gsup_client *gsupc;
+	int rc;
+
+	gsupc = talloc_zero(tall_bsc_ctx, struct gsup_client);
+	OSMO_ASSERT(gsupc);
+
+	/* struct ipaccess_unit has a non-const unit_name, so let's copy to be
+	 * able to have a non-const unit_name here as well. To not taint the
+	 * public gsup_client API, let's store it in a const char* anyway. */
+	gsupc->unit_name = talloc_strdup(gsupc, unit_name);
+	OSMO_ASSERT(gsupc->unit_name);
+
+	/* a NULL oapc_config will mark oap_state disabled. */
+	rc = oap_client_init(oapc_config, &gsupc->oap_state);
+	if (rc != 0)
+		goto failed;
+
+	gsupc->link = ipa_client_conn_create(gsupc,
+					     /* no e1inp */ NULL,
+					     0,
+					     ip_addr, tcp_port,
+					     gsup_client_updown_cb,
+					     gsup_client_read_cb,
+					     /* default write_cb */ NULL,
+					     gsupc);
+	if (!gsupc->link)
+		goto failed;
+
+	osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc);
+
+	rc = gsup_client_connect(gsupc);
+
+	if (rc < 0)
+		goto failed;
+
+	gsupc->read_cb = read_cb;
+
+	return gsupc;
+
+failed:
+	gsup_client_destroy(gsupc);
+	return NULL;
+}
+
+void gsup_client_destroy(struct gsup_client *gsupc)
+{
+	osmo_timer_del(&gsupc->connect_timer);
+	osmo_timer_del(&gsupc->ping_timer);
+
+	if (gsupc->link) {
+		ipa_client_conn_close(gsupc->link);
+		ipa_client_conn_destroy(gsupc->link);
+		gsupc->link = NULL;
+	}
+	talloc_free(gsupc);
+}
+
+int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg)
+{
+	if (!gsupc) {
+		LOGP(DGPRS, LOGL_NOTICE, "No GSUP client, unable to "
+			"send %s\n", msgb_hexdump(msg));
+		msgb_free(msg);
+		return -ENOTCONN;
+	}
+
+	if (!gsupc->is_connected) {
+		LOGP(DGPRS, LOGL_NOTICE, "GSUP not connected, unable to "
+			"send %s\n", msgb_hexdump(msg));
+		msgb_free(msg);
+		return -EAGAIN;
+	}
+
+	client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg);
+
+	return 0;
+}
+
+struct msgb *gsup_client_msgb_alloc(void)
+{
+	return msgb_alloc_headroom(4000, 64, __func__);
+}
diff --git a/src/gprs/gtphub_main.c b/src/gprs/gtphub_main.c
index 2b87d19..d7b3ba7 100644
--- a/src/gprs/gtphub_main.c
+++ b/src/gprs/gtphub_main.c
@@ -39,6 +39,8 @@
 #include <osmocom/vty/telnet_interface.h>
 #include <osmocom/vty/ports.h>
 
+#include <osmocom/sigtran/osmo_ss7.h>
+
 #include <openbsc/debug.h>
 #include <openbsc/gtphub.h>
 #include <openbsc/vty.h>
@@ -46,7 +48,7 @@
 #include "../../bscconfig.h"
 
 extern void *osmo_gtphub_ctx;
-
+void *tall_bsc_ctx;
 
 const char *gtphub_copyright =
 	"Copyright (C) 2015 sysmocom s.f.m.c GmbH <info@sysmocom.de>\r\n"
@@ -113,13 +115,38 @@
 	}
 }
 
-extern int bsc_vty_go_parent(struct vty *vty);
+int gtphub_vty_go_parent(struct vty *vty)
+{
+	switch (vty->node) {
+	default:
+		osmo_ss7_vty_go_parent(vty);
+	}
+
+	return vty->node;
+}
+
+int gtphub_vty_is_config_node(struct vty *vty, int node)
+{
+	/* Check if libosmo-sccp declares the node in
+	 * question as config node */
+	if (osmo_ss7_is_config_node(vty, node))
+		return 1;
+
+	switch (node) {
+	/* add items that are not config */
+	case CONFIG_NODE:
+		return 0;
+
+	default:
+		return 1;
+	}
+}
 
 static struct vty_app_info vty_info = {
 	.name 		= "OsmoGTPhub",
 	.version	= PACKAGE_VERSION,
-	.go_parent_cb	= bsc_vty_go_parent,
-	.is_config_node	= bsc_vty_is_config_node,
+	.go_parent_cb	= gtphub_vty_go_parent,
+	.is_config_node	= gtphub_vty_is_config_node,
 };
 
 struct cmdline_cfg {
diff --git a/src/gprs/gtphub_vty.c b/src/gprs/gtphub_vty.c
index a30ad2a..d611b1f 100644
--- a/src/gprs/gtphub_vty.c
+++ b/src/gprs/gtphub_vty.c
@@ -37,6 +37,7 @@
  * globals. */
 #include <openbsc/sgsn.h>
 extern struct sgsn_instance *sgsn;
+extern void *tall_bsc_ctx;
 
 static struct gtphub *g_hub = 0;
 static struct gtphub_cfg *g_cfg = 0;
diff --git a/src/gprs/oap_client.c b/src/gprs/oap_client.c
new file mode 100644
index 0000000..5128ac1
--- /dev/null
+++ b/src/gprs/oap_client.c
@@ -0,0 +1,280 @@
+/* Osmocom Authentication Protocol API */
+
+/* (C) 2015 by Sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/crypt/auth.h>
+#include <osmocom/gsm/oap.h>
+
+#include <openbsc/oap_client.h>
+#include <openbsc/debug.h>
+
+int oap_client_init(struct oap_client_config *config,
+		    struct oap_client_state *state)
+{
+	OSMO_ASSERT(state->state == OAP_UNINITIALIZED);
+
+	if (!config)
+		goto disable;
+
+	if (config->client_id == 0)
+		goto disable;
+
+	if (config->secret_k_present == 0) {
+		LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret K missing.\n");
+		goto disable;
+	}
+
+	if (config->secret_opc_present == 0) {
+		LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret OPC missing.\n");
+		goto disable;
+	}
+
+	state->client_id = config->client_id;
+	memcpy(state->secret_k, config->secret_k, sizeof(state->secret_k));
+	memcpy(state->secret_opc, config->secret_opc, sizeof(state->secret_opc));
+	state->state = OAP_INITIALIZED;
+	return 0;
+
+disable:
+	state->state = OAP_DISABLED;
+	return 0;
+}
+
+/* From the given state and received RAND and AUTN octets, validate the
+ * server's authenticity and formulate the matching milenage reply octets in
+ * *tx_xres. The state is not modified.
+ * On success, and if tx_res is not NULL, exactly 8 octets will be written to
+ * *tx_res. If not NULL, tx_res must point at allocated memory of at least 8
+ * octets. The caller will want to send XRES back to the server in a challenge
+ * response message and update the state.
+ * Return 0 on success; -1 if OAP is disabled; -2 if rx_random and rx_autn fail
+ * the authentication check; -3 for any other errors. */
+static int oap_evaluate_challenge(const struct oap_client_state *state,
+				  const uint8_t *rx_random,
+				  const uint8_t *rx_autn,
+				  uint8_t *tx_xres)
+{
+	struct osmo_auth_vector vec;
+
+	struct osmo_sub_auth_data auth = {
+		.type		= OSMO_AUTH_TYPE_UMTS,
+		.algo		= OSMO_AUTH_ALG_MILENAGE,
+	};
+
+	osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.k)
+			   == sizeof(state->secret_k), _secret_k_size_match);
+	osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.opc)
+			   == sizeof(state->secret_opc), _secret_opc_size_match);
+
+	switch (state->state) {
+	case OAP_UNINITIALIZED:
+	case OAP_DISABLED:
+		return -1;
+	default:
+		break;
+	}
+
+	memcpy(auth.u.umts.k, state->secret_k, sizeof(auth.u.umts.k));
+	memcpy(auth.u.umts.opc, state->secret_opc, sizeof(auth.u.umts.opc));
+	memset(auth.u.umts.amf, '\0', sizeof(auth.u.umts.amf));
+	auth.u.umts.sqn = 41; /* TODO use incrementing sequence nr */
+
+	memset(&vec, 0, sizeof(vec));
+	osmo_auth_gen_vec(&vec, &auth, rx_random);
+
+	if (vec.res_len != 8) {
+		LOGP(DLOAP, LOGL_ERROR, "OAP: Expected XRES to be 8 octets, got %d\n",
+		     vec.res_len);
+		return -3;
+	}
+
+	if (osmo_constant_time_cmp(vec.autn, rx_autn, sizeof(vec.autn)) != 0) {
+		LOGP(DLOAP, LOGL_ERROR, "OAP: AUTN mismatch!\n");
+		LOGP(DLOAP, LOGL_INFO, "OAP: AUTN from server: %s\n",
+		     osmo_hexdump_nospc(rx_autn, sizeof(vec.autn)));
+		LOGP(DLOAP, LOGL_INFO, "OAP: AUTN expected:    %s\n",
+		     osmo_hexdump_nospc(vec.autn, sizeof(vec.autn)));
+		return -2;
+	}
+
+	if (tx_xres != NULL)
+		memcpy(tx_xres, vec.res, 8);
+	return 0;
+}
+
+struct msgb *oap_client_encoded(const struct osmo_oap_message *oap_msg)
+{
+	struct msgb *msg = msgb_alloc_headroom(1000, 64, __func__);
+	OSMO_ASSERT(msg);
+	osmo_oap_encode(msg, oap_msg);
+	return msg;
+}
+
+/* Create a new msgb containing an OAP registration message.
+ * On error, return NULL. */
+static struct msgb* oap_msg_register(uint16_t client_id)
+{
+	struct osmo_oap_message oap_msg = {0};
+
+	if (client_id < 1) {
+		LOGP(DLOAP, LOGL_ERROR, "OAP: Invalid client ID: %d\n", client_id);
+		return NULL;
+	}
+
+	oap_msg.message_type = OAP_MSGT_REGISTER_REQUEST;
+	oap_msg.client_id = client_id;
+	return oap_client_encoded(&oap_msg);
+}
+
+int oap_client_register(struct oap_client_state *state, struct msgb **msg_tx)
+{
+	*msg_tx = oap_msg_register(state->client_id);
+	if (!(*msg_tx))
+		return -1;
+
+	state->state = OAP_REQUESTED_CHALLENGE;
+	return 0;
+}
+
+/* Create a new msgb containing an OAP challenge response message.
+ * xres must point at 8 octets to return as challenge response.
+ * On error, return NULL. */
+static struct msgb* oap_msg_challenge_response(uint8_t *xres)
+{
+	struct osmo_oap_message oap_reply = {0};
+
+	oap_reply.message_type = OAP_MSGT_CHALLENGE_RESULT;
+	memcpy(oap_reply.xres, xres, sizeof(oap_reply.xres));
+	oap_reply.xres_present = 1;
+	return oap_client_encoded(&oap_reply);
+}
+
+static int handle_challenge(struct oap_client_state *state,
+			    struct osmo_oap_message *oap_rx,
+			    struct msgb **msg_tx)
+{
+	int rc;
+	uint8_t xres[8];
+
+	if (!(oap_rx->rand_present && oap_rx->autn_present)) {
+		LOGP(DLOAP, LOGL_ERROR,
+		     "OAP challenge incomplete (rand_present: %d, autn_present: %d)\n",
+		     oap_rx->rand_present, oap_rx->autn_present);
+		rc = -2;
+		goto failure;
+	}
+
+	rc = oap_evaluate_challenge(state,
+				    oap_rx->rand,
+				    oap_rx->autn,
+				    xres);
+	if (rc < 0)
+		goto failure;
+
+	*msg_tx = oap_msg_challenge_response(xres);
+	if ((*msg_tx) == NULL) {
+		rc = -1;
+		goto failure;
+	}
+
+	state->state = OAP_SENT_CHALLENGE_RESULT;
+	return 0;
+
+failure:
+	OSMO_ASSERT(rc < 0);
+	state->state = OAP_INITIALIZED;
+	return rc;
+}
+
+int oap_client_handle(struct oap_client_state *state,
+		      const struct msgb *msg_rx, struct msgb **msg_tx)
+{
+	uint8_t *data = msgb_l2(msg_rx);
+	size_t data_len = msgb_l2len(msg_rx);
+	struct osmo_oap_message oap_msg = {0};
+	int rc = 0;
+
+	*msg_tx = NULL;
+
+	OSMO_ASSERT(data);
+
+	rc = osmo_oap_decode(&oap_msg, data, data_len);
+	if (rc < 0) {
+		LOGP(DLOAP, LOGL_ERROR,
+		     "Decoding OAP message failed with error '%s' (%d)\n",
+		     get_value_string(gsm48_gmm_cause_names, -rc), -rc);
+		return -10;
+	}
+
+	switch (state->state) {
+	case OAP_UNINITIALIZED:
+		LOGP(DLOAP, LOGL_ERROR,
+		     "Received OAP message %d, but the OAP client is"
+		     " not initialized\n", oap_msg.message_type);
+		return -ENOTCONN;
+	case OAP_DISABLED:
+		LOGP(DLOAP, LOGL_ERROR,
+		     "Received OAP message %d, but the OAP client is"
+		     " disabled\n", oap_msg.message_type);
+		return -ENOTCONN;
+	default:
+		break;
+	}
+
+	switch (oap_msg.message_type) {
+	case OAP_MSGT_CHALLENGE_REQUEST:
+		return handle_challenge(state, &oap_msg, msg_tx);
+
+	case OAP_MSGT_REGISTER_RESULT:
+		/* successfully registered */
+		state->state = OAP_REGISTERED;
+		break;
+
+	case OAP_MSGT_REGISTER_ERROR:
+		LOGP(DLOAP, LOGL_ERROR,
+		     "OAP registration failed\n");
+		state->state = OAP_INITIALIZED;
+		if (state->registration_failures < 3) {
+			state->registration_failures ++;
+			return oap_client_register(state, msg_tx);
+		}
+		return -11;
+
+	case OAP_MSGT_REGISTER_REQUEST:
+	case OAP_MSGT_CHALLENGE_RESULT:
+		LOGP(DLOAP, LOGL_ERROR,
+		     "Received invalid OAP message type for OAP client side: %d\n",
+		     (int)oap_msg.message_type);
+		return -12;
+
+	default:
+		LOGP(DLOAP, LOGL_ERROR,
+		     "Unknown OAP message type: %d\n",
+		     (int)oap_msg.message_type);
+		return -13;
+	}
+
+	return 0;
+}
diff --git a/src/gprs/sgsn_ares.c b/src/gprs/sgsn_ares.c
index d94d184..6238099 100644
--- a/src/gprs/sgsn_ares.c
+++ b/src/gprs/sgsn_ares.c
@@ -24,6 +24,8 @@
 
 #include <netdb.h>
 
+extern void *tall_bsc_ctx;
+
 struct cares_event_fd {
 	struct llist_head head;
 	struct osmo_fd fd;
diff --git a/src/gprs/sgsn_ctrl.c b/src/gprs/sgsn_ctrl.c
index 31ac74f..f7b1180 100644
--- a/src/gprs/sgsn_ctrl.c
+++ b/src/gprs/sgsn_ctrl.c
@@ -21,7 +21,6 @@
 
 #include <osmocom/ctrl/control_if.h>
 #include <osmocom/ctrl/control_cmd.h>
-#include <openbsc/gsm_data.h>
 #include <openbsc/gprs_sgsn.h>
 #include <openbsc/sgsn.h>
 #include <openbsc/debug.h>
diff --git a/src/gprs/sgsn_main.c b/src/gprs/sgsn_main.c
index 25ee632..e24a57b 100644
--- a/src/gprs/sgsn_main.c
+++ b/src/gprs/sgsn_main.c
@@ -62,13 +62,13 @@
 #include <osmocom/ctrl/control_if.h>
 #include <osmocom/ctrl/ports.h>
 
-#include <osmocom/sigtran/protocol/m3ua.h>
-
 #include <gtp.h>
 
 #include "../../bscconfig.h"
 
 #if BUILD_IU
+#include <osmocom/sigtran/osmo_ss7.h>
+#include <osmocom/sigtran/protocol/m3ua.h>
 #include <osmocom/ranap/iu_client.h>
 #endif
 
@@ -173,13 +173,40 @@
 /* NSI that BSSGP uses when transmitting on NS */
 extern struct gprs_ns_inst *bssgp_nsi;
 
-extern int bsc_vty_go_parent(struct vty *vty);
+int sgsn_vty_is_config_node(struct vty *vty, int node)
+{
+	/* So far the SGSN has no nested nodes that need parent node
+	 * declaration, except for the ss7 vty nodes. */
+	switch (node) {
+	case SGSN_NODE:
+		return 1;
+	default:
+#if BUILD_IU
+		return osmo_ss7_is_config_node(vty, node);
+#else
+		return 0;
+#endif
+	}
+}
+
+int sgsn_vty_go_parent(struct vty *vty)
+{
+	/* So far the SGSN has no nested nodes that need parent node
+	 * declaration, except for the ss7 vty nodes. */
+#if BUILD_IU
+	return osmo_ss7_vty_go_parent(vty);
+#else
+	vty->node = CONFIG_NODE;
+	vty->index = NULL;
+	return 0;
+#endif
+}
 
 static struct vty_app_info vty_info = {
 	.name 		= "OsmoSGSN",
 	.version	= PACKAGE_VERSION,
-	.go_parent_cb	= bsc_vty_go_parent,
-	.is_config_node	= bsc_vty_is_config_node,
+	.go_parent_cb	= sgsn_vty_go_parent,
+	.is_config_node	= sgsn_vty_is_config_node,
 };
 
 static void print_help(void)
@@ -325,14 +352,17 @@
 	.num_cat = ARRAY_SIZE(gprs_categories),
 };
 
-int sgsn_ranap_iu_event(struct ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data);
+#if BUILD_IU
+int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data);
+#endif
 
 int main(int argc, char **argv)
 {
 	struct ctrl_handle *ctrl;
-	struct gsm_network dummy_network;
-	struct osmo_sccp_instance *sccp;
 	int rc;
+#if BUILD_IU
+	struct osmo_sccp_instance *sccp;
+#endif
 
 	srand(time(NULL));
 	tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
@@ -354,7 +384,11 @@
 	osmo_stats_vty_add_cmds(&gprs_log_info);
 	sgsn_vty_init(&sgsn_inst.cfg);
 	ctrl_vty_init(tall_bsc_ctx);
+
+#if BUILD_IU
 	osmo_ss7_init();
+	osmo_ss7_vty_init_asp(tall_bsc_ctx);
+#endif
 
 	handle_options(argc, argv);
 
@@ -389,7 +423,7 @@
 	}
 
 	/* start telnet after reading config for vty_get_bind_addr() */
-	rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network,
+	rc = telnet_init_dynif(tall_bsc_ctx, NULL,
 			       vty_get_bind_addr(), OSMO_VTY_PORT_SGSN);
 	if (rc < 0)
 		exit(1);
@@ -442,7 +476,7 @@
 		}
 	}
 
-#ifdef BUILD_IU
+#if BUILD_IU
 	sccp = osmo_sccp_simple_client(tall_bsc_ctx, "OsmoSGSN",
 				       2 /* FIXME: configurable */,
 				       OSMO_SS7_ASP_PROT_M3UA, 0,
diff --git a/src/gprs/sgsn_vty.c b/src/gprs/sgsn_vty.c
index 3a5b2ca..fce2518 100644
--- a/src/gprs/sgsn_vty.c
+++ b/src/gprs/sgsn_vty.c
@@ -51,6 +51,8 @@
 #include <osmocom/ranap/iu_client.h>
 #endif
 
+extern void *tall_bsc_ctx;
+
 static struct sgsn_config *g_cfg = NULL;
 
 const struct value_string sgsn_auth_pol_strs[] = {