GPRS: Introduce a GPRS Gb Proxy

The ida of the Gb proxy is to aggregate Gb links with a number of BSS
and then present all the BSSGP-VC's together inside one NS-VC to the
actual SGSN.

The code is not yet expected to be complete.
diff --git a/openbsc/include/openbsc/gprs_bssgp.h b/openbsc/include/openbsc/gprs_bssgp.h
index f85ac48..3040e6a 100644
--- a/openbsc/include/openbsc/gprs_bssgp.h
+++ b/openbsc/include/openbsc/gprs_bssgp.h
@@ -133,6 +133,16 @@
 	BSSGP_CAUSE_PDU_INCOMP_FEAT	= 0x28,
 };
 
+/* Our implementation */
+
+#include <osmocore/tlv.h>
+
 extern int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t bvci);
 
+/* Wrapper around TLV parser to parse BSSGP IEs */
+static inline int bssgp_tlv_parse(struct tlv_parsed *tp, u_int8_t *buf, int len)
+{
+	return tlv_parse(tp, &tvlv_att_def, buf, len, 0, 0);
+}
+
 #endif /* _GPRS_BSSGP_H */
diff --git a/openbsc/include/openbsc/gprs_ns.h b/openbsc/include/openbsc/gprs_ns.h
index 34a3e58..dd10d33 100644
--- a/openbsc/include/openbsc/gprs_ns.h
+++ b/openbsc/include/openbsc/gprs_ns.h
@@ -73,7 +73,37 @@
 	NS_CAUSE_UNKN_IP_TEST_FAILED	= 0x14,
 };
 
-struct gprs_nsvc;
+
+/* Our Implementation */
+#include <netinet/in.h>
+
+#define NSE_S_BLOCKED	0x0001
+#define NSE_S_ALIVE	0x0002
+
+struct gprs_nsvc {
+	struct llist_head list;
+	struct gprs_ns_inst *nsi;
+
+	u_int16_t nsei;		/* end-to-end significance */
+	u_int16_t nsvci;	/* uniquely identifies NS-VC at SGSN */
+
+	u_int32_t state;
+	u_int32_t remote_state;
+
+	struct timer_list alive_timer;
+	int timer_is_tns_alive;
+	int alive_retries;
+
+	int remote_end_is_sgsn;
+
+	union {
+		struct {
+			struct sockaddr_in bts_addr;
+		} ip;
+	};
+};
+
+
 struct gprs_ns_inst;
 
 enum gprs_ns_evt {
@@ -101,4 +131,11 @@
 /* main function for higher layers (BSSGP) to send NS messages */
 int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg);
 
+
+/* Listen for incoming GPRS packets */
+int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port);
+
+/* Establish a connection (from the BSS) to the SGSN */
+struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi,
+				struct sockaddr_in *dest, uint16_t nsvci);
 #endif
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index 718252f..ac41870 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -3,7 +3,7 @@
 AM_LDFLAGS = $(LIBOSMOCORE_LIBS)
 
 sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \
-                isdnsync bsc_mgcp ipaccess-proxy
+                isdnsync bsc_mgcp ipaccess-proxy osmo-gb_proxy
 noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a libsgsn.a
 noinst_HEADERS = vty/cardshell.h
 
@@ -11,7 +11,7 @@
 bsc_LIBRARIES = libsccp.a
 
 libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
-		chan_alloc.c debug.c \
+		chan_alloc.c debug.c socket.c \
 		gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.c \
 		trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c \
 		input/misdn.c input/ipaccess.c \
@@ -50,3 +50,8 @@
 bsc_mgcp_LDADD = libvty.a
 
 ipaccess_proxy_SOURCES = ipaccess/ipaccess-proxy.c debug.c
+
+osmo_gb_proxy_SOURCES = gb_proxy.c gb_proxy_main.c \
+			gprs_ns.c \
+			socket.c debug.c telnet_interface.c vty_interface_cmds.c 
+osmo_gb_proxy_LDADD = libvty.a
diff --git a/openbsc/src/gb_proxy.c b/openbsc/src/gb_proxy.c
new file mode 100644
index 0000000..77cc8ca
--- /dev/null
+++ b/openbsc/src/gb_proxy.c
@@ -0,0 +1,315 @@
+/* NS-over-IP proxy */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by On Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+
+#include <openbsc/signal.h>
+#include <openbsc/debug.h>
+#include <openbsc/gprs_ns.h>
+#include <openbsc/gprs_bssgp.h>
+
+struct gbprox_peer {
+	struct llist_head list;
+
+	/* NS-VC over which we send/receive data to this BVC */
+	struct gprs_nsvc *nsvc;
+
+	/* BVCI used for Point-to-Point to this peer */
+	uint16_t bvci;
+
+	/* Routeing Area that this peer is part of (raw 04.08 encoding) */
+	uint8_t ra[6];
+};
+
+/* Linked list of all Gb peers (except SGSN) */
+static LLIST_HEAD(gbprox_bts_peers);
+
+/* Pointer to the SGSN peer */
+struct gbprox_peer *gbprox_peer_sgsn;
+
+/* The NS protocol stack instance we're using */
+extern struct gprs_ns_inst *gbprox_nsi;
+
+/* Find the gbprox_peer by its BVCI */
+static struct gbprox_peer *peer_by_bvci(uint16_t bvci)
+{
+	struct gbprox_peer *peer;
+	llist_for_each_entry(peer, &gbprox_bts_peers, list) {
+		if (peer->bvci == bvci)
+			return peer;
+	}
+	return NULL;
+}
+
+static struct gbprox_peer *peer_by_nsvc(struct gprs_nsvc *nsvc)
+{
+	struct gbprox_peer *peer;
+	llist_for_each_entry(peer, &gbprox_bts_peers, list) {
+		if (peer->nsvc == nsvc)
+			return peer;
+	}
+	return NULL;
+}
+
+/* look-up a peer by its Routeing Area Code (RAC) */
+static struct gbprox_peer *peer_by_rac(uint8_t *ra)
+{
+	struct gbprox_peer *peer;
+	llist_for_each_entry(peer, &gbprox_bts_peers, list) {
+		if (!memcmp(&peer->ra, ra, 6))
+			return peer;
+	}
+	return NULL;
+}
+
+/* look-up a peer by its Location Area Code (LAC) */
+static struct gbprox_peer *peer_by_lac(uint8_t *la)
+{
+	struct gbprox_peer *peer;
+	llist_for_each_entry(peer, &gbprox_bts_peers, list) {
+		if (!memcmp(&peer->ra, la, 5))
+			return peer;
+	}
+	return NULL;
+}
+
+static struct gbprox_peer *peer_alloc(uint16_t bvci)
+{
+	struct gbprox_peer *peer;
+
+	peer = talloc_zero(tall_bsc_ctx, struct gbprox_peer);
+	if (!peer)
+		return NULL;
+
+	peer->bvci = bvci;
+	llist_add(&peer->list, &gbprox_bts_peers);
+
+	return peer;
+}
+
+static void peer_free(struct gbprox_peer *peer)
+{
+	llist_del(&peer->list);
+	talloc_free(peer);
+}
+
+/* feed a message down the NS-VC associated with the specified peer */
+static int gbprox_tx2peer(struct msgb *msg, struct gbprox_peer *peer,
+			  uint16_t ns_bvci)
+{
+	msgb_bvci(msg) = ns_bvci;
+	msgb_nsei(msg) = peer->nsvc->nsei;
+
+	return gprs_ns_sendmsg(gbprox_nsi, msg);
+}
+
+/* Send a message to a peer identified by ptp_bvci but using ns_bvci
+ * in the NS hdr */
+static int gbprox_tx2bvci(struct msgb *msg, uint16_t ptp_bvci,
+			  uint16_t ns_bvci)
+{
+	struct gbprox_peer *peer;
+
+	peer = peer_by_bvci(ptp_bvci);
+	if (!peer)
+		return -ENOENT;
+
+	return gbprox_tx2peer(msg, peer, ns_bvci);
+}
+
+/* Receive an incoming signalling message from a BSS-side NS-VC */
+static int gbprox_rx_sig_from_bss(struct msgb *msg, struct gprs_nsvc *nsvc,
+				  uint16_t ns_bvci)
+{
+	struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msg->l3h;
+	struct tlv_parsed tp;
+	uint8_t pdu_type = bgph->pdu_type;
+	int data_len = msgb_l3len(msg) - sizeof(*bgph);
+	struct gbprox_peer *from_peer;
+
+	if (ns_bvci != 0) {
+		LOGP(DGPRS, LOGL_NOTICE, "BVCI %u not signalling\n", ns_bvci);
+		return -EINVAL;
+	}
+
+	/* we actually should never see those two for BVCI == 0, but double-check
+	 * just to make sure  */
+	if (pdu_type == BSSGP_PDUT_UL_UNITDATA ||
+	    pdu_type == BSSGP_PDUT_DL_UNITDATA) {
+		LOGP(DGPRS, LOGL_NOTICE, "UNITDATA not allowed in signalling\n");
+		return -EINVAL;
+	}
+
+	bssgp_tlv_parse(&tp, bgph->data, data_len);
+
+	switch (pdu_type) {
+	case BSSGP_PDUT_SUSPEND:
+	case BSSGP_PDUT_RESUME:
+		/* RAC snooping for SUSPEND/RESUME */
+		if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA))
+			goto err_mand_ie;
+		from_peer = peer_by_nsvc(nsvc);
+		if (!from_peer)
+			goto err_no_peer;
+		memcpy(&from_peer->ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA),
+			sizeof(&from_peer->ra));
+		/* FIXME: This only supports one BSS per RA */
+		break;
+	}
+
+	/* Normally, we can simply pass on all signalling messages from BSS to SGSN */
+	return gbprox_tx2peer(msg, gbprox_peer_sgsn, ns_bvci);
+err_no_peer:
+err_mand_ie:
+	/* FIXME: do something */
+	;
+}
+
+/* Receive paging request from SGSN, we need to relay to proper BSS */
+static int gbprox_rx_paging(struct msgb *msg, struct tlv_parsed *tp,
+			    struct gprs_nsvc *nsvc, uint16_t ns_bvci)
+{
+	struct gbprox_peer *peer;
+
+	if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
+		uint16_t bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
+		return gbprox_tx2bvci(msg, bvci, ns_bvci);
+	} else if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
+		peer = peer_by_rac(TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
+		return gbprox_tx2peer(msg, peer, ns_bvci);
+	} else if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) {
+		peer = peer_by_lac(TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA));
+		return gbprox_tx2peer(msg, peer, ns_bvci);
+	} else
+		return -EINVAL;
+}
+
+/* Receive an incoming signalling message from the SGSN-side NS-VC */
+static int gbprox_rx_sig_from_sgsn(struct msgb *msg, struct gprs_nsvc *nsvc,
+				   uint16_t ns_bvci)
+{
+	struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msg->l3h;
+	struct tlv_parsed tp;
+	uint8_t pdu_type = bgph->pdu_type;
+	int data_len = msgb_l3len(msg) - sizeof(*bgph);
+	struct gbprox_peer *peer;
+	uint16_t bvci;
+	int rc = 0;
+
+	if (ns_bvci != 0) {
+		LOGP(DGPRS, LOGL_NOTICE, "BVCI %u not signalling\n", ns_bvci);
+		return -EINVAL;
+	}
+
+	/* we actually should never see those two for BVCI == 0, but double-check
+	 * just to make sure  */
+	if (pdu_type == BSSGP_PDUT_UL_UNITDATA ||
+	    pdu_type == BSSGP_PDUT_DL_UNITDATA) {
+		LOGP(DGPRS, LOGL_NOTICE, "UNITDATA not allowed in signalling\n");
+		return -EINVAL;
+	}
+
+	rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
+
+	switch (pdu_type) {
+	case BSSGP_PDUT_FLUSH_LL:
+	case BSSGP_PDUT_BVC_BLOCK_ACK:
+	case BSSGP_PDUT_BVC_UNBLOCK_ACK:
+	case BSSGP_PDUT_BVC_RESET:
+	case BSSGP_PDUT_BVC_RESET_ACK:
+		/* simple case: BVCI IE is mandatory */
+		if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI))
+			goto err_mand_ie;
+		bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI));
+		rc = gbprox_tx2bvci(msg, bvci, ns_bvci);
+		break;
+	case BSSGP_PDUT_PAGING_PS:
+	case BSSGP_PDUT_PAGING_CS:
+		/* process the paging request (LAC/RAC lookup) */
+		rc = gbprox_rx_paging(msg, &tp, nsvc, ns_bvci);
+		break;
+	case BSSGP_PDUT_STATUS:
+		/* FIXME: Some exception has occurred */
+		LOGP(DGPRS, LOGL_NOTICE, "STATUS not implemented yet\n");
+		break;
+	/* those only exist in the SGSN -> BSS direction */
+	case BSSGP_PDUT_SUSPEND_ACK:
+	case BSSGP_PDUT_SUSPEND_NACK:
+	case BSSGP_PDUT_RESUME_ACK:
+	case BSSGP_PDUT_RESUME_NACK:
+		/* RAC IE is mandatory */
+		if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA))
+			goto err_mand_ie;
+		peer = peer_by_rac(TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA));
+		if (!peer)
+			goto err_no_peer;
+		rc = gbprox_tx2peer(msg, peer, ns_bvci);
+		break;
+	case BSSGP_PDUT_SGSN_INVOKE_TRACE:
+		LOGP(DGPRS, LOGL_ERROR, "SGSN INVOKE TRACE not supported\n");
+		break;
+	default:
+		DEBUGP(DGPRS, "BSSGP PDU type 0x%02x unknown\n", pdu_type);
+		break;
+	}
+
+	return rc;
+err_mand_ie:
+	; /* FIXME: this would pull gprs_bssgp.c in, which in turn has dependencies */
+	//return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
+err_no_peer:
+	; /* FIXME */
+}
+
+/* Main input function for Gb proxy */
+int gbprox_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci)
+{
+	struct gbprox_peer *peer;
+
+	/* Only BVCI=0 messages need special treatment */
+	if (ns_bvci == 0 || ns_bvci == 1) {
+		if (nsvc->remote_end_is_sgsn)
+			return gbprox_rx_sig_from_sgsn(msg, nsvc, ns_bvci);
+		else
+			return gbprox_rx_sig_from_bss(msg, nsvc, ns_bvci);
+	}
+
+	/* All other BVCI are PTP and thus can be simply forwarded */
+	peer = peer_by_bvci(ns_bvci);
+	if (!peer) {
+		LOGP(DGPRS, LOGL_ERROR, "Couldn't find peer for BVCI %u\n", ns_bvci);
+		return -EIO;
+	}
+
+	return gbprox_tx2peer(msg, peer, ns_bvci);
+}
diff --git a/openbsc/src/gb_proxy_main.c b/openbsc/src/gb_proxy_main.c
new file mode 100644
index 0000000..c624cf8
--- /dev/null
+++ b/openbsc/src/gb_proxy_main.c
@@ -0,0 +1,140 @@
+/* NS-over-IP proxy */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by On Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+
+#include <openbsc/signal.h>
+#include <openbsc/debug.h>
+#include <openbsc/gprs_ns.h>
+#include <openbsc/telnet_interface.h>
+#include <openbsc/vty.h>
+
+#include "../bscconfig.h"
+
+/* this is here for the vty... it will never be called */
+void subscr_put() { abort(); }
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+void *tall_bsc_ctx;
+
+struct gprs_ns_inst *gbprox_nsi;
+static u_int16_t nsip_listen_port;
+
+const char *openbsc_version = "Osmocom NSIP Proxy " PACKAGE_VERSION;
+const char *openbsc_copyright =
+	"Copyright (C) 2010 Harald Welte and On-Waves\n"
+	"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\n"
+	"Dieter Spaar, Andreas Eversberg, Holger Freyther\n\n"
+	"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
+	"This is free software: you are free to change and redistribute it.\n"
+	"There is NO WARRANTY, to the extent permitted by law.\n";
+
+static char *config_file = "nsip_proxy.cfg";
+
+/* Pointer to the SGSN peer */
+extern struct gbprox_peer *gbprox_peer_sgsn;
+
+/* call-back function for the NS protocol */
+static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
+		      struct msgb *msg, u_int16_t bvci)
+{
+	int rc = 0;
+
+	switch (event) {
+	case GPRS_NS_EVT_UNIT_DATA:
+		rc = gbprox_rcvmsg(msg, nsvc, bvci);
+		break;
+	default:
+		LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
+		if (msg)
+			talloc_free(msg);
+		rc = -EIO;
+		break;
+	}
+	return rc;
+}
+
+
+int main(int argc, char **argv)
+{
+	struct gsm_network dummy_network;
+	struct log_target *stderr_target;
+	struct sockaddr_in sin;
+	int rc;
+
+	tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy");
+
+	log_init(&log_info);
+	stderr_target = log_target_create_stderr();
+	log_add_target(stderr_target);
+	log_set_all_filter(stderr_target, 1);
+
+	telnet_init(&dummy_network, 4244);
+
+	gbprox_nsi = gprs_ns_instantiate(&proxy_ns_cb);
+	if (!gbprox_nsi) {
+		LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
+		exit(1);
+	}
+	nsip_listen(gbprox_nsi, nsip_listen_port);
+
+	/* 'establish' the outgoing connection to the SGSN */
+	sin.sin_port = ntohs(23000);
+	inet_aton("192.168.100.239", &sin.sin_addr);
+	gbprox_peer_sgsn = nsip_connect(gbprox_nsi, &sin, 2342);
+
+	while (1) {
+		rc = bsc_select_main(0);
+		if (rc < 0)
+			exit(3);
+	}
+
+	exit(0);
+}
+
+struct gsm_network;
+int bsc_vty_init(struct gsm_network *dummy)
+{
+	cmd_init(1);
+	vty_init();
+
+	openbsc_vty_add_cmds();
+        //mgcp_vty_init();
+	return 0;
+}
+
diff --git a/openbsc/src/gprs_bssgp.c b/openbsc/src/gprs_bssgp.c
index 650d7d4..a2181b1 100644
--- a/openbsc/src/gprs_bssgp.c
+++ b/openbsc/src/gprs_bssgp.c
@@ -74,11 +74,6 @@
 	return "undefined";
 }
 
-static inline int bssgp_tlv_parse(struct tlv_parsed *tp, u_int8_t *buf, int len)
-{
-	return tlv_parse(tp, &tvlv_att_def, buf, len, 0, 0);
-}
-
 static inline struct msgb *bssgp_msgb_alloc(void)
 {
 	return msgb_alloc_headroom(4096, 128, "BSSGP");
@@ -120,7 +115,7 @@
 }
 
 /* Chapter 10.4.14: Status */
-static int bssgp_tx_status(u_int8_t cause, u_int16_t *bvci, struct msgb *orig_msg)
+int bssgp_tx_status(u_int8_t cause, u_int16_t *bvci, struct msgb *orig_msg)
 {
 	struct msgb *msg = bssgp_msgb_alloc();
 	struct bssgp_normal_hdr *bgph =
diff --git a/openbsc/src/gprs_ns.c b/openbsc/src/gprs_ns.c
index 6c495b0..3bb0bf9 100644
--- a/openbsc/src/gprs_ns.c
+++ b/openbsc/src/gprs_ns.c
@@ -72,30 +72,6 @@
 	},
 };
 
-#define NSE_S_BLOCKED	0x0001
-#define NSE_S_ALIVE	0x0002
-
-struct gprs_nsvc {
-	struct llist_head list;
-	struct gprs_ns_inst *nsi;
-
-	u_int16_t nsei;		/* end-to-end significance */
-	u_int16_t nsvci;	/* uniquely identifies NS-VC at SGSN */
-
-	u_int32_t state;
-	u_int32_t remote_state;
-
-	struct timer_list alive_timer;
-	int timer_is_tns_alive;
-	int alive_retries;
-
-	union {
-		struct {
-			struct sockaddr_in bts_addr;
-		} ip;
-	};
-};
-
 enum gprs_ns_ll {
 	GPRS_NS_LL_UDP,
 	GPRS_NS_LL_E1,
@@ -474,7 +450,7 @@
 	nsi->cb = cb;
 	INIT_LLIST_HEAD(&nsi->gprs_nsvcs);
 
-	return NULL;
+	return nsi;
 }
 
 void gprs_ns_destroy(struct gprs_ns_inst *nsi)
@@ -586,3 +562,24 @@
 
 	return ret;
 }
+
+/* Establish a connection (from the BSS) to the SGSN */
+struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi,
+				struct sockaddr_in *dest, uint16_t nsvci)
+{
+	struct gprs_nsvc *nsvc;
+
+	nsvc = nsvc_by_rem_addr(nsi, dest);
+	if (!nsvc) {
+		nsvc = nsvc_create(nsi, nsvci);
+		nsvc->ip.bts_addr = *dest;
+	}
+	nsvc->remote_end_is_sgsn = 1;
+
+	/* Initiate a RESET procedure */
+	if (gprs_ns_tx_simple(nsvc, NS_PDUT_RESET) < 0)
+		return NULL;
+	/* FIXME: should we run a timer and re-transmit the reset request? */
+
+	return nsvc;
+}
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
index 2b5bf21..721cadd 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -707,58 +707,6 @@
 	return 0;
 }
 
-int make_sock(struct bsc_fd *bfd, int proto, u_int16_t port,
-	      int (*cb)(struct bsc_fd *fd, unsigned int what))
-{
-	struct sockaddr_in addr;
-	int ret, on = 1;
-	int type = SOCK_STREAM;
-
-	if (proto == IPPROTO_UDP)
-		type = SOCK_DGRAM;
-	
-	bfd->fd = socket(AF_INET, type, proto);
-	bfd->cb = cb;
-	bfd->when = BSC_FD_READ;
-	//bfd->data = line;
-
-	if (bfd->fd < 0) {
-		LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n");
-		return -EIO;
-	}
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(port);
-	addr.sin_addr.s_addr = INADDR_ANY;
-
-	setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
-	ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
-	if (ret < 0) {
-		LOGP(DINP, LOGL_ERROR, "could not bind l2 socket %s\n",
-			strerror(errno));
-		close(bfd->fd);
-		return -EIO;
-	}
-
-	if (proto != IPPROTO_UDP) {
-		ret = listen(bfd->fd, 1);
-		if (ret < 0) {
-			perror("listen");
-			return ret;
-		}
-	}
-	
-	ret = bsc_register_fd(bfd);
-	if (ret < 0) {
-		perror("register_listen_fd");
-		close(bfd->fd);
-		return ret;
-	}
-	return 0;
-}
-
 /* Actively connect to a BTS.  Currently used by ipaccess-config.c */
 int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
 {
diff --git a/openbsc/src/socket.c b/openbsc/src/socket.c
new file mode 100644
index 0000000..3ed4d42
--- /dev/null
+++ b/openbsc/src/socket.c
@@ -0,0 +1,94 @@
+/* OpenBSC sokcet code, taken from Abis input driver for ip.access */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Holger Hans Peter Freyther
+ * (C) 2010 by On-Waves
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include <osmocore/select.h>
+#include <osmocore/tlv.h>
+#include <osmocore/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <osmocore/talloc.h>
+
+int make_sock(struct bsc_fd *bfd, int proto, u_int16_t port,
+	      int (*cb)(struct bsc_fd *fd, unsigned int what))
+{
+	struct sockaddr_in addr;
+	int ret, on = 1;
+	int type = SOCK_STREAM;
+
+	if (proto == IPPROTO_UDP)
+		type = SOCK_DGRAM;
+
+	bfd->fd = socket(AF_INET, type, proto);
+	bfd->cb = cb;
+	bfd->when = BSC_FD_READ;
+	//bfd->data = line;
+
+	if (bfd->fd < 0) {
+		LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n");
+		return -EIO;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	addr.sin_addr.s_addr = INADDR_ANY;
+
+	setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+	ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
+	if (ret < 0) {
+		LOGP(DINP, LOGL_ERROR, "could not bind l2 socket %s\n",
+			strerror(errno));
+		close(bfd->fd);
+		return -EIO;
+	}
+
+	if (proto != IPPROTO_UDP) {
+		ret = listen(bfd->fd, 1);
+		if (ret < 0) {
+			perror("listen");
+			return ret;
+		}
+	}
+
+	ret = bsc_register_fd(bfd);
+	if (ret < 0) {
+		perror("register_listen_fd");
+		close(bfd->fd);
+		return ret;
+	}
+	return 0;
+}