aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2014-03-31 10:53:32 +0200
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2014-03-31 14:33:18 +0200
commit98af3c3a6df8fc6e8ef765b30aff2a013b9904fa (patch)
treeccf17e6985872dd4778f6ead301f02b03c7ebc86 /src
parent36106ae24475f5742dd3335c1bbe4d5e5321912e (diff)
ipa: Change ipa_msg_recv() to support partial receive
Currently ipa_msg_recv() fails, when messages are received partially. This patch provides a new function ipa_msg_recv_buffered() that uses an additional ** to a message buffer to store partial data. When this happens, -EAGAIN is returned. If NULL is used, the function behaves similar to ipa_msg_recv() and fails on partial read. In addition in case of errors the return value is now always -EXXX and the contents of errno is undefined. Note that this feature needs support by the calling code insofar that *tmp_msg must be set to NULL initially and it must be freed and set to NULL manually when the socket is closed. Note also that ipa_msg_recv() is then a wrapper around ipa_msg_recv_buffered() which mimics the old error behaviour by setting errno explicitely to -rc and returning -1 when an error has happened. Ticket: OW#728 Sponsored-by: On-Waves ehf
Diffstat (limited to 'src')
-rw-r--r--src/input/ipa.c151
-rw-r--r--src/input/ipaccess.c13
2 files changed, 128 insertions, 36 deletions
diff --git a/src/input/ipa.c b/src/input/ipa.c
index b5abd36..71e1227 100644
--- a/src/input/ipa.c
+++ b/src/input/ipa.c
@@ -49,50 +49,130 @@ void ipa_msg_push_header(struct msgb *msg, uint8_t proto)
int ipa_msg_recv(int fd, struct msgb **rmsg)
{
- struct msgb *msg;
+ int rc = ipa_msg_recv_buffered(fd, rmsg, NULL);
+ if (rc < 0) {
+ errno = -rc;
+ rc = -1;
+ }
+ return rc;
+}
+
+int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg)
+{
+ struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
struct ipaccess_head *hh;
int len, ret;
+ int needed;
- msg = ipa_msg_alloc(0);
if (msg == NULL)
- return -ENOMEM;
+ msg = ipa_msg_alloc(0);
- /* 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)) {
- LOGP(DLINP, LOGL_ERROR, "too small message received\n");
- msgb_free(msg);
- return -EIO;
+ if (msg == NULL) {
+ ret = -ENOMEM;
+ goto discard_msg;
}
- msgb_put(msg, ret);
+
+ if (msg->l2h == NULL) {
+ /* first read our 3-byte header */
+ needed = sizeof(*hh) - msg->len;
+ ret = recv(fd, msg->tail, needed, 0);
+ if (ret == 0)
+ goto discard_msg;
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ ret = 0;
+ else {
+ ret = -errno;
+ goto discard_msg;
+ }
+ }
+
+ msgb_put(msg, ret);
+
+ if (ret < needed) {
+ if (msg->len == 0) {
+ ret = -EAGAIN;
+ goto discard_msg;
+ }
+
+ LOGP(DLINP, LOGL_INFO,
+ "Received part of IPA message header (%d/%d)\n",
+ msg->len, sizeof(*hh));
+ if (!tmp_msg) {
+ ret = -EIO;
+ goto discard_msg;
+ }
+ *tmp_msg = msg;
+ return -EAGAIN;
+ }
+
+ msg->l2h = msg->tail;
+ }
+
+ hh = (struct ipaccess_head *) msg->data;
/* 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)) {
LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
- "received %d bytes\n", len, ret);
- msgb_free(msg);
- return -EIO;
+ "received %d bytes\n", len, msg->len);
+ ret = -EIO;
+ goto discard_msg;
}
- ret = recv(fd, msg->l2h, len, 0);
- if (ret <= 0) {
- msgb_free(msg);
- return ret;
- } else if (ret < len) {
- LOGP(DLINP, LOGL_ERROR, "truncated message received\n");
- msgb_free(msg);
- return -EIO;
+ needed = len - msgb_l2len(msg);
+
+ if (needed > 0) {
+ ret = recv(fd, msg->tail, needed, 0);
+
+ if (ret == 0)
+ goto discard_msg;
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ ret = 0;
+ else {
+ ret = -errno;
+ goto discard_msg;
+ }
+ }
+
+ msgb_put(msg, ret);
+
+ if (ret < needed) {
+ LOGP(DLINP, LOGL_INFO,
+ "Received part of IPA message L2 data (%d/%d)\n",
+ msgb_l2len(msg), len);
+ if (!tmp_msg) {
+ ret = -EIO;
+ goto discard_msg;
+ }
+ *tmp_msg = msg;
+ return -EAGAIN;
+ }
}
- msgb_put(msg, ret);
+
+ ret = msgb_l2len(msg);
+
+ if (ret == 0) {
+ LOGP(DLINP, LOGL_INFO,
+ "Discarding IPA message without payload\n");
+ ret = -EAGAIN;
+ goto discard_msg;
+ }
+
+ if (tmp_msg)
+ *tmp_msg = NULL;
*rmsg = msg;
return ret;
+
+discard_msg:
+ if (tmp_msg)
+ *tmp_msg = NULL;
+ msgb_free(msg);
+ return ret;
}
void ipa_client_conn_close(struct ipa_client_conn *link)
@@ -103,6 +183,8 @@ void ipa_client_conn_close(struct ipa_client_conn *link)
close(link->ofd->fd);
link->ofd->fd = -1;
}
+ msgb_free(link->pending_msg);
+ link->pending_msg = NULL;
}
static void ipa_client_read(struct ipa_client_conn *link)
@@ -113,11 +195,12 @@ static void ipa_client_read(struct ipa_client_conn *link)
LOGP(DLINP, LOGL_DEBUG, "message received\n");
- ret = ipa_msg_recv(ofd->fd, &msg);
+ ret = ipa_msg_recv_buffered(ofd->fd, &msg, &link->pending_msg);
if (ret < 0) {
- if (errno == EPIPE || errno == ECONNRESET) {
+ if (ret == -EAGAIN)
+ return;
+ if (ret == -EPIPE || ret == -ECONNRESET)
LOGP(DLINP, LOGL_ERROR, "lost connection with server\n");
- }
ipa_client_conn_close(link);
if (link->updown_cb)
link->updown_cb(link, 0);
@@ -382,11 +465,12 @@ static void ipa_server_conn_read(struct ipa_server_conn *conn)
LOGP(DLINP, LOGL_DEBUG, "message received\n");
- ret = ipa_msg_recv(ofd->fd, &msg);
+ ret = ipa_msg_recv_buffered(ofd->fd, &msg, &conn->pending_msg);
if (ret < 0) {
- if (errno == EPIPE || errno == ECONNRESET) {
+ if (ret == -EAGAIN)
+ return;
+ if (ret == -EPIPE || ret == -ECONNRESET)
LOGP(DLINP, LOGL_ERROR, "lost connection with server\n");
- }
ipa_server_conn_destroy(conn);
return;
} else if (ret == 0) {
@@ -471,6 +555,7 @@ ipa_server_conn_create(void *ctx, struct ipa_server_link *link, int fd,
void ipa_server_conn_destroy(struct ipa_server_conn *conn)
{
close(conn->ofd.fd);
+ msgb_free(conn->pending_msg);
osmo_fd_unregister(&conn->ofd);
if (conn->closed_cb)
conn->closed_cb(conn);
diff --git a/src/input/ipaccess.c b/src/input/ipaccess.c
index 225d70c..7ac5ad1 100644
--- a/src/input/ipaccess.c
+++ b/src/input/ipaccess.c
@@ -258,6 +258,8 @@ int ipaccess_rcvmsg_bts_base(struct msgb *msg,
static int ipaccess_drop(struct osmo_fd *bfd, struct e1inp_line *line)
{
int ret = 1;
+ unsigned int ts_nr = bfd->priv_nr;
+ struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
/* Error case: we did not see any ID_RESP yet for this socket. */
if (bfd->fd != -1) {
@@ -269,6 +271,9 @@ static int ipaccess_drop(struct osmo_fd *bfd, struct e1inp_line *line)
ret = -ENOENT;
}
+ msgb_free(e1i_ts->pending_msg);
+ e1i_ts->pending_msg = NULL;
+
/* e1inp_sign_link_destroy releases the socket descriptors for us. */
line->ops->sign_link_down(line);
@@ -415,13 +420,15 @@ static int handle_ts1_read(struct osmo_fd *bfd)
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
struct e1inp_sign_link *link;
struct ipaccess_head *hh;
- struct msgb *msg;
+ struct msgb *msg = NULL;
int ret;
- ret = ipa_msg_recv(bfd->fd, &msg);
+ ret = ipa_msg_recv_buffered(bfd->fd, &msg, &e1i_ts->pending_msg);
if (ret < 0) {
+ if (ret == -EAGAIN)
+ return 0;
LOGP(DLINP, LOGL_NOTICE, "Sign link problems, "
- "closing socket. Reason: %s\n", strerror(errno));
+ "closing socket. Reason: %s\n", strerror(-ret));
goto err;
} else if (ret == 0) {
LOGP(DLINP, LOGL_NOTICE, "Sign link vanished, dead socket\n");