#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CHAN_SIGN_OML 0 #define CHAN_SIGN_RSL 1 /* default IPA cli ports. */ #define IPA_TCP_PORT_OML 3002 #define IPA_TCP_PORT_RSL 3003 static void *abis_ipa_cli_tall; struct chan_abis_ipa_cli { struct ipaccess_unit *unit; struct osmo_stream_cli *oml; struct osmo_stream_cli *rsl; void (*signal_msg)(struct msgb *msg, int type); }; static int oml_read_cb(struct osmo_stream_cli *conn); static int rsl_read_cb(struct osmo_stream_cli *conn); static int chan_abis_ipa_cli_create(struct osmo_chan *chan) { struct chan_abis_ipa_cli *c = (struct chan_abis_ipa_cli *)chan->data; c->unit = osmo_ipa_unit_alloc(); if (c->unit == NULL) goto err; c->oml = osmo_stream_cli_create(abis_ipa_cli_tall); if (c->oml == NULL) goto err_oml; /* default address and port for OML. */ osmo_stream_cli_set_addr(c->oml, "0.0.0.0"); osmo_stream_cli_set_port(c->oml, IPA_TCP_PORT_OML); osmo_stream_cli_set_read_cb(c->oml, oml_read_cb); osmo_stream_cli_set_data(c->oml, chan); c->rsl = osmo_stream_cli_create(abis_ipa_cli_tall); if (c->rsl == NULL) goto err_rsl; /* default address and port for RSL. */ osmo_stream_cli_set_addr(c->rsl, "0.0.0.0"); osmo_stream_cli_set_port(c->rsl, IPA_TCP_PORT_RSL); osmo_stream_cli_set_read_cb(c->rsl, rsl_read_cb); osmo_stream_cli_set_data(c->rsl, chan); return 0; err_rsl: osmo_stream_cli_destroy(c->oml); err_oml: osmo_ipa_unit_free(c->unit); err: return -1; } static void chan_abis_ipa_cli_destroy(struct osmo_chan *chan) { struct chan_abis_ipa_cli *c = (struct chan_abis_ipa_cli *)chan->data; osmo_ipa_unit_free(c->unit); talloc_free(c->rsl); talloc_free(c->oml); } static int chan_abis_ipa_cli_open(struct osmo_chan *chan) { struct chan_abis_ipa_cli *c = (struct chan_abis_ipa_cli *)chan->data; struct osmo_fd *ofd; int ret, on = 1; if (osmo_stream_cli_open(c->oml) < 0) goto err; ofd = osmo_stream_cli_get_ofd(c->oml); ret = setsockopt(ofd->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); if (ret < 0) goto err_oml; if (osmo_stream_cli_open(c->rsl) < 0) goto err_oml; ofd = osmo_stream_cli_get_ofd(c->rsl); ret = setsockopt(ofd->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); if (ret < 0) goto err_rsl; return 0; err_rsl: osmo_stream_cli_close(c->rsl); err_oml: osmo_stream_cli_close(c->oml); err: return -1; } static void chan_abis_ipa_cli_close(struct osmo_chan *chan) { struct chan_abis_ipa_cli *c = (struct chan_abis_ipa_cli *)chan->data; osmo_stream_cli_close(c->oml); osmo_stream_cli_close(c->rsl); } static int chan_abis_ipa_cli_enqueue(struct osmo_chan *c, struct msgb *msg) { osmo_stream_cli_send(msg->dst, msg); return 0; } void osmo_abis_ipa_cli_set_oml_addr(struct osmo_chan *c, const char *addr) { struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data; osmo_stream_cli_set_addr(s->oml, addr); } void osmo_abis_ipa_cli_set_oml_port(struct osmo_chan *c, uint16_t port) { struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data; osmo_stream_cli_set_port(s->oml, port); } void osmo_abis_ipa_cli_set_rsl_addr(struct osmo_chan *c, const char *addr) { struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data; osmo_stream_cli_set_addr(s->rsl, addr); } void osmo_abis_ipa_cli_set_rsl_port(struct osmo_chan *c, uint16_t port) { struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data; osmo_stream_cli_set_port(s->rsl, port); } void osmo_abis_ipa_cli_set_unit(struct osmo_chan *c, struct ipaccess_unit *unit) { struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data; osmo_ipa_unit_free(s->unit); s->unit = unit; } void osmo_abis_ipa_cli_set_cb_signalmsg(struct osmo_chan *c, void (*signal_msg)(struct msgb *msg, int type)) { struct chan_abis_ipa_cli *s = (struct chan_abis_ipa_cli *)&c->data; s->signal_msg = signal_msg; } static struct msgb * ipa_cli_id_resp(struct ipaccess_unit *dev, uint8_t *data, int len) { struct msgb *nmsg; char str[64]; uint8_t *tag; nmsg = osmo_ipa_msg_alloc(0); if (nmsg == NULL) return NULL; *msgb_put(nmsg, 1) = IPAC_MSGT_ID_RESP; while (len) { if (len < 2) { LOGP(DLINP, LOGL_NOTICE, "Short read of ipaccess tag\n"); msgb_free(nmsg); return NULL; } switch (data[1]) { case IPAC_IDTAG_UNIT: sprintf(str, "%u/%u/%u", dev->site_id, dev->bts_id, dev->trx_id); break; case IPAC_IDTAG_MACADDR: sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", dev->mac_addr[0], dev->mac_addr[1], dev->mac_addr[2], dev->mac_addr[3], dev->mac_addr[4], dev->mac_addr[5]); break; case IPAC_IDTAG_LOCATION1: strcpy(str, dev->location1); break; case IPAC_IDTAG_LOCATION2: strcpy(str, dev->location2); break; case IPAC_IDTAG_EQUIPVERS: strcpy(str, dev->equipvers); break; case IPAC_IDTAG_SWVERSION: strcpy(str, dev->swversion); break; case IPAC_IDTAG_UNITNAME: sprintf(str, "%s-%02x-%02x-%02x-%02x-%02x-%02x", dev->unit_name, dev->mac_addr[0], dev->mac_addr[1], dev->mac_addr[2], dev->mac_addr[3], dev->mac_addr[4], dev->mac_addr[5]); break; case IPAC_IDTAG_SERNR: strcpy(str, dev->serno); break; default: LOGP(DLINP, LOGL_NOTICE, "Unknown ipaccess tag 0x%02x\n", *data); msgb_free(nmsg); return NULL; } LOGP(DLINP, LOGL_INFO, " tag %d: %s\n", data[1], str); tag = msgb_put(nmsg, 3 + strlen(str) + 1); tag[0] = 0x00; tag[1] = 1 + strlen(str) + 1; tag[2] = data[1]; memcpy(tag + 3, str, strlen(str) + 1); data += 2; len -= 2; } osmo_ipa_msg_push_header(nmsg, IPAC_PROTO_IPACCESS); return nmsg; } static struct msgb *ipa_cli_id_ack(void) { struct msgb *nmsg2; nmsg2 = osmo_ipa_msg_alloc(0); if (nmsg2 == NULL) return NULL; *msgb_put(nmsg2, 1) = IPAC_MSGT_ID_ACK; osmo_ipa_msg_push_header(nmsg2, IPAC_PROTO_IPACCESS); return nmsg2; } static int abis_ipa_cli_rcvmsg(struct osmo_chan *c, struct osmo_stream_cli *conn, struct msgb *msg, int type) { uint8_t msg_type = *(msg->l2h); struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn); struct chan_abis_ipa_cli *chan = (struct chan_abis_ipa_cli *)&c->data; int ret; /* Handle IPA PING, PONG and ID_ACK messages. */ if (osmo_ipa_rcvmsg_base(msg, ofd, 0)) /* XXX: 0 indicates client */ return 0; if (msg_type == IPAC_MSGT_ID_GET) { struct msgb *rmsg; uint8_t *data = msgb_l2(msg); int len = msgb_l2len(msg); LOGP(DLINP, LOGL_NOTICE, "received ID get\n"); rmsg = ipa_cli_id_resp(chan->unit, data + 1, len - 1); osmo_stream_cli_send(conn, rmsg); /* send ID_ACK. */ rmsg = ipa_cli_id_ack(); osmo_stream_cli_send(conn, rmsg); ret = 0; } else { LOGP(DLINP, LOGL_ERROR, "Unknown IPA message type\n"); ret = -EINVAL; } return ret; } static int read_cb(struct osmo_stream_cli *conn, int type) { int ret; struct msgb *msg; struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn); struct osmo_chan *chan = osmo_stream_cli_get_data(conn); struct chan_abis_ipa_cli *s; struct ipa_head *hh; LOGP(DLINP, LOGL_DEBUG, "received message from stream\n"); msg = osmo_ipa_msg_alloc(0); if (msg == NULL) { LOGP(DLINP, LOGL_ERROR, "cannot allocate message\n"); return 0; } /* XXX replace with generic stream reader */ ret = osmo_ipa_msg_recv(ofd->fd, msg); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "cannot receive message\n"); msgb_free(msg); /* not the dummy connection, release it. */ return 0; } else if (ret == 0) { /* link has vanished, dead socket. */ LOGP(DLINP, LOGL_ERROR, "closed connection\n"); msgb_free(msg); return 0; } hh = (struct ipa_head *) msg->data; if (hh->proto == IPAC_PROTO_IPACCESS) { abis_ipa_cli_rcvmsg(chan, conn, msg, type); msgb_free(msg); return -EIO; } chan = osmo_stream_cli_get_data(conn); if (chan == NULL) { LOGP(DLINP, LOGL_ERROR, "no matching signalling link\n"); msgb_free(msg); return -EIO; } if (hh->proto != IPAC_PROTO_OML && hh->proto != IPAC_PROTO_RSL) { LOGP(DLINP, LOGL_ERROR, "wrong protocol\n"); return -EIO; } msg->dst = chan; s = (struct chan_abis_ipa_cli *)chan->data; s->signal_msg(msg, type); return 0; } static int oml_read_cb(struct osmo_stream_cli *conn) { return read_cb(conn, CHAN_SIGN_OML); } static int rsl_read_cb(struct osmo_stream_cli *conn) { return read_cb(conn, CHAN_SIGN_RSL); } struct osmo_chan_type chan_abis_ipa_cli = { .type = OSMO_CHAN_ABIS_IPA_CLI, .subtype = OSMO_SUBCHAN_STREAM, .name = "A-bis IPA client", .datasiz = sizeof(struct chan_abis_ipa_cli), .create = chan_abis_ipa_cli_create, .destroy = chan_abis_ipa_cli_destroy, .open = chan_abis_ipa_cli_open, .close = chan_abis_ipa_cli_close, .enqueue = chan_abis_ipa_cli_enqueue, };