summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/input/Makefile.am1
-rw-r--r--src/input/ipa.c234
-rw-r--r--src/input/ipaccess.c29
3 files changed, 260 insertions, 4 deletions
diff --git a/src/input/Makefile.am b/src/input/Makefile.am
index 8e9e336..c382269 100644
--- a/src/input/Makefile.am
+++ b/src/input/Makefile.am
@@ -6,6 +6,7 @@ AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS)
libosmoabis_input_la_SOURCES = dahdi.c \
hsl.c \
+ ipa.c \
ipaccess.c \
lapd.c \
misdn.c
diff --git a/src/input/ipa.c b/src/input/ipa.c
new file mode 100644
index 0000000..6f9639f
--- /dev/null
+++ b/src/input/ipa.c
@@ -0,0 +1,234 @@
+#include "internal.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <talloc.h>
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/abis/ipaccess.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/abis/logging.h>
+
+#include <osmocom/abis/ipa.h>
+
+#define IPA_ALLOC_SIZE 1200
+
+static struct msgb *ipa_msg_alloc(void)
+{
+ return msgb_alloc(IPA_ALLOC_SIZE, "Abis/IP");
+}
+
+int ipa_msg_recv(int fd, struct msgb **rmsg)
+{
+ struct msgb *msg;
+ struct ipaccess_head *hh;
+ int len, ret;
+
+ msg = ipa_msg_alloc();
+ if (msg == NULL)
+ return -ENOMEM;
+
+ /* first read our 3-byte header */
+ hh = (struct ipaccess_head *) msg->data;
+ ret = recv(fd, msg->data, sizeof(*hh), 0);
+ if (ret <= 0) {
+ msgb_free(msg);
+ return ret;
+ } else if (ret != sizeof(*hh)) {
+ msgb_free(msg);
+ return -EIO;
+ }
+ msgb_put(msg, ret);
+
+ /* then read the length as specified in header */
+ msg->l2h = msg->data + sizeof(*hh);
+ len = ntohs(hh->len);
+
+ if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ ret = recv(fd, msg->l2h, len, 0);
+ if (ret <= 0) {
+ msgb_free(msg);
+ return ret;
+ } else if (ret < len) {
+ msgb_free(msg);
+ return -EIO;
+ }
+ msgb_put(msg, ret);
+ *rmsg = msg;
+ return ret;
+}
+
+void ipa_client_link_close(struct ipa_link *link);
+
+static void ipa_client_retry(struct ipa_link *link)
+{
+ LOGP(DINP, LOGL_NOTICE, "connection closed\n");
+ ipa_client_link_close(link);
+ LOGP(DINP, LOGL_NOTICE, "retrying in 5 seconds...\n");
+ osmo_timer_schedule(&link->timer, 5, 0);
+ link->state = IPA_LINK_STATE_CONNECTING;
+}
+
+void ipa_client_link_close(struct ipa_link *link)
+{
+ osmo_fd_unregister(&link->ofd);
+ close(link->ofd.fd);
+}
+
+static void ipa_client_read(struct ipa_link *link)
+{
+ struct osmo_fd *ofd = &link->ofd;
+ struct msgb *msg;
+ int ret;
+
+ LOGP(DINP, LOGL_NOTICE, "message received\n");
+
+ ret = ipa_msg_recv(ofd->fd, &msg);
+ if (ret < 0) {
+ if (errno == EPIPE || errno == ECONNRESET) {
+ LOGP(DINP, LOGL_ERROR, "lost connection with server\n");
+ } else {
+ LOGP(DINP, LOGL_ERROR, "unknown error\n");
+ }
+ ipa_client_retry(link);
+ return;
+ } else if (ret == 0) {
+ LOGP(DINP, LOGL_ERROR, "connection closed with server\n");
+ ipa_client_retry(link);
+ return;
+ }
+ if (link->process)
+ link->process(link, msg);
+}
+
+static void ipa_client_write(struct ipa_link *link)
+{
+ struct osmo_fd *ofd = &link->ofd;
+ struct msgb *msg;
+ struct llist_head *lh;
+ int ret;
+
+ LOGP(DINP, LOGL_NOTICE, "sending data\n");
+
+ if (llist_empty(&link->tx_queue)) {
+ ofd->when &= ~BSC_FD_WRITE;
+ return;
+ }
+ lh = link->tx_queue.next;
+ llist_del(lh);
+ msg = llist_entry(lh, struct msgb, list);
+
+ ret = send(link->ofd.fd, msg->data, msg->len, 0);
+ if (ret < 0) {
+ if (errno == EPIPE || errno == ENOTCONN) {
+ ipa_client_retry(link);
+ }
+ LOGP(DINP, LOGL_ERROR, "error to send\n");
+ }
+ msgb_free(msg);
+}
+
+int ipa_client_fd_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ struct ipa_link *link = ofd->data;
+ int error, ret;
+ size_t len = sizeof(error);
+
+ switch(link->state) {
+ case IPA_LINK_STATE_CONNECTING:
+ ret = getsockopt(ofd->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+ if (ret >= 0 && error > 0) {
+ ipa_client_retry(link);
+ return 0;
+ }
+ ofd->when &= ~BSC_FD_WRITE;
+ LOGP(DINP, LOGL_NOTICE, "connection done.\n");
+ link->state = IPA_LINK_STATE_CONNECTED;
+ break;
+ case IPA_LINK_STATE_CONNECTED:
+ LOGP(DINP, LOGL_NOTICE, "connected read/write\n");
+ if (what & BSC_FD_READ)
+ ipa_client_read(link);
+ if (what & BSC_FD_WRITE)
+ ipa_client_write(link);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void ipa_link_timer_cb(void *data);
+
+struct ipa_link *ipa_client_link_create(void *ctx)
+{
+ struct ipa_link *ipa_link;
+
+ ipa_link = talloc_zero(ctx, struct ipa_link);
+ if (!ipa_link)
+ return NULL;
+
+ ipa_link->ofd.when |= BSC_FD_READ | BSC_FD_WRITE;
+ ipa_link->ofd.cb = ipa_client_fd_cb;
+ ipa_link->ofd.data = ipa_link;
+ ipa_link->state = IPA_LINK_STATE_CONNECTING;
+ ipa_link->timer.cb = ipa_link_timer_cb;
+ ipa_link->timer.data = ipa_link;
+
+ return ipa_link;
+}
+
+void ipa_client_link_destroy(struct ipa_link *link)
+{
+ talloc_free(link);
+}
+
+int ipa_client_link_open(struct ipa_link *link)
+{
+ int ret;
+
+ ret = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
+ "127.0.0.1", IPA_TCP_PORT_OML,
+ OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK);
+ if (ret < 0) {
+ if (errno != EINPROGRESS)
+ return ret;
+ }
+ link->ofd.fd = ret;
+ if (osmo_fd_register(&link->ofd) < 0) {
+ close(ret);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void ipa_link_timer_cb(void *data)
+{
+ struct ipa_link *link = data;
+
+ LOGP(DINP, LOGL_NOTICE, "reconnecting.\n");
+
+ switch(link->state) {
+ case IPA_LINK_STATE_CONNECTING:
+ ipa_client_link_open(link);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/input/ipaccess.c b/src/input/ipaccess.c
index 78b2533..4a86300 100644
--- a/src/input/ipaccess.c
+++ b/src/input/ipaccess.c
@@ -43,6 +43,7 @@
#include <osmocom/abis/ipaccess.h>
#include <osmocom/core/socket.h>
#include <osmocom/abis/logging.h>
+#include <osmocom/abis/ipa.h>
#define PRIV_OML 1
#define PRIV_RSL 2
@@ -485,9 +486,12 @@ ipaccess_line_update(struct e1inp_line *line, enum e1inp_line_role role)
switch(role) {
case E1INP_LINE_R_BSC:
+ LOGP(DINP, LOGL_NOTICE, "enabling ipaccess BSC mode\n");
+
/* Listen for OML connections */
ret = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
- "0.0.0.0", IPA_TCP_PORT_OML, 1);
+ "0.0.0.0", IPA_TCP_PORT_OML,
+ OSMO_SOCK_F_BIND);
if (ret < 0)
return ret;
@@ -502,7 +506,8 @@ ipaccess_line_update(struct e1inp_line *line, enum e1inp_line_role role)
}
/* Listen for RSL connections */
ret = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
- "0.0.0.0", IPA_TCP_PORT_RSL, 1);
+ "0.0.0.0", IPA_TCP_PORT_RSL,
+ OSMO_SOCK_F_BIND);
if (ret < 0)
return ret;
@@ -516,9 +521,25 @@ ipaccess_line_update(struct e1inp_line *line, enum e1inp_line_role role)
return ret;
}
break;
- case E1INP_LINE_R_BTS:
- /* XXX: no implemented yet. */
+ case E1INP_LINE_R_BTS: {
+ struct ipa_link *link;
+
+ LOGP(DINP, LOGL_NOTICE, "enabling ipaccess BTS mode\n");
+
+ link = ipa_client_link_create(tall_ipa_ctx);
+ if (link == NULL) {
+ perror("ipa_client_link_create: ");
+ return -ENOMEM;
+ }
+ if (ipa_client_link_open(link) < 0) {
+ perror("ipa_client_link_open: ");
+ ipa_client_link_close(link);
+ ipa_client_link_destroy(link);
+ return -EIO;
+ }
+ ret = 0;
break;
+ }
default:
break;
}