ns2: add support for frame relay

Add support for frame relay over dahdi hdlc device.
It's supporting lmi by q933 and supports both
SGSN and BSS.

Change-Id: Id3b49f93d33c271f77cd9c9db03cde6b727a4d30
diff --git a/src/gb/gprs_ns2_vty.c b/src/gb/gprs_ns2_vty.c
index 63331b9..4eb9d37 100644
--- a/src/gb/gprs_ns2_vty.c
+++ b/src/gb/gprs_ns2_vty.c
@@ -31,6 +31,7 @@
 #include <stdint.h>
 
 #include <arpa/inet.h>
+#include <net/if.h>
 
 #include <osmocom/core/msgb.h>
 #include <osmocom/core/byteswap.h>
@@ -42,6 +43,7 @@
 #include <osmocom/core/sockaddr_str.h>
 #include <osmocom/core/linuxlist.h>
 #include <osmocom/core/socket.h>
+#include <osmocom/gprs/frame_relay.h>
 #include <osmocom/gprs/gprs_ns2.h>
 #include <osmocom/gsm/tlv.h>
 #include <osmocom/vty/vty.h>
@@ -78,12 +80,19 @@
 	uint16_t nsvci;
 	uint16_t frdlci;
 
+	struct {
+		enum osmo_fr_role role;
+	} fr;
+
+	char netif[IF_NAMESIZE];
+
 	bool remote_end_is_sgsn;
 	bool configured;
 };
 
 static struct gprs_ns2_inst *vty_nsi = NULL;
 static struct ns2_vty_priv priv;
+static struct osmo_fr_network *vty_fr_network = NULL;
 
 /* FIXME: this should go to some common file as it is copied
  * in vty_interface.c of the BSC */
@@ -223,6 +232,11 @@
 				vtyvc->nsei, vtyvc->frdlci,
 				VTY_NEWLINE);
 			break;
+		case GPRS_NS_LL_FR:
+			vty_out(vty, " nse %u fr %s dlci %u%s",
+				vtyvc->nsei, vtyvc->netif, vtyvc->frdlci,
+				VTY_NEWLINE);
+			break;
 		default:
 			break;
 		}
@@ -387,6 +401,44 @@
 
 #define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n"
 
+DEFUN(cfg_nse_fr, cfg_nse_fr_cmd,
+	"nse <0-65535> nsvci <0-65535> (fr|frnet) NETIF dlci <0-1023>",
+	NSE_CMD_STR
+	"NS Virtual Connection\n"
+	"NS Virtual Connection ID (NSVCI)\n"
+	"frame relay\n"
+	IFNAME_STR
+	"Data Link connection identifier\n"
+	"Data Link connection identifier\n"
+	)
+{
+	struct ns2_vty_vc *vtyvc;
+
+	uint16_t nsei = atoi(argv[0]);
+	uint16_t nsvci = atoi(argv[1]);
+	const char *role = argv[2];
+	const char *name = argv[3];
+	uint16_t dlci = atoi(argv[4]);
+
+	vtyvc = vtyvc_by_nsei(nsei, true);
+	if (!vtyvc) {
+		vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	if (!strcmp(role, "fr"))
+		vtyvc->fr.role = FR_ROLE_USER_EQUIPMENT;
+	else if (!strcmp(role, "frnet"))
+		vtyvc->fr.role = FR_ROLE_NETWORK_EQUIPMENT;
+
+	osmo_strlcpy(vtyvc->netif, name, sizeof(vtyvc->netif));
+	vtyvc->frdlci = dlci;
+	vtyvc->nsvci = nsvci;
+	vtyvc->ll = GPRS_NS_LL_FR;
+
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
 	"nse <0-65535> nsvci <0-65535>",
 	NSE_CMD_STR
@@ -453,13 +505,14 @@
 }
 
 DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd,
-	"nse <0-65535> fr-dlci <16-1007>",
+	"nse <0-65535> nsvci <0-65535> fr-dlci <16-1007>",
 	NSE_CMD_STR
 	"Frame Relay DLCI\n"
 	"Frame Relay DLCI Number\n")
 {
 	uint16_t nsei = atoi(argv[0]);
-	uint16_t dlci = atoi(argv[1]);
+	uint16_t nsvci = atoi(argv[1]);
+	uint16_t dlci = atoi(argv[2]);
 	struct ns2_vty_vc *vtyvc;
 
 	vtyvc = vtyvc_by_nsei(nsei, true);
@@ -474,6 +527,7 @@
 	}
 
 	vtyvc->frdlci = dlci;
+	vtyvc->nsvci = nsvci;
 
 	return CMD_SUCCESS;
 }
@@ -736,6 +790,7 @@
 
 	install_lib_element(CONFIG_NODE, &cfg_ns_cmd);
 	install_node(&ns_node, config_write_ns);
+	install_lib_element(L_NS_NODE, &cfg_nse_fr_cmd);
 	install_lib_element(L_NS_NODE, &cfg_nse_nsvci_cmd);
 	install_lib_element(L_NS_NODE, &cfg_nse_remoteip_cmd);
 	install_lib_element(L_NS_NODE, &cfg_nse_remoteport_cmd);
@@ -763,10 +818,11 @@
  */
 int gprs_ns2_vty_create() {
 	struct ns2_vty_vc *vtyvc;
-	struct gprs_ns2_vc_bind *bind;
+	struct gprs_ns2_vc_bind *bind, *fr;
 	struct gprs_ns2_nse *nse;
 	struct gprs_ns2_vc *nsvc;
 	struct osmo_sockaddr sockaddr;
+	int rc = 0;
 
 	if (!vty_nsi)
 		return -1;
@@ -787,18 +843,28 @@
 
 	/* create vcs */
 	llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
-		if (strlen(vtyvc->remote.ip) == 0) {
-			/* Invalid IP for VC */
-			continue;
-		}
+		/* validate settings */
+		switch (vtyvc->ll) {
+		case GPRS_NS_LL_UDP:
+			if (strlen(vtyvc->remote.ip) == 0) {
+				/* Invalid IP for VC */
+				continue;
+			}
 
-		if (!vtyvc->remote.port) {
-			/* Invalid port for VC */
-			continue;
-		}
+			if (!vtyvc->remote.port) {
+				/* Invalid port for VC */
+				continue;
+			}
 
-		if (osmo_sockaddr_str_to_sockaddr(&vtyvc->remote, &sockaddr.u.sas)) {
-			/* Invalid sockaddr for VC */
+			if (osmo_sockaddr_str_to_sockaddr(&vtyvc->remote, &sockaddr.u.sas)) {
+				/* Invalid sockaddr for VC */
+				continue;
+			}
+			break;
+		case GPRS_NS_LL_FR:
+			break;
+		case GPRS_NS_LL_FR_GRE:
+		case GPRS_NS_LL_E1:
 			continue;
 		}
 
@@ -812,15 +878,46 @@
 		}
 		nse->persistent = true;
 
-		nsvc = gprs_ns2_ip_connect(bind,
-					   &sockaddr,
-					   nse,
-					   vtyvc->nsvci);
-		if (!nsvc) {
-			/* Could not create NSVC, connect failed */
+		switch (vtyvc->ll) {
+		case GPRS_NS_LL_UDP:
+			nsvc = gprs_ns2_ip_connect(bind,
+						   &sockaddr,
+						   nse,
+						   vtyvc->nsvci);
+			if (!nsvc) {
+				/* Could not create NSVC, connect failed */
+				continue;
+			}
+			nsvc->persistent = true;
+			break;
+		case GPRS_NS_LL_FR: {
+			if (vty_fr_network == NULL) {
+				/* TODO: add a switch for BSS/SGSN/gbproxy */
+				vty_fr_network = osmo_fr_network_alloc(vty_nsi);
+			}
+			fr = gprs_ns2_fr_bind_by_netif(
+						vty_nsi,
+						vtyvc->netif);
+			if (!fr) {
+				rc = gprs_ns2_fr_bind(vty_nsi, vtyvc->netif, vty_fr_network, vtyvc->fr.role, &fr);
+				if (rc < 0) {
+					LOGP(DLNS, LOGL_ERROR, "Can not create fr bind on device %s err: %d\n", vtyvc->netif, rc);
+					return rc;
+				}
+			}
+
+			nsvc = gprs_ns2_fr_connect(fr, vtyvc->nsei, vtyvc->nsvci, vtyvc->frdlci);
+			if (!nsvc) {
+				/* Could not create NSVC, connect failed */
+				continue;
+			}
+			nsvc->persistent = true;
+			break;
+		}
+		case GPRS_NS_LL_FR_GRE:
+		case GPRS_NS_LL_E1:
 			continue;
 		}
-		nsvc->persistent = true;
 	}