summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openbsc/include/openbsc/gprs_ns.h62
-rw-r--r--openbsc/src/gprs_ns.c253
2 files changed, 284 insertions, 31 deletions
diff --git a/openbsc/include/openbsc/gprs_ns.h b/openbsc/include/openbsc/gprs_ns.h
index 9ea4d675..98b31f8d 100644
--- a/openbsc/include/openbsc/gprs_ns.h
+++ b/openbsc/include/openbsc/gprs_ns.h
@@ -1,6 +1,10 @@
#ifndef _GPRS_NS_H
#define _GPRS_NS_H
+/* GPRS Networks Service (NS) messages on the Gb interface
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * 3GPP TS 48.016 version 6.5.0 Release 6 / ETSI TS 148 016 V6.5.0 (2005-11) */
+
struct gprs_ns_hdr {
u_int8_t pdu_type;
u_int8_t data[0];
@@ -18,6 +22,15 @@ enum ns_pdu_type {
NS_PDUT_STATUS = 0x08,
NS_PDUT_ALIVE = 0x0a,
NS_PDUT_ALIVE_ACK = 0x0b,
+ /* TS 48.016 Section 10.3.7, Table 10.3.7.1 */
+ SNS_PDUT_ACK = 0x0c,
+ SNS_PDUT_ADD = 0x0d,
+ SNS_PDUT_CHANGE_WEIGHT = 0x0e,
+ SNS_PDUT_CONFIG = 0x0f,
+ SNS_PDUT_CONFIG_ACK = 0x10,
+ SNS_PDUT_DELETE = 0x11,
+ SNS_PDUT_SIZE = 0x12,
+ SNS_PDUT_SIZE_ACK = 0x13,
};
/* TS 08.16, Section 10.3, Table 12 */
@@ -27,6 +40,14 @@ enum ns_ctrl_ie {
NS_IE_PDU = 0x02,
NS_IE_BVCI = 0x03,
NS_IE_NSEI = 0x04,
+ /* TS 48.016 Section 10.3, Table 10.3.1 */
+ NS_IE_IPv4_LIST = 0x05,
+ NS_IE_IPv6_LIST = 0x06,
+ NS_IE_MAX_NR_NSVC = 0x07,
+ NS_IE_IPv4_EP_NR = 0x08,
+ NS_IE_IPv6_EP_NR = 0x09,
+ NS_IE_RESET_FLAG = 0x0a,
+ NS_IE_IP_ADDR = 0x0b,
};
/* TS 08.16, Section 10.3.2, Table 13 */
@@ -42,20 +63,43 @@ enum ns_cause {
NS_CAUSE_PROTO_ERR_UNSPEC = 0x0b,
NS_CAUSE_INVAL_ESSENT_IE = 0x0c,
NS_CAUSE_MISSING_ESSENT_IE = 0x0d,
+ /* TS 48.016 Section 10.3.2, Table 10.3.2.1 */
+ NS_CAUSE_INVAL_NR_IPv4_EP = 0x0e,
+ NS_CAUSE_INVAL_NR_IPv6_EP = 0x0f,
+ NS_CAUSE_INVAL_NR_NS_VC = 0x10,
+ NS_CAUSE_INVAL_WEIGH = 0x11,
+ NS_CAUSE_UNKN_IP_EP = 0x12,
+ NS_CAUSE_UNKN_IP_ADDR = 0x13,
+ NS_CAUSE_UNKN_IP_TEST_FAILED = 0x14,
};
-/* a layer 1 entity transporting NS frames */
-struct gprs_ns_link {
- union {
- struct {
- int fd;
- } ip;
- };
+struct gprs_nsvc;
+struct gprs_ns_inst;
+
+enum gprs_ns_evt {
+ GPRS_NS_EVT_UNIT_DATA,
};
+typedef int gprs_ns_cb_t(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
+ struct msgb *msg, u_int16_t bvci);
+
+/* Create a new NS protocol instance */
+struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb);
+
+/* Destroy a NS protocol instance */
+void gprs_ns_destroy(struct gprs_ns_inst *nsi);
-int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr);
+/* Listen for incoming GPRS packets */
+int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port);
-int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci,
+struct sockaddr_in;
+
+/* main entry point, here incoming NS frames enter */
+int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
+ struct sockaddr_in *saddr);
+
+/* main function for higher layers (BSSGP) to send NS messages */
+int gprs_ns_sendmsg(struct gprs_nsvc *nsvc, u_int16_t bvci,
struct msgb *msg);
+
#endif
diff --git a/openbsc/src/gprs_ns.c b/openbsc/src/gprs_ns.c
index 6fb7d1d9..c5166fa5 100644
--- a/openbsc/src/gprs_ns.c
+++ b/openbsc/src/gprs_ns.c
@@ -1,7 +1,7 @@
/* GPRS Networks Service (NS) messages on the Gb interface
* 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -55,6 +55,7 @@
#include <osmocore/msgb.h>
#include <osmocore/tlv.h>
#include <osmocore/talloc.h>
+#include <osmocore/select.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_ns.h>
#include <openbsc/gprs_bssgp.h>
@@ -76,11 +77,13 @@ static const struct tlv_definition ns_att_tlvdef = {
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;
@@ -93,8 +96,67 @@ struct gprs_nsvc {
};
};
-/* FIXME: dynamically search for the matching NSVC */
-static struct gprs_nsvc dummy_nsvc = { .state = NSE_S_BLOCKED | NSE_S_ALIVE };
+enum gprs_ns_ll {
+ GPRS_NS_LL_UDP,
+ GPRS_NS_LL_E1,
+};
+
+/* An instance of the NS protocol stack */
+struct gprs_ns_inst {
+ /* callback to the user for incoming UNIT DATA IND */
+ gprs_ns_cb_t *cb;
+
+ /* linked lists of all NSVC in this instance */
+ struct llist_head gprs_nsvcs;
+
+ /* which link-layer are we based on? */
+ enum gprs_ns_ll ll;
+
+ union {
+ /* NS-over-IP specific bits */
+ struct {
+ struct bsc_fd fd;
+ } nsip;
+ };
+};
+
+/* Lookup struct gprs_nsvc based on NSVCI */
+static struct gprs_nsvc *nsvc_by_nsvci(struct gprs_ns_inst *nsi,
+ u_int16_t nsvci)
+{
+ struct gprs_nsvc *nsvc;
+ llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
+ if (nsvc->nsvci == nsvci)
+ return nsvc;
+ }
+ return NULL;
+}
+
+/* Lookup struct gprs_nsvc based on remote peer socket addr */
+static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi,
+ struct sockaddr_in *sin)
+{
+ struct gprs_nsvc *nsvc;
+ llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
+ if (!memcmp(&nsvc->ip.bts_addr, sin, sizeof(*sin)))
+ return nsvc;
+ }
+ return NULL;
+}
+
+static struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, u_int16_t nsvci)
+{
+ struct gprs_nsvc *nsvc;
+
+ nsvc = talloc_zero(nsi, struct gprs_nsvc);
+ nsvc->nsvci = nsvci;
+ /* before RESET procedure: BLOCKED and DEAD */
+ nsvc->state = NSE_S_BLOCKED;
+ nsvc->nsi = nsi;
+ llist_add(&nsvc->list, &nsi->gprs_nsvcs);
+
+ return nsvc;
+}
/* Section 10.3.2, Table 13 */
static const char *ns_cause_str[] = {
@@ -122,9 +184,23 @@ static const char *gprs_ns_cause_str(enum ns_cause cause)
return "undefined";
}
+static int nsip_sendmsg(struct msgb *msg, struct gprs_nsvc *nsvc);
+
static int gprs_ns_tx(struct msgb *msg, struct gprs_nsvc *nsvc)
{
- return ipac_gprs_send(msg, &nsvc->ip.bts_addr);
+ int ret;
+
+ switch (nsvc->nsi->ll) {
+ case GPRS_NS_LL_UDP:
+ ret = nsip_sendmsg(msg, nsvc);
+ break;
+ default:
+ LOGP(DGPRS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->nsi->ll);
+ msgb_free(msg);
+ ret = -EIO;
+ break;
+ }
+ return ret;
}
static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, u_int8_t pdu_type)
@@ -196,11 +272,10 @@ static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc)
}
/* Section 9.2.10: transmit side */
-int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci,
+int gprs_ns_sendmsg(struct gprs_nsvc *nsvc, u_int16_t bvci,
struct msgb *msg)
{
struct gprs_ns_hdr *nsh;
- struct gprs_nsvc *nsvc = &dummy_nsvc;
nsh = (struct gprs_ns_hdr *) msgb_push(msg, sizeof(*nsh) + 3);
if (!nsh) {
@@ -217,7 +292,7 @@ int gprs_ns_sendmsg(struct gprs_ns_link *link, u_int16_t bvci,
}
/* Section 9.2.10: receive side */
-static int gprs_ns_rx_unitdata(struct msgb *msg)
+static int gprs_ns_rx_unitdata(struct msgb *msg, struct gprs_nsvc *nsvc)
{
struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h;
u_int16_t bvci;
@@ -227,13 +302,13 @@ static int gprs_ns_rx_unitdata(struct msgb *msg)
msg->l3h = &nsh->data[3];
/* call upper layer (BSSGP) */
- return gprs_bssgp_rcvmsg(msg, bvci);
+ return nsvc->nsi->cb(GPRS_NS_EVT_UNIT_DATA, nsvc, msg, bvci);
}
/* Section 9.2.7 */
-static int gprs_ns_rx_status(struct msgb *msg)
+static int gprs_ns_rx_status(struct msgb *msg, struct gprs_nsvc *nsvc)
{
- struct gprs_ns_hdr *nsh = msg->l2h;
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
struct tlv_parsed tp;
u_int8_t cause;
int rc;
@@ -254,10 +329,9 @@ static int gprs_ns_rx_status(struct msgb *msg)
}
/* Section 7.3 */
-static int gprs_ns_rx_reset(struct msgb *msg)
+static int gprs_ns_rx_reset(struct msgb *msg, struct gprs_nsvc *nsvc)
{
struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
- struct gprs_nsvc *nsvc = &dummy_nsvc;
struct tlv_parsed tp;
u_int8_t *cause;
u_int16_t *nsvci, *nsei;
@@ -296,14 +370,24 @@ static int gprs_ns_rx_reset(struct msgb *msg)
}
/* main entry point, here incoming NS frames enter */
-int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr)
+int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
+ struct sockaddr_in *saddr)
{
struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
- struct gprs_nsvc *nsvc = &dummy_nsvc;
+ struct gprs_nsvc *nsvc;
int rc = 0;
- /* FIXME: do this properly! */
- nsvc->ip.bts_addr = *saddr;
+ /* look up the NSVC based on source address */
+ nsvc = nsvc_by_rem_addr(nsi, saddr);
+ if (!nsvc) {
+ /* Only the RESET procedure creates a new NSVC */
+ if (nsh->pdu_type != NS_PDUT_RESET)
+ return -EIO;
+ nsvc = nsvc_create(nsi, 0xffff);
+ nsvc->ip.bts_addr = *saddr;
+ rc = gprs_ns_rx_reset(msg, nsvc);
+ return rc;
+ }
switch (nsh->pdu_type) {
case NS_PDUT_ALIVE:
@@ -320,16 +404,18 @@ int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr)
break;
case NS_PDUT_UNITDATA:
/* actual user data */
- rc = gprs_ns_rx_unitdata(msg);
+ rc = gprs_ns_rx_unitdata(msg, nsvc);
break;
case NS_PDUT_STATUS:
- rc = gprs_ns_rx_status(msg);
+ rc = gprs_ns_rx_status(msg, nsvc);
break;
case NS_PDUT_RESET:
- rc = gprs_ns_rx_reset(msg);
+ rc = gprs_ns_rx_reset(msg, nsvc);
break;
case NS_PDUT_RESET_ACK:
- /* FIXME: mark remote NS-VC as blocked + active */
+ DEBUGP(DGPRS, "NS RESET ACK\n");
+ /* mark remote NS-VC as blocked + active */
+ nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE;
break;
case NS_PDUT_UNBLOCK:
/* Section 7.2: unblocking procedure */
@@ -338,7 +424,9 @@ int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr)
rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK);
break;
case NS_PDUT_UNBLOCK_ACK:
- /* FIXME: mark remote NS-VC as unblocked + active */
+ DEBUGP(DGPRS, "NS UNBLOCK ACK\n");
+ /* mark remote NS-VC as unblocked + active */
+ nsvc->remote_state = NSE_S_ALIVE;
break;
case NS_PDUT_BLOCK:
DEBUGP(DGPRS, "NS BLOCK\n");
@@ -346,7 +434,9 @@ int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr)
rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK);
break;
case NS_PDUT_BLOCK_ACK:
- /* FIXME: mark remote NS-VC as blocked + active */
+ DEBUGP(DGPRS, "NS BLOCK ACK\n");
+ /* mark remote NS-VC as blocked + active */
+ nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE;
break;
default:
DEBUGP(DGPRS, "Unknown NS PDU type 0x%02x\n", nsh->pdu_type);
@@ -356,3 +446,122 @@ int gprs_ns_rcvmsg(struct msgb *msg, struct sockaddr_in *saddr)
return rc;
}
+struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb)
+{
+ struct gprs_ns_inst *nsi = talloc_zero(tall_bsc_ctx, struct gprs_ns_inst);
+
+ nsi->cb = cb;
+ INIT_LLIST_HEAD(&nsi->gprs_nsvcs);
+
+ return NULL;
+}
+
+void gprs_ns_destroy(struct gprs_ns_inst *nsi)
+{
+ /* FIXME: clear all timers */
+
+ /* recursively free the NSI and all its NSVCs */
+ talloc_free(nsi);
+}
+
+
+/* NS-over-IP code, according to 3GPP TS 48.016 Chapter 6.2
+ * We don't support Size Procedure, Configuration Procedure, ChangeWeight Procedure */
+
+/* Read a single NS-over-IP message */
+static struct msgb *read_nsip_msg(struct bsc_fd *bfd, int *error,
+ struct sockaddr_in *saddr)
+{
+ struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Abis/IP/GPRS-NS");
+ int ret = 0;
+ socklen_t saddr_len = sizeof(*saddr);
+
+ if (!msg) {
+ *error = -ENOMEM;
+ return NULL;
+ }
+
+ ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
+ (struct sockaddr *)saddr, &saddr_len);
+ if (ret < 0) {
+ fprintf(stderr, "recv error %s\n", strerror(errno));
+ msgb_free(msg);
+ *error = ret;
+ return NULL;
+ } else if (ret == 0) {
+ msgb_free(msg);
+ *error = ret;
+ return NULL;
+ }
+
+ msg->l2h = msg->data;
+ msgb_put(msg, ret);
+
+ return msg;
+}
+
+static int handle_nsip_read(struct bsc_fd *bfd)
+{
+ int error;
+ struct sockaddr_in saddr;
+ struct gprs_ns_inst *nsi = bfd->data;
+ struct msgb *msg = read_nsip_msg(bfd, &error, &saddr);
+
+ if (!msg)
+ return error;
+
+ return gprs_ns_rcvmsg(nsi, msg, &saddr);
+}
+
+static int handle_nsip_write(struct bsc_fd *bfd)
+{
+ /* FIXME: actually send the data here instead of nsip_sendmsg() */
+ return -EIO;
+}
+
+int nsip_sendmsg(struct msgb *msg, struct gprs_nsvc *nsvc)
+{
+ int rc;
+ struct gprs_ns_inst *nsi = nsvc->nsi;
+ struct sockaddr_in *daddr = &nsvc->ip.bts_addr;
+
+ rc = sendto(nsi->nsip.fd.fd, msg->data, msg->len, 0,
+ (struct sockaddr *)daddr, sizeof(*daddr));
+
+ talloc_free(msg);
+
+ return rc;
+}
+
+/* UDP Port 23000 carries the LLC-in-BSSGP-in-NS protocol stack */
+static int nsip_fd_cb(struct bsc_fd *bfd, unsigned int what)
+{
+ int rc = 0;
+
+ if (what & BSC_FD_READ)
+ rc = handle_nsip_read(bfd);
+ if (what & BSC_FD_WRITE)
+ rc = handle_nsip_write(bfd);
+
+ return rc;
+}
+
+
+/* FIXME: this is currently in input/ipaccess.c */
+extern int make_sock(struct bsc_fd *bfd, int proto, u_int16_t port,
+ int (*cb)(struct bsc_fd *fd, unsigned int what));
+
+/* Listen for incoming GPRS packets */
+int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port)
+{
+ int ret;
+
+ ret = make_sock(&nsi->nsip.fd, IPPROTO_UDP, udp_port, nsip_fd_cb);
+ if (ret < 0)
+ return ret;
+
+ nsi->ll = GPRS_NS_LL_UDP;
+ nsi->nsip.fd.data = nsi;
+
+ return ret;
+}